pylint-disable-comment (ODC8502)
Preview (since 0.16.2.3) · Related issues · View source
Derived from the odoo linter.
Fix is sometimes available.
This rule is unstable and in preview. The --preview flag is required for use.
What it does
Checks for # pylint: disable=... comments that suppress checks Ruff now
covers, so they can be migrated to Ruff's # ruff: ... suppression comments.
A message is considered covered when it matches a Ruff rule name (most
pylint-odoo checks were ported under their original names, and many pylint
checks exist in Ruff under the same name, e.g. too-many-branches), a
pylint-odoo message code (e.g. E8102), or a known rename — either across
linters, as pylint's too-complex becoming mccabe's complex-structure, or
within Ruff's own Pylint rules, which keep pylint's code but not always its
name (C0415 import-outside-toplevel is PLC0415 import-outside-top-level).
One message can be covered by several rules, because pylint reported as a
single message what Ruff splits up: redefined-builtin is
builtin-variable-shadowing, builtin-argument-shadowing and
builtin-import-shadowing. The suppression then names all of them, since
leaving one out would let it report what the pragma used to silence.
The pragma does not have to be alone in its comment. pylint reads a #
followed by anything and then pylint:, so a codebase part-way through the
migration usually looks like # noqa: F401 pylint: disable=..., and only the
directive is rewritten, leaving the rest of the comment untouched. Prose that
merely mentions a pragma (# see pylint: disable=x for details) is not one:
what tells them apart is that a real pragma is followed by nothing, or by
another comment, but never by more words.
Why is this bad?
After migrating from pylint / pylint-odoo to Ruff, # pylint: disable
comments have no effect: Ruff does not read them, so the previously silenced
diagnostics are reported again despite the suppression comment.
Example
Use instead:
The suppression names the rule rather than its code, because a name is what a
reader recognizes and, for every check ported here, it is the very name pylint
used. Note that Ruff only resolves a rule name in a suppression comment when
preview mode is on; with preview off only codes resolve. That is not a
restriction in practice — every OD/OAPP rule is itself a preview rule, so
preview is already on wherever these suppressions matter. The exception worth
knowing is the handful of messages that map onto stable upstream rules
(print-used to print, too-complex to complex-structure): those keep
firing with preview off, and a name-based suppression stops covering them.
Fix safety
Each pylint pragma has a Ruff suppression with the same scope, so all three rewrites preserve behavior and are safe:
- an inline (trailing)
disablepragma becomes# ruff: ignore[...]on that same line; - a
disable-nextpragma becomes an own-line# ruff: ignore[...], which applies to the statement below it; - a standalone
disablepragma governs the rest of the enclosing block, which is what a# ruff: disable[...]/# ruff: enable[...]pair expresses. When the pragma opens adefbody it also covers thedefheader, as pylint does for messages anchored there (missing-return,method-required-super, ...), so thedisableis placed above the header; - a standalone
disablepragma at module level is pylint's global disable, which governs everything to the end of the file. An unclosed# ruff: disable[...]does the same, so no closing# ruff: enable[...]is appended to the file.
One deliberate difference: disable-next applies to the next line in pylint
and to the next statement in Ruff, so a multi-line statement ends up fully
covered rather than only on its first line. Widening a suppression can hide a
later diagnostic but never unsuppresses one.
A standalone pragma sharing its comment with other text is the one case left without a fix: rebuilding it as a pair consumes the whole comment, which would take that text with it.
Messages without a Ruff equivalent are kept in a # pylint: disable comment
next to the inserted suppression.