---
title: "Layers - Import Linter"
section: "raw"
type: "source"
created: "2026-08-27"
updated: "2026-08-27"
canonical: "https://pyweb.dev/wiki/raw/articles/import-linter-layer-contracts-2026"
---
# Layers - Import Linter

[Skip to content](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#layers)

# Layers

## Layers

_Type name:_`layers`

Layers contracts enforce a 'layered architecture', where higher layers may depend on lower layers, but not the other
way around.

**Configuration options**

- `layers`:
An ordered list with the name of each layer module. If `containers` are specified too, then these names must be
_relative to the container_. The order is from higher to lower level layers. Layers wrapped in parentheses
(e.g. `(foo)`) will be ignored if they are not present in the file system; otherwise, the contract will fail.
It's also possible to include multiple layer modules on the same line, separated by either exclusively pipes
(`|`) or exclusively colons (`:`) \- see [Multi-item layers](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#multi-item-layers). Does not support [wildcards](https://import-linter.readthedocs.io/en/v2.9/contract_types/#wildcards).
- `containers`:
List of the parent modules of the layers, as _absolute names_ that you could import, such as
`mypackage.foo`. See [Containers](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#containers). Supports [wildcards](https://import-linter.readthedocs.io/en/v2.9/contract_types/#wildcards). (Optional.)
- `ignore_imports`: See [shared options](https://import-linter.readthedocs.io/en/v2.9/contract_types/#options-used-by-multiple-contracts).
- `unmatched_ignore_imports_alerting`: See [shared options](https://import-linter.readthedocs.io/en/v2.9/contract_types/#options-used-by-multiple-contracts).
- `exhaustive`. If true, check that the contract declares every possible layer in its list of layers to check.
See [Exhaustive contracts](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#exhaustive-contracts). (Optional, default False.)
- `exhaustive_ignores`. A list of layers to ignore in exhaustiveness checks. (Optional.)

### Basic usage

'Layers' is a software architecture pattern in which a list of modules/packages have a dependency direction
from high to low.

![Layered architecture](https://import-linter.readthedocs.io/en/v2.9/img/layers.png)

In this diagram, the Python package `mypackage` has a layered architecture in which its subpackage `high` is the
highest layer and its subpackage `low` is the lowest layer. `low` is not allowed to import from any of the layers
above it, while `high` can import from everything. In the middle, `medium` can import from `low` but not `high`.
This includes indirect imports (i.e. chains of imports via other modules), so if there was a module not listed here that
imports `high` (say, `utils`) then `low` wouldn't be allowed to import that either.

The architecture is enforced for all modules within the layers, too, so `mypackage.low.one` would not be
allowed to import from `mypackage.high.two`. That said, the layers don't have to be subpackages - they could just be
individual `.py` modules.

Here's how the architecture shown above could be checked using a `layers` contract:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_1_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_1_2)

```
[importlinter:contract:my-layers-contract]
name = My layers contract
type = layers
layers =
    mypackage.high
    mypackage.medium
    mypackage.low
```

```
[[tool.importlinter.contracts]]
name = "My layers contract"
type = "layers"
layers = [\
    "mypackage.high",\
    "mypackage.medium",\
    "mypackage.low",\
]
```

If a layer is listed in the contract, the contract will be broken if the layer doesn't exist. You can make a layer
optional by wrapping it in parentheses, but this is only likely to be useful if you are using
[containers](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#containers).

### Layering across root packages

Layers don't have to be subpackages - they can be top-level (root) packages. We can still layer a Python project
consisting of three packages `high`, `medium` and `low`, in a directory that does not contain an
`__init__.py` file:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_2_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_2_2)

```
[importlinter]
root_packages=
    high
    medium
    low

[importlinter:contract:my-layers-contract]
name = My three-tier layers contract (multiple root packages)
type = layers
layers =
    high
    medium
    low
```

```
[tool.importlinter]
root_packages = [\
    "high",\
    "medium",\
    "low",\
]

[[tool.importlinter.contracts]]
name = "My three-tier layers contract (multiple root packages)"
type = "layers"
layers = [\
    "high",\
    "medium",\
    "low",\
]
```

In this contract, each top level package is treated as a layer. (Note, though, that they all need to be specified
as `root_packages` in the `[importlinter]` configuration, too.)

### Containers

Containers allow for a less repetitive way of specifying layers.

Here's a contract that layers `mypackage.high`, `mypackage.medium` and `mypackage.low` using a single container:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_3_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_3_2)

```
[importlinter:contract:my-layers-contract]
name = My layers contract
type = layers
layers =
    high
    medium
    low
containers =
    mypackage
```

```
[[tool.importlinter.contracts]]
name = "My layers contract"
type = "layers"
layers = [\
    "high",\
    "medium",\
    "low",\
]
containers = [\
    "mypackage",\
]
```

Note that by using a container, we don't need to repeat the containing package in the `layers` section.

Containers are particularly useful if you want to specify a recurring pattern of layers in different places in the graph:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_4_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_4_2)

```
[importlinter:contract:my-layers-contract]
name = My multiple package layers contract
type = layers
layers =
    high
    (medium)
    low
containers =
    mypackage.foo
    mypackage.bar
    mypackage.baz
```

```
[[tool.importlinter.contracts]]
name = "My multiple package layers contract"
type = "layers"
layers = [\
    "high",\
    "(medium)",\
    "low",\
]
containers = [\
    "mypackage.foo",\
    "mypackage.bar",\
    "mypackage.baz",\
]
```

In this example, each container has its own layered architecture. For example, it will not allow `mypackage.foo.low`
to import `mypackage.foo.high`. However, it will allow `mypackage.foo.low` to import `mypackage.bar.high`,
as they are in different containers:

Notice that `medium` is wrapped in parentheses, making it an optional layer. This means that if it is missing from any of
the containers, Import Linter won't complain.

### Exhaustive contracts

If you want to make sure that _every_ module in each container is defined as a layer, you can mark the contract as
'exhaustive'. This means that if a module is added to the code base in the same package as your layers, the contract
will fail. Any such modules that shouldn't cause a failure can be added to an `exhaustive_ignores` list.

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_5_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_5_2)

```
[importlinter:contract:my-layers-contract]
name = My multiple package layers contract
type = layers
layers =
  high
  (medium)
  low
containers=
  mypackage.foo
  mypackage.bar
  mypackage.baz
exhaustive = true
exhaustive_ignores =
  utils
```

```
[[tool.importlinter.contracts]]
name = "My multiple package layers contract"
type = "layers"
layers = [\
    "high",\
    "(medium)",\
    "low",\
]
containers = [\
    "mypackage.foo",\
    "mypackage.bar",\
    "mypackage.baz",\
]
exhaustive = true
exhaustive_ignores = [\
    "utils",\
]
```

If, say, a module existed called `mypackage.foo.extra`, the contract will fail as it is not listed as a layer. However
`mypackage.foo.utils` would be allowed as it is listed in `exhaustive_ignores`.

Exhaustive contracts are only supported for layers that define containers.

### Multi-item layers

Import Linter supports the presence of multiple sibling modules or packages within the same layer. In the diagram below,
the modules `blue`, `green` and `yellow` are 'independent' in the same layer. This means that, in addition to not
being allowed to import from layers above them, they are not allowed to import from each other.

![Architecture with a layer containing independent siblings](https://import-linter.readthedocs.io/en/v2.9/img/layers-independent.png)

An architecture like this can be checked by listing the siblings on the same line, separated by pipe characters:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_6_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_6_2)

```
[importlinter:contract:my-layers-contract]
name = Contract with sibling modules (independent)
type = layers
layers =
  mypackage.high
  mypackage.blue | mypackage.green | mypackage.yellow
  mypackage.low
```

```
[[tool.importlinter.contracts]]
name = "Contract with sibling modules (independent)"
type = "layers"
layers = [\
    "mypackage.high",\
    "mypackage.blue | mypackage.green | mypackage.yellow",\
    "mypackage.low",\
]
```

For a more relaxed architecture siblings can be designated as non-independent, meaning that they are allowed to import
from each other, as shown:

![Architecture with a layer containing non-independent siblings](https://import-linter.readthedocs.io/en/v2.9/img/layers-non-independent.png)

To allow siblings to depend on each other, use colons instead of pipes to separate them:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_7_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_7_2)

```
[importlinter:contract:my-layers-contract]
name = Contract with sibling modules (independent)
type = layers
layers =
  mypackage.high
  mypackage.blue : mypackage.green : mypackage.yellow
  mypackage.low
```

```
[[tool.importlinter.contracts]]
name = "Contract with sibling modules (independent)"
type = "layers"
layers = [\
    "mypackage.high",\
    "mypackage.blue : mypackage.green : mypackage.yellow",\
    "mypackage.low",\
]
```

Note: you are not allowed to mix different kinds of separators on the same line. This would be an invalid contract:

[INI](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_8_1)[TOML](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/#__tabbed_8_2)

```
[importlinter:contract:my-invalid-contract]
name = Invalid contract
type = layers
layers =
  mypackage.high
  # The line below is invalid, as it mixes separators.
  mypackage.blue | mypackage.green : mypackage.yellow
  mypackage.low
```

```
[[tool.importlinter.contracts]]
name = "Invalid contract"
type = "layers"
layers = [\
    "mypackage.high",\
    # The line below is invalid, as it mixes separators.\
    "mypackage.blue | mypackage.green : mypackage.yellow",\
    "mypackage.low",\
]
```

Versions[latest](https://import-linter.readthedocs.io/en/latest/contract_types/layers/)[stable](https://import-linter.readthedocs.io/en/stable/contract_types/layers/)[v2.13](https://import-linter.readthedocs.io/en/v2.13/contract_types/layers/)[v2.12](https://import-linter.readthedocs.io/en/v2.12/contract_types/layers/)[v2.11](https://import-linter.readthedocs.io/en/v2.11/contract_types/layers/)[v2.10](https://import-linter.readthedocs.io/en/v2.10/contract_types/layers/)**[v2.9](https://import-linter.readthedocs.io/en/v2.9/contract_types/layers/)**[v2.8](https://import-linter.readthedocs.io/en/v2.8/contract_types/layers/)[v2.7](https://import-linter.readthedocs.io/en/v2.7/contract_types/layers/)[v2.6](https://import-linter.readthedocs.io/en/v2.6/contract_types/layers/)[v2.5.2](https://import-linter.readthedocs.io/en/v2.5.2/contract_types/layers/)[v2.5.1](https://import-linter.readthedocs.io/en/v2.5.1/contract_types/layers/)[v2.5](https://import-linter.readthedocs.io/en/v2.5/contract_types/layers/)[v2.4](https://import-linter.readthedocs.io/en/v2.4/contract_types/layers/)[v2.3](https://import-linter.readthedocs.io/en/v2.3/contract_types/layers/)[v2.2](https://import-linter.readthedocs.io/en/v2.2/contract_types/layers/)[v2.1](https://import-linter.readthedocs.io/en/v2.1/contract_types/layers/)[v2.0](https://import-linter.readthedocs.io/en/v2.0/contract_types/layers/)[v1.12.1](https://import-linter.readthedocs.io/en/v1.12.1/contract_types/layers/)[v1.12.0](https://import-linter.readthedocs.io/en/v1.12.0/contract_types/layers/)[v1.11.1](https://import-linter.readthedocs.io/en/v1.11.1/contract_types/layers/)[v1.11.0](https://import-linter.readthedocs.io/en/v1.11.0/contract_types/layers/)[v1.10.0](https://import-linter.readthedocs.io/en/v1.10.0/contract_types/layers/)[v1.9.0](https://import-linter.readthedocs.io/en/v1.9.0/contract_types/layers/)[v1.8.0](https://import-linter.readthedocs.io/en/v1.8.0/contract_types/layers/)[v1.7.0](https://import-linter.readthedocs.io/en/v1.7.0/contract_types/layers/)[v1.6.0](https://import-linter.readthedocs.io/en/v1.6.0/contract_types/layers/)[v1.5.0](https://import-linter.readthedocs.io/en/v1.5.0/contract_types/layers/)[v1.4.0](https://import-linter.readthedocs.io/en/v1.4.0/contract_types/layers/)[v1.3.0](https://import-linter.readthedocs.io/en/v1.3.0/contract_types/layers/)[v1.2.6](https://import-linter.readthedocs.io/en/v1.2.6/contract_types/layers/)[v1.2.5](https://import-linter.readthedocs.io/en/v1.2.5/contract_types/layers/)[v1.2.4](https://import-linter.readthedocs.io/en/v1.2.4/contract_types/layers/)[v1.2.3](https://import-linter.readthedocs.io/en/v1.2.3/contract_types/layers/)[v1.2.2](https://import-linter.readthedocs.io/en/v1.2.2/contract_types/layers/)[v1.2.1](https://import-linter.readthedocs.io/en/v1.2.1/contract_types/layers/)[v1.2](https://import-linter.readthedocs.io/en/v1.2/contract_types/layers/)[v1.1](https://import-linter.readthedocs.io/en/v1.1/contract_types/layers/)[v1.0](https://import-linter.readthedocs.io/en/v1.0/contract_types/layers/)[v1.0b5](https://import-linter.readthedocs.io/en/v1.0b5/contract_types/layers/)[v1.0b4](https://import-linter.readthedocs.io/en/v1.0b4/contract_types/layers/)[v1.0b3](https://import-linter.readthedocs.io/en/v1.0b3/contract_types/layers/)[v1.0b2](https://import-linter.readthedocs.io/en/v1.0b2/contract_types/layers/)[v1.0b1](https://import-linter.readthedocs.io/en/v1.0b1/contract_types/layers/)On Read the Docs[Project Home](https://app.readthedocs.org/projects/import-linter/?utm_source=import-linter&utm_content=flyout)[Builds](https://app.readthedocs.org/projects/import-linter/builds/?utm_source=import-linter&utm_content=flyout)Search

* * *

[Addons documentation](https://docs.readthedocs.io/page/addons.html?utm_source=import-linter&utm_content=flyout) ― Hosted by
[Read the Docs](https://about.readthedocs.com/?utm_source=import-linter&utm_content=flyout)

This _may_ be an
old version of this documentation

You may be reading an old version of this documentation. Read the
[latest stable version of this documentation](https://import-linter.readthedocs.io/en/stable/contract_types/layers/).

---

## Agent Navigation

### Machine endpoints
- Knowledge graph: https://pyweb.dev/api/graph.json
- Graph analysis: https://pyweb.dev/api/graph-analysis.json
- Context index: https://pyweb.dev/llms.txt
