Layers Linter
A static code analyzer that enforces architectural boundaries for layers and outher libraries
Overview
Layers Linter helps you maintain a clean architecture by:
- Enforcing layer dependencies: Ensures that dependencies between modules follow the allowed directions
- Restricting library usage: Verifies that libraries are only used in permitted layers
- Integrating with your workflow: Works as a standalone CLI tool or as a Flake8 plugin
The linter analyzes your project's import structure by parsing the AST (Abstract Syntax Tree) of Python files, building a dependency graph, and validating it against your configuration.
Installation
# Install from PyPI
pip install layers-linter
# Or install with Flake8 plugin support
pip install layers-linter[flake8]
Usage
Command Line Interface
Default path to layers.toml config in the current working directory.
# Basic usage
layers-linter /path/to/your/project
# With custom configuration file
layers-linter /path/to/your/project --config /path/to/your/layers.toml
# Disable checking for modules without a layer
layers-linter /path/to/your/project --no-check-no-layer
Flake8 Plugin
Add to your .flake8 configuration:
[flake8]
select = LA
la-config = layers.toml
Then run Flake8 as usual:
Default path to layers.toml config in the current working directory.
flake8 /path/to/your/project [--la-config /path/to/your/config]
Configuration
Layers Linter uses a TOML configuration file to define your project's architecture. By default, it looks for a file named layers.toml in the current directory.
Configuration Format
# Optional: Modules to exclude from analysis
exclude_modules = ["*.__init__"]
# Layer definitions
[layers]
[layers.dicontainer]
description = ""
# Modules that belong to this layer
contains_modules = [
"project.container"
]
# Layers that this layer can use (if empty list, it can't use any other layers)
depends_on = ["usecases"]
[layers.usecases]
description = ""
contains_modules = [
"project.domains.*.use_cases.*",
"project.domains.*.use_cases",
]
depends_on = []
[layers.presentation]
description = ""
contains_modules = [
"project.presentation.*",
"project.domains.*.endpoints"
]
depends_on = ["dicontainer"]
# Library restrictions
[libs]
[libs.fastapi]
# Layers that can use this library (if not specified, all layers can use it)
allowed_in = ["presentation"]
Pattern Matching
The contains_modules field supports pattern matching with wildcards:
project.module- Exact matchproject.module.*- All submodules (the module itself is not matched, list it separately)project.*.module- Any module with the specified pattern (*also matches dots)*.module- Any module ending with the specified pattern
Module names start with the name of the checked directory, e.g. for
layers-linter /path/to/project the file /path/to/project/domains/user.py is project.domains.user.
When the path is ., module names are relative to the current directory.
Hidden directories (.venv, .git, ...) are skipped.
Dependency Rules
- depends_on: Controls which layers this layer can use
- Empty list (
[]): This layer cannot use any other layers - Not specified or
"none": No restrictions - Modules of the same layer can always import each other
- Empty list (
Error Codes
-
LA001: Invalid dependency between layers
- Example:
Invalid dependency from layer 'presentation' to layer 'repositories'
- Example:
-
LA002: Module without a layer
- Example:
Module 'project.utils.helpers' does not belong to any layer
- Example:
-
LA020: Restricted library usage
- Example:
Layers [presentation] cannot use restricted library 'sqlalchemy'
- Example:
Notes
- Imports inside
if typing.TYPE_CHECKING:blocks are ignored (alsoif t.TYPE_CHECKING:, aliases likefrom typing import TYPE_CHECKING as TC, and theelsebranch ofif not TYPE_CHECKING:) import packageis treated as a dependency onpackage.__init__from package import submodule(including relativefrom . import submodule) is treated as a dependency on the submodule- The CLI exits with code
1if problems were found and2on configuration errors - The linter builds a complete dependency graph before validation, allowing for comprehensive analysis
Metadata
Release files for layers-linter 3.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| layers_linter-3.2.3.tar.gz | 15.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| layers_linter-3.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.5 kB
Release files / layers_linter-3.2.3.tar.gz
| Download URL | layers_linter-3.2.3.tar.gz |
|---|---|
| Size | 15.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
37dace7d5bc4cf756bd78b06ff5a3537ee1d675de141c4e369e8655b5e25baff
|
|
BLAKE2b-256 checksum How to use checksums |
52b219d635c3d01091ead6476fd5e7b39a6ba22d30fbcd4806b69bd82a95f1de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / layers_linter-3.2.3-py3-none-any.whl
| Download URL | layers_linter-3.2.3-py3-none-any.whl |
|---|---|
| Size | 11.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8377393c741cad9fc48adc3be54a14e72d29152bf05ac202c950e0f8fde5f97e
|
|
BLAKE2b-256 checksum How to use checksums |
41674d579a8c723c3842b1fbb8c356ef3a663f12168b71b66f99d745018215cf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for layers-linter 3.2.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.