Skip to main content

flake8-lint

Reusable AST-based Python lint rules with a standalone CLI, a thin Flake8 adapter, explicit pytest helpers, and first-class custom-rule support.

What it is

flake8-lint packages the copied flake8_project_rules behavior as a reusable distribution:

  • standalone flake8-lint check
  • shared Python API
  • built-in X001–X012 rules with reserved X003
  • project-local and installed custom rules
  • opt-in pytest helper
  • thin Flake8 integration over the same core engine

Installation

pip install flake8-lint

For local development:

pip install -e .[dev]

Basic CLI usage

flake8-lint --version
flake8-lint check
flake8-lint check .
flake8-lint check src tests
flake8-lint check path/to/file.py
flake8-lint check --select X002 --ignore X011 src
flake8-lint check --output-format json .
flake8-lint check --rule-module my_project.lint_rules .
flake8-lint check --no-rule-plugins .

Exit codes:

  • 0 = completed, no violations
  • 1 = completed, violations found
  • 2 = invalid config, invalid invocation, or tool failure

pyproject.toml configuration

[tool.flake8_lint]
include = []
exclude = []
select = []
ignore = []
allow_noqa = true
noqa_allowed = []
noqa_forbidden = []
rule_modules = []

Legacy [tool.flake8_lint_tests] is still accepted as a 1.0.0 migration fallback when the canonical section is absent. The CLI warns:

[tool.flake8_lint_tests] is deprecated; rename it to [tool.flake8_lint].

flake8_lint.toml configuration

include = []
exclude = []
select = []
ignore = []
allow_noqa = true
noqa_allowed = []
noqa_forbidden = []
rule_modules = []

Discovery precedence:

  1. --config PATH
  2. flake8_lint.toml
  3. pyproject.toml with [tool.flake8_lint]
  4. pyproject.toml with [tool.flake8_lint_tests]
  5. defaults

include is a whitelist. exclude is a blacklist and wins. select is a whitelist. ignore is a blacklist and wins. noqa_allowed and noqa_forbidden are path filters for files allowed to use # noqa; noqa_forbidden wins.

Built-in rules

  • X001 bare except:
  • X002 except Exception:
  • X003 reserved/disabled
  • X004 silently swallowed exceptions
  • X005 missing or malformed docstrings
  • X006 local imports inside functions or classes
  • X007 value-returning functions without return annotations
  • X008 explicit -> None
  • X009 old % string formatting
  • X010 suppressed ImportError / ModuleNotFoundError
  • X011 Type | None instead of Optional[Type]
  • X012 non-None PEP 604 unions like Type1 | Type2 instead of Union[...]

# noqa semantics

Supported forms:

# noqa
# noqa: X002
# noqa: X001, X002
# noqa: ACME001
  • bare # noqa suppresses package violations on that line
  • code-qualified markers suppress only matching codes or prefixes
  • allow_noqa = false disables suppression globally
  • noqa_allowed restricts suppression to matching file paths when non-empty
  • noqa_forbidden always blocks suppression for matching file paths

If source text is unavailable, the engine still reports violations but cannot apply source-based # noqa suppression.

Custom rules

Minimal project-local registration:

# my_project/lint_rules.py
import ast
from flake8_lint import RuleContext, RuleRegistry, RuleViolation


class NoPrintRule:
    code = "ACME001"
    description = "Do not call print() in production code."

    def check(self, context: RuleContext):
        for node in ast.walk(context.tree):
            if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "print":
                yield RuleViolation(
                    context.filename,
                    node.lineno,
                    node.col_offset,
                    self.code,
                    self.description,
                )


def register_rules(registry: RuleRegistry) -> None:
    registry.register(NoPrintRule(), provider="my_project.lint_rules")
[tool.flake8_lint]
rule_modules = ["my_project.lint_rules"]

Reusable provider packages can also register through the flake8_lint.rules Python entry-point group. See docs/custom-rules.md.

Flake8 integration

flake8-lint registers a Flake8 AST plugin entry point. The adapter is intentionally thin:

  • built-in diagnostics come from the same shared engine used by the CLI/API
  • Flake8 selection and --disable-noqa map into the shared engine
  • installed flake8_lint.rules providers remain authoritative for the standalone CLI/API
  • third-party packages that need native Flake8 discovery should also expose their own Flake8 entry points

pytest integration

Pytest integration is explicit:

from flake8_lint.testing import assert_lint_clean


def test_project_lint() -> None:
    assert_lint_clean("src", "tests")

Installing flake8-lint alone does not auto-run repository lint during ordinary pytest.

CI recommendation

Prefer separate steps or jobs:

flake8-lint check .
pytest

That keeps unit and integration tests running even when lint finds violations.

Migration from the copied implementation

Before:

src/flake8_project_rules/
tests/flake8_lint/config.py
tests/flake8_lint/test_plugin_direct.py
tests/flake8_lint/test_project_lint.py
tests/flake8_lint/samples/

Add dependency:

flake8-lint==1.0.0

Then:

  • remove src/flake8_project_rules/
  • remove copied package-implementation tests now owned by flake8-lint
  • remove test_project_lint.py unless pytest should stay an explicit lint gate
  • rename [tool.flake8_lint_tests] to [tool.flake8_lint]
  • change CI from implicit pytest lint gating to:
flake8-lint check .
pytest
  • update direct imports from flake8_project_rules to public flake8_lint APIs where needed

Canonical migrated configuration example

pyproject.toml:

[tool.flake8_lint]

include = []

exclude = [
    "tests/flake8_lint/samples/",
    "src/aegis_swr/openclaude/grpc/client.py",
    "src/aegis_swr/openclaude/tool.py",
    "src/aegis_swr/openclaude/grpc/proto/",
    "src/aegis_swr/openhands/client.py",
    "src/aegis_swr/openhands/tool.py",
    "src/aegis_swr/claude_code/tool.py",
]

select = [
    "X001",
    "X002",
    "X004",
    "X005",
    "X006",
    "X007",
    "X008",
    "X009",
    "X010",
    "X011",
    "X012",
]

ignore = ["X003"]

allow_noqa = true

noqa_allowed = [
    "src/aegis_swr/core/events.py",
]

noqa_forbidden = []

rule_modules = []

Equivalent flake8_lint.toml:

include = []

exclude = [
    "tests/flake8_lint/samples/",
    "src/aegis_swr/openclaude/grpc/client.py",
    "src/aegis_swr/openclaude/tool.py",
    "src/aegis_swr/openclaude/grpc/proto/",
    "src/aegis_swr/openhands/client.py",
    "src/aegis_swr/openhands/tool.py",
    "src/aegis_swr/claude_code/tool.py",
]

select = [
    "X001",
    "X002",
    "X004",
    "X005",
    "X006",
    "X007",
    "X008",
    "X009",
    "X010",
    "X011",
    "X012",
]

ignore = ["X003"]
allow_noqa = true
noqa_allowed = ["src/aegis_swr/core/events.py"]
noqa_forbidden = []
rule_modules = []

Development / build

pytest --cov=flake8_lint --cov-report=term-missing
ruff check .
python -m build

Additional documentation:

Metadata

Release files for flake8-lint 1.0.0

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

Source distribution (sdist)

Source distribution for flake8-lint 1.0.0
File Size Uploaded Provenance
flake8_lint-1.0.0.tar.gz 38.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for flake8-lint 1.0.0
File Interpreter ABI Platform
flake8_lint-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 65.3 kB

Release files / flake8_lint-1.0.0.tar.gz

Download URL flake8_lint-1.0.0.tar.gz
Size 38.0 kB
Tags Source
SHA-256 checksum
How to use checksums
45ef19974b843f725a096d03c40863bb09b368f1bde7c10f4b075ed622c56fbb
BLAKE2b-256 checksum
How to use checksums
bd3d7fac3295425c6cebf7d89f8a4ecd7e2a906f60cdf8a347f36ce9e125deb8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / flake8_lint-1.0.0-py3-none-any.whl

Download URL flake8_lint-1.0.0-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3d6d441c6759d0c1db03fb74e155ff2243e892bd2bf7ee0b5ae9909c33534cc9
BLAKE2b-256 checksum
How to use checksums
31cc41a07b8eb7020481c47e3e2b3714ef8d4d50193d068dfdbb937746d5d702
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Security for flake8-lint 1.0.0

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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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