Skip to main content

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:

  1. Enforcing layer dependencies: Ensures that dependencies between modules follow the allowed directions
  2. Restricting library usage: Verifies that libraries are only used in permitted layers
  3. 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 match
  • project.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

Error Codes

  • LA001: Invalid dependency between layers

    • Example: Invalid dependency from layer 'presentation' to layer 'repositories'
  • LA002: Module without a layer

    • Example: Module 'project.utils.helpers' does not belong to any layer
  • LA020: Restricted library usage

    • Example: Layers [presentation] cannot use restricted library 'sqlalchemy'

Notes

  • Imports inside if typing.TYPE_CHECKING: blocks are ignored (also if t.TYPE_CHECKING:, aliases like from typing import TYPE_CHECKING as TC, and the else branch of if not TYPE_CHECKING:)
  • import package is treated as a dependency on package.__init__
  • from package import submodule (including relative from . import submodule) is treated as a dependency on the submodule
  • The CLI exits with code 1 if problems were found and 2 on 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)

Source distribution for layers-linter 3.2.3
File Size Uploaded Provenance
layers_linter-3.2.3.tar.gz 15.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for layers-linter 3.2.3
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page