Pylint
| Language | Python |
|---|---|
| Kind | tool |
| Readiness | 🟢 |
| Detector conformance | 🟢 |
| Checked | 2026-09-08, version 4.0.8 |
Pylint is the oldest general-purpose Python linter still in wide use, built on the astroid AST library and organised as a set of checkers that emit messages with numeric identifiers such as W0611 and symbolic names such as unused-import. In a pipeline it is slower than Ruff and is kept for its deeper inference and, above all, for the fact that a project can write its own checkers and register them alongside the bundled ones.
How it is conformant
Pylint is the strongest Python tool on section 4. The custom checker guide shows a project subclassing BaseChecker, declaring a msgs dictionary that maps a chosen identifier and symbolic name to the message text and description, and enabling it through load-plugins in the configuration (4.1). The identifier is chosen once by the rule author and is not derived from the class or file name, and it is printed with every message in the default output and in the JSON reporter (4.3). The same guide describes CheckerTestCase with assertAddsMessages for proving a checker against a fixture without running the project’s tests (4.2). Locally, pylint mymodule.py runs on a single file, and --disable=all --enable=<symbol> runs one rule in isolation (running Pylint), which meets 5.1, 5.2 and 5.4. Every message is printed with its identifier in the command’s output (5.3). pylint --help-msg=<msg-id> resolves a printed identifier to its description from the installed package, offline (6.1, 6.2), and because every bundled message’s description lives in its checker’s msgs entry, the documentation ships with the rule at the same version (6.3). The same command resolves a project checker’s identifier too, which goes further than 6.1 asks. Clause 7.1 holds because the inline route is detectable in the detector itself: the guide documents raw and token checkers that see comments, so a project can write a Pylint checker that fails on any # pylint: disable= comment, and Pylint’s own useless-suppression message already reports disables that no longer suppress anything.
How it is not conformant
Every MUST in sections 4 to 7 holds. The two SHOULDs do not: there is no bundled rule that fails a checker whose messages lack a stable identifier (4.4), and # pylint: disable=<symbol> suppresses any message inline with no reason asked for and no option that would demand one (message control) (7.2). Pylint runs its own checks on its own source, but no release-blocking audit that every bundled message resolves is published (6.4, not verified). Pylint declares no version of this document, so the 🟢 rests on the evidence above and not on a declaration.
Clause by clause
| Document | Clause | Result | Evidence |
|---|---|---|---|
| Detector | 4.1 | Yes | Custom checkers via load-plugins |
| Detector | 4.2 | Yes | CheckerTestCase in the same guide |
| Detector | 4.3 | Yes | msgs id and symbolic name chosen by the author, printed with every message |
| Detector | 4.4 | No | No rule requiring identifiers on checkers |
| Detector | 5.1 | Yes | pylint <module> (running Pylint) |
| Detector | 5.2 | Yes | Single file accepted |
| Detector | 5.3 | Yes | Messages with ids printed to stdout |
| Detector | 5.4 | Yes | --disable=all --enable=<symbol>; every message runnable locally |
| Detector | 6.1 | Yes | --help-msg=<msg-id> |
| Detector | 6.2 | Yes | Works from the installed package |
| Detector | 6.3 | Yes | Description travels in the checker’s msgs |
| Detector | 6.4 | Not verified | No published release gate found |
| Detector | 7.1 | Yes | Raw and token checkers can detect # pylint: disable; useless-suppression bundled |
| Detector | 7.2 | No | No reason required on # pylint: disable= |
Notes for a practitioner
Write the class as a checker under load-plugins, choose a message id in the custom range the guide recommends, prove it with CheckerTestCase and then with --disable=all --enable=<symbol> on the originating file. Forbid # pylint: disable with a small token checker of the project’s own, since Pylint will not do so unasked, and keep every configuration-level disable= entry beside a comment naming the hazard and scope.