noqaexplain
Comply or explain - justify every ignored linting rule.
✨ Features 🚀 Quick start 📚 Documentation 🤝 Contribute 👍 Adopters 📜 Legal
Features
noqaexplain is a linter which enforces justifying every ignored linting rule supporting multiple formats/linters:
- Python -
ruffandflake8# noqa,coveragepy# pragma: no cover, andlintkitline, span, and file suppressions - JavaScript/TypeScript -
eslint - Rust -
clippy - Dockerfiles -
hadolint - YAML -
yamllintandlintkitline, span, and file suppressions - TOML -
lintkitline, span, and file suppressions - Markdown -
PyMarkdown,Vale, andmd-dead-link-check - Shell -
shellcheck
Table of contents
Quick start
Installation
> pip install noqaexplain[all]
To install rich output and MCP server as well:
> pip install noqaexplain[all]
Usage
To check against all files (the ones with defined mappings from file extension to error disable comment format), run:
> noqaxplain check
You can pass additional arguments to noqaexplain check, like files
to check:
> noqaexplain check path/to/file.py maybe.rs other.yml formats.js
If a certain file has a line with disabled check without an explanation, the tool will report it:
path/to/file.py:10:5: ENQ0 Missing explanation (enoqa) for disabled linting rule
to fix it, just add an explanation after the disable comment prefixed by enq:,
e.g.:
import some_library
# enq: Disabled private access check as there is no other workaround currently.
# noqa: SLF001
some_library._private_function()
The same rule applies to opening span and file-wide lintkit directives:
# enq: Generated settings are checked separately before publication.
# noqa-file: MYLINTER1
# enq: This generated section cannot follow the repository style.
# noqa-start: MYLINTER2
generated = true
# noqa-end: MYLINTER2
Markdown suppressions use an HTML comment for the explanation:
<!-- enq: Generated content cannot satisfy the line length rule. -->
<!-- pyml disable-next-line line-length -->
Markdown matching uses the literal comment prefixes <!-- pyml, <!-- vale,
and <!-- md-dead-link-check. Every matching comment needs an explanation.
Advanced
Configuration
You can configure noqaexplain in pyproject.toml (or .noqaexplain.toml
in the root of your project, just remove the [tool.noqaexplain] section),
for example:
[tool.noqaexplain]
explain_noqa_pattern = "enq:"
# Extends Python noqas mappings
# Now every # my_noqa_header: will be treated as a noqa comment
# and checked for explanations.
extend_mapping_suffix = {".py" = ["# my_noqa_header:"]}
# Target any MySuperFile.md file(s) and look for explanations
extend_mapping_name = {"MySuperFile.md" = ["# my_noqa_header:"]}
[tool.noqaexplain.ENQ1]
min_explain_length = 10
Select rules and stopping behavior with CLI flags, for example:
noqaexplain check --names ENQ0 --end_mode first
Run as a pre-commit hook
noqaexplain can be used as a pre-commit hook, to add as a plugin:
repos:
- repo: "https://github.com/open-nudge/noqaexplain"
rev: ... # select the tag or revision you want, or run `pre-commit autoupdate`
hooks:
- id: "noqaexplain"
Rules
noqaexplain provides the following rules:
| Name | Description |
|---|---|
NQE0 |
Ensures that all disabled linting rules have an explanation on the nearest preceding nonblank line |
NQE1 |
Ensures that all disabled linting rules have an associated explanation of at least |
Shared options belong in [tool.noqaexplain] (or at the root of
.noqaexplain.toml):
| Option | Description | Affected rules | Default |
|---|---|---|---|
extend_mapping_suffix |
Additional file suffix to noqa comment(s) format mappings (dict of lists) | All | {} |
extend_mapping_name |
Additional file name to noqas comment(s) format mappings (dict of lists) | All | {} |
mapping_suffix |
File suffix to noqa comment format(s) mappings (dict of lists, overrides default!) | All | {} |
mapping_name |
File name to noqa comment format(s) mappings (dict of lists, overrides default!) | All | {} |
dir_ignores |
Directory names ignored during default file discovery | All | standard |
extend_dir_ignores |
Additional directory names ignored during default file discovery | All | [] |
explain_noqa_pattern |
String identifying explanation for disabled linting rule | All | "enq:" |
ENQ1 options belong in [tool.noqaexplain.ENQ1]:
| Option | Description | Default |
|---|---|---|
min_explain_length |
Minimum char length of explanation for disabled linting rules | 10 |
Contribute
We welcome your contributions! Start here:
Legal
- This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
- This project is copyrighted by open-nudge - the appropriate copyright notice is included in each file.
Metadata
Release files for noqaexplain 2.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 | |
|---|---|---|---|
| noqaexplain-2.0.0.tar.gz | 18.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| noqaexplain-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.9 kB
Release files / noqaexplain-2.0.0.tar.gz
| Download URL | noqaexplain-2.0.0.tar.gz |
|---|---|
| Size | 18.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b8c0e88c6227f11b58eff8f3063e8656c15fc8e01056a5aee503140fdebcdda
|
|
BLAKE2b-256 checksum How to use checksums |
622d26c6444373f3cb06c0025c4b38ad1947ee178d555b9ef9fe9e4720a15051
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Sep 4, 2026.
Transparency logRelease files / noqaexplain-2.0.0-py3-none-any.whl
| Download URL | noqaexplain-2.0.0-py3-none-any.whl |
|---|---|
| Size | 12.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a37c375757c4e56d1fa2492515e9fde5f6b29e1068ba17f91d56bf2f1d7bf932
|
|
BLAKE2b-256 checksum How to use checksums |
19f6a7f82e1ceff32a22b916cd208a75ba781478f09df9c840a049dc06eed2a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Sep 4, 2026.
Transparency log