Skip to main content

klint

Architecture-as-Code checks for Python projects.

klint enforces architecture rules from a small klint.yaml file. Use it to keep module boundaries explicit, block risky patterns in specific layers, pin important symbols to their intended owner file, and hold files to size and comment budgets.

It installs as a Python package and runs as a native executable:

pip install klint
klint

For machine-readable output:

klint --json
python -m klint --json

By default, klint looks for klint.yaml or klint.config.json in the current working directory. Use --config when the config lives somewhere else:

klint --config path/to/project

CLI

The Python package exposes both a console command and a module entrypoint:

klint
python -m klint

Supported options:

Option Description
--config <dir> Directory containing klint.yaml or klint.config.json. Defaults to the current working directory.
--json Emit structured JSON to stdout. Useful in CI and agentic lifecycle hooks.
--version, -V, version Print the klint version.
--help, -h, help, h Print CLI usage.

Arguments are passed straight through to the bundled native binary.

Configuration

Create klint.yaml at the root of your project:

include: ["src"]
rules: {}
arch:
  layers:
    api: ["src/app/api/**"]
    db: ["src/app/db/**"]
    jobs: ["src/app/jobs/**"]

include selects which paths are scanned. Prefix an entry with ! to exclude, either a whole directory or individual files:

include: ["src", "!**/.venv/**", "!**/*_test.py", "!src/jobs/legacy.py"]

Set $schema to a version-tagged URL to pin the schema your editor validates against:

$schema: https://raw.githubusercontent.com/konvert7/klint/refs/tags/v0.29.0/klint.schema.json

When the tagged version differs from the installed klint, the run reports a klint/schema-version warning naming both versions and the URL to move to. It is a warning, so the exit code stays 0. Local paths carry no version and are never checked.

arch.layers gives names to file groups so rules can talk about architecture instead of repeating globs. root optionally sets the directory that include paths and reported file names resolve against.

rules holds klint's top-level source rules, which are TypeScript/JavaScript only. For a Python project it stays empty — everything below lives under arch.

Every arch rule accepts severity. Use error (exit code 2) or warn (reported, exit code 0). Arch rules cannot be off; remove the entry instead.

Import Boundaries

Use arch.imports to block dependencies between layers.

include: ["src"]
rules: {}
arch:
  layers:
    api: ["src/app/api/**"]
    db: ["src/app/db/**"]
  imports:
    - from: api
      deny: db
      message: "API code must not import database internals directly"

This flags Python imports such as:

from app.db.session import get_session

from files under src/app/api/**.

Use allow instead of deny to invert the check — anything not matching allow is denied.

What resolves. Relative imports (from ..lib.auth import load_key) and absolute project imports (from app.lib.auth import load_key) both resolve. Absolute imports are matched against the project root and its direct child directories containing Python files, checking <module>.py, <module>/__init__.py, and — for PEP 420 namespace packages — a <module>/ directory the scan found .py files under.

Every target of a multi-target statement is checked separately, so import json, app.lib.auth and from . import helper, sibling each produce one record per target. Dynamic imports are read off the AST too: importlib.import_module("…") and __import__("…"), including aliased bindings such as import importlib as il and from importlib import import_module as load. A call to a same-named method that is not bound to importlib is not treated as an import.

Imports that do not resolve to a project file — third-party packages such as import requests — are ignored by deny/allow. Use deny-packages to reach those.

Third-party and stdlib packages

deny-packages matches pip packages and standard-library modules, which deny/allow cannot see because they resolve to no project file. Matching is per dotted segment, so denying os also catches os.path.

arch:
  imports:
    - from: jobs
      deny-packages: ["os", "requests"]
      message: "Jobs must go through the platform adapter"

Type-only imports

Set type-only: allow to exempt imports inside an if TYPE_CHECKING: block, mirroring how import type is exempted in TypeScript. The else and elif branches of that guard remain runtime imports.

arch:
  imports:
    - from: api
      deny: db
      type-only: allow

Forbidden Patterns

Use arch.forbidden to block text patterns inside a layer.

include: ["src"]
rules: {}
arch:
  layers:
    jobs: ["src/app/jobs/**"]
  forbidden:
    - in: jobs
      pattern: "print("
      message: "Jobs must not print directly"

pattern is a literal substring scanned per line. Prefix it with re: to match a regular expression instead:

    - in: jobs
      pattern: "re:^\\s*os\\.environ\\["
      message: "Read configuration through settings, not os.environ"

Regexes must stay inside the common regex subset — no lookaround or backreferences. A literal pattern that itself begins with re: cannot be expressed; such a value is always read as a regex.

This is useful for project-specific policies such as blocking direct logging, environment access, framework shortcuts, or unsafe helpers in the wrong layer.

Singleton Ownership

Use arch.singleton when a symbol or pattern must only appear in one file.

include: ["src"]
rules: {}
arch:
  singleton:
    - only: "src/app/config/settings.py"
      pattern: "API_KEY"
      message: "API_KEY must only live in settings.py"

This allows API_KEY in src/app/config/settings.py and flags the same pattern anywhere else in scanned files. pattern takes the same literal-or-re: form as arch.forbidden.

File Size

Use arch.maxLines to cap how long a file may get. The limit counts physical lines, and the violation is reported at the first line past the limit.

arch:
  layers:
    jobs: ["src/app/jobs/**"]
  maxLines:
    - limit: 500
      in: jobs
      message: "Split this job into smaller modules"

Comment Budgets

Use arch.maxCommentDensity to cap what share of a file may be comments, and arch.maxCommentBlock to cap how tall a single run of comment lines may get.

arch:
  layers:
    jobs: ["src/app/jobs/**"]
  maxCommentDensity:
    - limit: 10
      in: jobs
  maxCommentBlock:
    - limit: 3
      in: jobs

Density is measured against total physical lines — code, comments, and blanks — the same denominator maxLines uses. A comment block violation is reported at the first line past the limit.

What counts as a comment in Python. # comments count toward both limits. Docstrings are string expressions rather than comments, so they never count toward either limit — a module that is nothing but docstrings measures 0% density. The countDocComments option therefore has no effect on Python files; it exists for languages whose doc-comments are real comment nodes.

Ignoring structural comments

Some comment lines are machinery rather than prose — tool directives a linter or codegen step reads. Use ignore to keep them out of both budgets:

arch:
  maxCommentDensity:
    - limit: 10
      in: jobs
      ignore: ["re:^\\s*# (noqa|type:|pragma:)"]

ignore takes the same literal-or-re: form as arch.forbidden and tests the physical source line. Ignored lines still count in the density denominator, and for maxCommentBlock they connect a run without adding to its height — so a directive sitting inside a comment block does not split it in two.

Supported Python Rules

The Python package supports:

  • arch/imports
  • arch/forbidden
  • arch/singleton
  • arch/max-lines
  • arch/max-comment-density
  • arch/max-comment-block

These rules are intentionally configuration-driven. They are for enforcing your project's architecture, not for replacing formatters or style linters.

klint's top-level source rules and its sonar plugin are TypeScript/JavaScript-only and do not apply to .py files.

CI

Run klint in CI after installing your Python dependencies:

pip install klint
klint --json

klint exits with:

  • 0 when no errors are found
  • 2 when rule violations are found
  • 1 for configuration or runtime errors

Metadata

Release files for klint 1.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for klint 1.5.1
File Actions
klint-1.5.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for klint-1.5.1-py3-none-win_amd64.whl
klint-1.5.1-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details for klint-1.5.1-py3-none-musllinux_1_2_x86_64.whl
klint-1.5.1-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details for klint-1.5.1-py3-none-manylinux_2_28_x86_64.whl
klint-1.5.1-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details for klint-1.5.1-py3-none-manylinux_2_28_aarch64.whl
klint-1.5.1-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for klint-1.5.1-py3-none-macosx_11_0_arm64.whl
klint-1.5.1-py3-none-macosx_10_13_x86_64.whl Python 3 none macOS 10.13+ x86-64 Details for klint-1.5.1-py3-none-macosx_10_13_x86_64.whl

Total release size: 17.8 MB

Release files / klint-1.5.1-py3-none-win_amd64.whl

Download URL klint-1.5.1-py3-none-win_amd64.whl
Size 2.8 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
ef9bc25e5c7f406729b2f0d7323b7f5b1321f29dffe5c892a3052e5d5856fdca
BLAKE2b-256 checksum
How to use checksums
76e96707df27b8e615a57ae22e5a83081b210a065d0ef3e9ab0c96a5ef139cf1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / klint-1.5.1-py3-none-musllinux_1_2_x86_64.whl

Download URL klint-1.5.1-py3-none-musllinux_1_2_x86_64.whl
Size 3.2 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
bef255a568e3c7a3157a1217a1f04331edbf9b0a6db929db07fa2535a7bd2749
BLAKE2b-256 checksum
How to use checksums
61fad232213d6b279c8ed739b99baad1cfd63460b68671fb8627c6d77eed3185
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / klint-1.5.1-py3-none-manylinux_2_28_x86_64.whl

Download URL klint-1.5.1-py3-none-manylinux_2_28_x86_64.whl
Size 3.1 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
c19dc923e3522f99cd9af3d1cb79d45ed83f51f3db18a491b06fb0502fbf9fd9
BLAKE2b-256 checksum
How to use checksums
3423de6c3e7cbd533ebfd05f66916130ed0fb2fbaff94bcc80da920b1865a8fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / klint-1.5.1-py3-none-manylinux_2_28_aarch64.whl

Download URL klint-1.5.1-py3-none-manylinux_2_28_aarch64.whl
Size 3.0 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
e0847cce47edbf2f3f3565aba4c29b254e5834a66bb87e45ba1d22c8dfda010a
BLAKE2b-256 checksum
How to use checksums
9116aa6f52eacda8ea1a942dc5de62bb66191e0793e365f05743111ca5997770
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / klint-1.5.1-py3-none-macosx_11_0_arm64.whl

Download URL klint-1.5.1-py3-none-macosx_11_0_arm64.whl
Size 2.9 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
14380fba2d45eebd7c1963417287244ee56420be5c05173d61a91a900bfb80b0
BLAKE2b-256 checksum
How to use checksums
66d6b60ed4bbaf0fef5a50ec2c2ec21c0dee3a181d2076d224eb4bb25bb5f4c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / klint-1.5.1-py3-none-macosx_10_13_x86_64.whl

Download URL klint-1.5.1-py3-none-macosx_10_13_x86_64.whl
Size 2.9 MB
Tags Python 3 macOS 10.13+ x86-64
SHA-256 checksum
How to use checksums
5b8b0067b4d1bcc8c89d4757e8df90841fa27bad20010552b89d5066d01238d2
BLAKE2b-256 checksum
How to use checksums
4b3722492a7e5de729493749eddc1b735f6bf44144a15bdecf3b8d121eede3b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Security for klint 1.5.1

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.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for klint-1.5.1-py3-none-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

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