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 violations1= completed, violations found2= 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:
--config PATHflake8_lint.tomlpyproject.tomlwith[tool.flake8_lint]pyproject.tomlwith[tool.flake8_lint_tests]- 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
X001bareexcept:X002except Exception:X003reserved/disabledX004silently swallowed exceptionsX005missing or malformed docstringsX006local imports inside functions or classesX007value-returning functions without return annotationsX008explicit-> NoneX009old%string formattingX010suppressedImportError/ModuleNotFoundErrorX011Type | Noneinstead ofOptional[Type]X012non-NonePEP 604 unions likeType1 | Type2instead ofUnion[...]
# noqa semantics
Supported forms:
# noqa
# noqa: X002
# noqa: X001, X002
# noqa: ACME001
- bare
# noqasuppresses package violations on that line - code-qualified markers suppress only matching codes or prefixes
allow_noqa = falsedisables suppression globallynoqa_allowedrestricts suppression to matching file paths when non-emptynoqa_forbiddenalways 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-noqamap into the shared engine - installed
flake8_lint.rulesproviders 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.pyunless 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_rulesto publicflake8_lintAPIs 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| flake8_lint-1.0.0.tar.gz | 38.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.