Suppression Comment Design #4051
Replies: 3 comments 4 replies
-
I'm not really familiar with how the current suppression comments work in Ruff, so pardon me if I say nonsense.
This means that you could do As someone not familiar enough with Ruff or other Python linters, I don't know what What would be clearer to be would more verbose comments
It's a lot more verbose (and autocomplete would help) but it's far easier to understand for a human, which I believe has its merits (I heard something about "explicit over implicit" in Python...). And as per your question, I think it would make sense to enable rules that have not been enabled for this file, if they've been explicitly been enabled inside of the file. Behind-the-scenes, for rules that collect some information (and not just pattern-match on a single element), I think you would need to run the rule anyway (to collect the information) but suppress the errors, or you could do a quick scan of which rules get enabled. I think there could be value in having a separation between "enable" comments and "reeanable": If you do want to "enable" rules that were previously not set up for this file, you could have a separate set of comments that follow the ideas from above.
Maybe I'm going against decisions that have been taken previously that I'm not aware of. But just like |
Beta Was this translation helpful? Give feedback.
-
just my 2 cents, why not |
Beta Was this translation helpful? Give feedback.
-
Just writing to say that I would really love seeing Approach 2 implemented! :) Is there any active development yet or are you still awaiting feedback? |
Beta Was this translation helpful? Give feedback.
-
Background
Several issues (#1054, #3711, #2999) mention the desire for block-scoped
noqa
comments and generalnoqa
changes. There is also appetite for human-friendly rule names (#1773) which could be incorporated intonoqa
comments. As such, I believe that there's sufficient motivation to start work on a large-scale suppression comment rework.I believe that this approach could be executed in two phases, one for reworking suppression scopes and the latter for changing the
noqa
parser. This document outlines potential solutions for the first phase, which I think would take the most work. The second phase would probably involve changing thenoqa
parser to allow human-readable names to be picked up as well as a translation layer to go from rule to name and back again. However, it probably falls outside the scope of this document.Phase 1 Proposal
We can build on the syntax introduced in #2978 to allow code blocks by enabling/disabling
noqa
selectors.Note that all approaches can be easily extended to include multiple rules by following the typical convention of
<rule1>,<rule2>,...
.We can also easily extend both approaches to add enabling on a per-block basis in the future, if we need to. We could build in logic to parse the appropriate suppression comment (either
disable
orenable
) and tailor our logic around that.Approach 1
This should yield one
AmbiguousVariableName
diagnostic on line 1. This is slightly more fragile than Approach 2 since you could delete either adisable
orenable
without deleting the other, which would prompt a new diagnostic to show up. That could get annoying, though you could make the argument that it also makes the linter more robust. No accidentally deleting a line and not knowing about it :)Pros/Cons
FAQ
E741
isn't enabled for the file being edited and I try to disable it? What if it's enabled and I try to re-enable it?RUF
rule would be appropriate which alerts the user to an unnecessarynoqa
block. We could also of course simply ignore it, but I think that goes against Ruff's philosophy - unnecessary suppression comments would clutter up the code with no good reason.# ruff: noqa: E741
?disable/enable
tags. This does lead to the issue of multiple syntaxes, though, which is something I'd like to avoid.Approach 2
This should only throw on line 2 - the idea behind this approach is that the
disable
functions on the AST node directly beneath it.This is pretty similar to Pylint's current block comment implementation. If we went with this approach, it may be worth considering whether we should simply copy their behavior exactly but with a different directive comment.
I put
< disable:>
in braces to delineate the fact that we could treat it as optional. I don't think that's a great idea though; if we did, the current# ruff: noqa
directive would need to be refactored to act in a per-block fashion and we'd have to figure out how to signify per-file suppressions. Long story short, if we went with this approach I'd strongly recommend having it be# ruff: noqa: disable: E741
and unifying the syntaxes.Pros/Cons
FAQ
E741
isn't enabled for the file being edited and I try to disable it? What if it's enabled and I try to re-enable it?# ruff: noqa: E741
?# ruff: noqa: disable: E741
at the top of the file (or in the file scope). This is in contrast to the current way of doing this:# ruff: noqa: E741
will disable everywhere, regardless of where the file is. I'd like to avoid the issue of having separate syntaxes for per-file and per-block suppression though, so IMO we should unify the two. See below for an example of how this could work.Approach 2, scoping, and per-file suppression
In this example, our
disable
comment applies to the function definition while theenable
comment applies ONLY to the expressiona = 1
. Since we enable E741 in a scope narrower than the one in which we disable it, enabling the rule would take precendence for that line only. The rest of the function would have the rule disabled.I think for the sake of this issue, it would be best to ignore the
enable
piece for now. So implementation would involveclass
statement, the suppression would apply to the entire class definition. If it's above an expression, the rule would apply only to that expression.Using this design would allow us to easily implement per-block rule enabling in the future. We could prioritize directives by narrowness of scope - i.e.,
a = 1
should throw because the rule is enabled in a scope which is narrower than the scope which disables it. Determining this "scope hierarchy" to enable/disable blocks based on scope narrowness should probably be a separate FR centered on enabling rules for certain blocks though.Rejected: I (very briefly) considered a
pyproject.toml
configuration option, but that's pretty infeasible given how often code changes. While it could clean up the code, it'd be more difficult to reason about ("why are these random diagnostics showing up? I haven't disabled them in my code anywhere? Ah they're inpyproject.toml
for some reason") and changing them whenever code lines change would be...tedious, to say the least.Notes
#4669 mentions changing the
noqa
keyword to something else. All designs here can also be considered through the lens of switchingnoqa
for a different keyword.Verdict
I'm leaning pretty heavily towards Approach 2 for two reasons. First, I remember some issue mentioning that a big component of Ruff's growth was the ease of dropping it into an existing project. Approach 2's cognitive overhead is therefore lower than Approach 1's as it would act similarly to Pylint's block disabling.
Second, and the main reason I advocate for Approach 2, is that it allows for a consistent, sensible syntax that, once understood, is pretty simple to implement and read. It integrates nicely with the existing per-file suppressions, since we could simply place the suppression in a file-scoped line separate from any expressions. That means that users wouldn't need to do all that much to accomadate this breaking change. We would simply need to change
# ruff: noqa: <rule>
to# ruff: noqa: disable: <rule>
and tell users that they'd need to put the comment on a file-scoped line.Open to any and all feedback! :)
Beta Was this translation helpful? Give feedback.
All reactions