Other linters would be welcome and invited to implement these rules, but the scope and goals would be:
to identify and define “bug magnets” which are not being or cannot be removed from the language
to only provide feedback on correctness, never style
to document each of these as part of the Python docs, helping explain concepts like for-else with some specific negative examples
to encourage other tools to flag these usages as well
to provide one simple and performant (enough) tool which implements only these correctness lints
Some example rules would be return-in-finally and for-else-without-break.
I’m suggesting this specifically as an alternative to PEP 765, but if that PEP is accepted this may still be interesting. That said, I would like to consider it first as a direct alternative, weighing a SyntaxWarning against an official guide and tool external to the runtime.
That’s an interesting idea! An official list of rules that third-party linters should enable by default would go a long way in getting rid of bug magnets. I think the current issue with “just use a linter” is that I have to be actually aware of the issue and I have to explicitly enable it. But if linters enabled some rules by default because it was recommended officially would definitely help the “just use a linter” case.
We already have ruff which is simple and performant. The problem is just that it has derived its rules from pyflakes which has hundreds of rules many/most of which should not be used in any given codebase. There is no organisation of the rules into categories that actually make sense from the perspective of choosing which rule sets to use in the linter configuration. I think that people who wanted to maintain a list of lint rules like this could do so without it needing to be “official”. This can just be a feature request or PR for ruff to define a named group of rules. Editors and other tools that have default rule sets already include way too many rules that should not be in this list and so would likely not choose to use such a minimal list if it existed.
I suspect that it would be impossible to get universal agreement on which are the rules that only concern “correctness”. The value of maintaining such a list comes from being conservative about what is included so that it is very unlikely that anyone would want to disable any of the rules or would want to ignore the reported errors. Once such a list exists though if it is widely used there would be strong pressure from lint enthusiasts to try to get their favourite rules included in it.
Inevitably over time whether the list is official or not I would expect that the list of rules will only grow and will eventually come to include rules that shouldn’t be there. Presumably this is already what has happened to each of the existing named rule categories and is why you end up having e.g. return-in-try-except-finally (SIM107) in the same category as random opinionated stuff like “yoda-conditions” (SIM300).
The point of linters is that they are not governed by Python. As soon as something is blessed as “official”, it gains a special status that is very hard to shake. Note, for example, how Python does NOT have a standard library type checker; mypy has somewhat special status but it’s not the one official type checker.
Keeping things out of the Python standard library (or other forms of officialness) gives everyone the freedom to make whichever choices they like.
There’s no need for the core team to standardize a set of linting rules. Standards provide guarantees of interoperability, we don’t need that for linting. Linting tools can compete on speed, strictness, utility, etc. Different projects can adopt different linters based on their needs, preferences and desires.
Rules that are widely considered good will be widely implemented by linters, and more projects will benefit. That’s all that’s needed.
As a single data point of prior art, C++ has officially blessed “core guidelines”: C++ Core Guidelines which is quite exhaustive (although I’d venture to say that says more about the language )
At the very least I think curating a set of “common pitfalls” (that can’t be fixed) and “gotchas” might be useful for linters and the like to include as part what they offer. Also beginners, code reviewers, etc… can link to it.
I agree but one thing that doesn’t seem to work well in this competition right now is defining good minimal sets of lint rules. There is an open market with many linters and hundreds of rules to choose from but actually choosing which to use and configuring the linter is a lot of work and so much potentially useful linting doesn’t happen.
I suspect that the kind of people who contribute to linters are also often the kind of people who really like enforcing lots of lint rules and that means that every rule set grows to include questionable rules. The end result is that there are no linters or rule categories that can be enabled unquestioningly without tedious evaluation and configuration.
It would be great if linting tools could divide the rules so that there are large sets of only uncontroversial rules that can be enabled easily without having to sift through hundreds of random rules. Having just one bad or controversial rule within any category significantly weakens the entire set so discipline is required to maintain a curated set of uncontroversial rules.
The problem is just that it has derived its rules from pyflakes which has hundreds of rules many/most of which should not be used in any given codebase. There is no organisation of the rules into categories that actually make sense from the perspective of choosing which rule sets to use in the linter configuration.
IMO this is more an issue of ruff documentation than anything else, there are default rules, which are presumably intended to be good for all codebases. But it’s difficult to find out precisely which ones are default and why they are default and why others aren’t. But that’s obviously an issue to raise directly with ruff, not here.
The specification doesn’t define precisely what’s valid and what’s not, though, and there have periodically been discussions in which people say things like “X flags this as an error, Y doesn’t”. Python standardizes the meanings of type hints but not the full set of validation rules.
But in any case, I still stand by my statement that anything you would call a “linter rule” needs to NOT be standardized. The cases that are so clearly universal that they belong in some sort of curated list are the ones that get SyntaxWarning (and subsequently SyntaxError).
This makes the idea sound like a PEP, which it wasn’t really meant to be. I meant more to suggest it as a bit of living documentation. Having a tool is secondary. Having a place to put information like “continue-in-finally suppresses exceptions” is the primary idea I wanted to float.
Would you feel differently if I phrased it as “documentation of Python anti patterns, with links to linters which check for them”, and dropped the implementation aspect?
I see such documentation as similar in purpose. It’s an escape-valve for usages which are not becoming SyntaxErrors but which are known to be problematic.
I wouldn’t have disagreed this time last week, but I feel that I have to point out that PEP 765 is under debate, and might not become a warning and later error. If “return in finally” isn’t being removed from the language (i.e. PEP rejected), I see two options for what we can do with the knowledge that it swallows exceptions and the research done in support of that PEP:
nothing
try to socialize the knowledge that this usage is usually wrong in practice
So this is why I’m not overly attached to my initial phrasing of this idea as a lint tool. I’m equally happy with explanatory docs for “The for-else without break antipattern” and so forth.
I want to note some overall discomfort with this line of thinking, which is that it seems to privilege ruff above other linters. Ruff should not be treated as special – it’s just an option in the community which has copied many rules from other popular tools.
If the best way to improve things for the community is for ruff to implement some new convenience feature, then
Cool
What about the users who aren’t using or don’t want to use ruff? e.g., users relying only on pycharm’s checking
Yes, and I would say that if the PEP is rejected, so will any kind of official linter rule whereby everyone is expected to follow it. The rejection of the PEP would be a signal that this should be up to individual freedom.
Isn’t PEP8 already an official set of linting and formatting rules? It seems like a natural place for authoritative prescriptions for the use of quirkier parts of the language (such as return-in-finally). It might be in a form of a new section in the document or an expansion to Programming Recommendations which already deals with the higher-level use (it also has s a bit about return-in-finally at the end). An addition of stricter phrasing such as do not use instead of discouraged there might be warranted.
Also linters are usually very quick to pick up and implement every little thing that is mentioned in PEP8 because of the perceived authority and renown that it has.
It depends on the reason: If the PEP is rejected because the warning could be shown to users that can’t do anything about it, that doesn’t mean this couldn’t find a place as a lint rule for the developers.
I wouldn’t draw that conclusion from PEP rejection. One of the main issues under discussion is whether SyntaxWarning disproportionately impacts downstream users versus library maintainers.
Right now I think there’s an issue of “how do we add errors and warnings but only for the code authors and not for their consumers?”
I don’t think I’m likely to convince you – I think we’re starting from pretty distant starting points – but I want to highlight that I’m only even toying with this idea because it seems that “SyntaxWarning is too impactful” is coming up as an issue. And I’m trying to figure out “well, what’s even less impactful than a non-fatal warning but still better than nothing?”
I am not saying that ruff should be treated as special because I am not supposing that this contribution to ruff comes from any official capacity. I’m just saying that it is an open source tool to which you can contribute if you would like to see improvements. You can also open a PR to other linters if you want.
I don’t see why we need an “official” set of lint rules but that doesn’t stop anyone from working to make a useful set of rules more easily available from ruff or other linters.
I wouldn’t mind a suite of optional lints built into the python interpreter that flag things like a return in finally as having edge cases that people need extra information about and link to more information about that, but I think if we’re considering this as an alternative to making things that aren’t strictly incorrect a syntax warning, it needs to be optional, in the standard library, and with support for it in the interpreter like how the warnings can be filtered at invocation.
I guess this would be ideal for cases we know are error-prone but have a useful correct usage when applied intentionally.
Having a standardized set and names for linting rules would make sense, but like how the typing community got together to define the typing spec it should be the linting tools that come together and propose this spec. They are the ones with the most knowledge and most interest after all. The fact that they haven’t points to the demand (from the linting tools’ perspective) either not being that high or not there being more important things to consider. Having people from the outside come in and dictate how these things should be done might not sit right with them.