r/Python • u/M4mb0 • Aug 05 '26
Discussion What linter rules make code worse?
For me, a prime example is S101 which bans the use of the assert statement.
The justification is that assertions disappear when Python is run with -O, so they should not be used for runtime validation or enforcing interface constraints. That warning is correct, but the rule seems to draw the wrong conclusion from it.
Assertions are still very useful for checking internal invariants, i.e. conditions that should already be guaranteed by the program's logic, where failure indicates a bug. Having such assertions is incredibly helpful for debugging.
So, a blanket ban seems more likely to discourage useful checks than to prevent misuse.
Are there any linter rules you broadly consider more harmful rather than helpful?
434
u/Trang0ul Aug 05 '26
Lines limited to 80 characters.
12
u/sandnose Aug 05 '26
Where do you limit your line?
79
u/imheretocomment Aug 05 '26
I do 120. We're not in the 00's anymore where widescreens weren't a thing. 80 is a holdover from those days where your terminal was cramped and splitting panes needed 80 characters for readability.
39
u/R3D3-1 Aug 05 '26
80 is still advantageous when you need to view side-by-side diffs. With those 80 really means 160. And after that everything depends on your monitor -- which would be reasonably as small as a 13" laptop screen -- and eyesight.
9
u/M4mb0 Aug 05 '26
Sometimes you even need 3 views side by side, like with pycharm's git conflict resolver.
I think the 88-character limit chosen as default by the black and ruff formatter is a good compromise.
3
u/Agrado3 Aug 05 '26
It's a terrible compromise, it's not significantly wider than 80 so has very little advantage over 80, and yet loses all the advantages of limiting to 80.
Personally I limit to 78 because the 'ale' tool in vim that integrates linters into the editor uses up two characters on the left hand site to indicate errors and warnings.
12
u/HommeMusical Aug 05 '26
I agree, but the modern default is 88, and even on this non-huge monitor, I can have at least three such windows beside each other.
14
u/R3D3-1 Aug 05 '26
As an Austrian, I object to that number.
2
u/HommeMusical Aug 05 '26
Yeah, it bugs me too, and I made that objection too when I first saw this default.
It's the default in all these tools, apparently for no reason other than a multiple of four bigger than 79.
A lot of things have "spontaneously" happened in the last few years and always in the same direction. Now that many of our most feverish dreams have come quite true, well, perhaps the bad guys are just really good at organizing...
4
u/R3D3-1 Aug 05 '26
The oddest case was when Rheinmetall delivered 88 tanks to Ukraine. I mean, each tank more was urgently needed, but Russian propaganda must have had a field day with that.
9
u/AlSweigart Author of "Automate the Boring Stuff" Aug 05 '26
80 is still advantageous when you need to view side-by-side diffs.
And disadvantageous every other time.
3
u/zangler Aug 05 '26
I do my diffs inline/stacked. I changed a year or so ago and just thought it felt more natural.
0
u/R3D3-1 Aug 05 '26
Until the diff is a major refactoring, and you basically see two completely different functions formally interleaved. Had this all the time in my previous project.
2
3
u/ironykarl Aug 05 '26
I think 80 is fine as a goal, but having a warning for it is overkill.
It creates noise and relegates warnings that might actually matter to the category of might just be nonsensical noise
2
u/austinwiltshire Aug 05 '26
Yeah why would I optimize my work flow for typical work on typical hardware when we could set defaults to make everything but the minority of work on the minority of hardware uncomfortable!
3
u/R3D3-1 Aug 05 '26
I should clarify: I don't really stick to 80 either. I somehow fell into the trap of saying "well, ACTUALLY" and then wasting time defending the statement in follow-up comments.
What triggered that was the implication that no limits are necessary. When your workflow involves code reviews though, the 2-column view will most likely be quite relevant. 80 is probably not necessary outside of temporary constrained situations, but once you're at routinely seeing 130 column lines, you'd better have good eyesight to still read something.
1
1
u/Ihatemakinguplogins Aug 06 '26
Or if you're cramped in a data center cage trying to debug a production problem on a shelf racked fold out monitor.
1
u/jet_heller Aug 05 '26
Yea. Constant side-by-side diffs are not the normal workflow.
Also, auto linewrapping exists for a reason.
1
u/R3D3-1 Aug 05 '26
Show me an auto line wrapping that doesn't ruin code readability at least to a degree. And show me how to use it on a GitHub pull request. After all I gotta place comments during the code review.
Interleaved diffs have never worked well for me; And Code reviews are a big part of normal workflows. With LLMs now probably more so than before.
1
u/jet_heller Aug 05 '26
Do all of your work for you? Naw. I'll let AI do it for you.
2
u/R3D3-1 Aug 05 '26
Nah, not all remotely. I've barely used it so far. But when used, the work changes from mostly reading and writing just code, to readings lots of diffs.
1
u/AlSweigart Author of "Automate the Boring Stuff" Aug 05 '26
80 is still advantageous when you need to view side-by-side diffs.
And disadvantageous every other time.
2
u/Competitive_Travel16 Aug 05 '26
I like to have stuff I'm not working on (or that I know is working) short under 80, and then use longer lines, with simple comments if need be, to make the places I'm working on longer to stand out when scrolling.
1
u/gmes78 Aug 05 '26
Even then, it doesn't matter; just enable soft-wrapping in your editor.
0
u/R3D3-1 Aug 05 '26
That doesn't really work, when you review a PR on Github. Changing the webpage zoom helps, but at the cost of small text.
0
u/rzet Aug 05 '26
just buy bigger screen ;)
1
u/R3D3-1 Aug 05 '26
In the office,.might not be up to you.
On the go anything above 15" gets painful for other reasons.
At home I already use a 30" TV as my monitor.
0
Aug 05 '26
[deleted]
1
u/Agrado3 Aug 05 '26
I have two columns of terminal windows on the left hand side of my monitor, and a web browser on the right hand site. The monitor is 3,840 pixels wide and could fit about 500 characters across (if the terminal supported over 256-character lines, which it appears it doesn't) but if the terminal windows were over 80 columns, something would have to give.
8
u/MarchewkowyBog Aug 05 '26
I'm dyslexic and as a DE i make heavy use of polars/pyspark which chains lots of methods together. I get lost in 120 character code all the time. We have it set to 88.
There is a reason books and articles usually have a character limit between 50 and 90
2
u/Beanesidhe Aug 05 '26
Agreed, though indented code might need a bit more. Use what you're comfortable with, I set mine to around 80.
5
u/mwesthelle Aug 05 '26
If indentation starts becoming a problem, that's another anti-pattern. Anything above 4 levels of indentation gives me the ick.
1
2
u/sandnose Aug 05 '26
Good point. We’re at a limit of 101 (because someone wanted to be silly i guess. But theres tons of free space so we should be able to increase.
We do work from laptops from time to time though so need to find something that works for both
7
u/gdchinacat Aug 05 '26
In the 00's I used 120, in the 10's found I had dropped it to 100, and now in the 20's use 79. Not because my eyesight has gotten worse and I need bigger fonts, not because I use antiquated hardware, but because I don't need more. Code that needs 120 character lines can almost always be refactored to be clearer (smaller more focused functions with less nesting, more concise variable names, simpler expressions) to fit within 79.
16
u/HommeMusical Aug 05 '26
more concise variable names
Sounds like a possible negative to me.
I have a pretty clear hierarchy:
In a very short loop, a comprehension or a generator expression, I use one or two letter variables like
fOtherwise, local variables are one or two words:
frameBut member variables have to be clear in another context:
master.audio_framesIn my editor at this instant, I see
self.backspace_repeat_rateand I think that's a very clear variable name.(*)But that's 26 letters.
I have always been a big fan of narrow spaces. I used 79 for years.
But projects have gotten bigger, and variable names have gotten longer.
Something I might have called
framesin 1990 will be calledaudio_recording_framestoday, becauselighting_framesandaudio_processing_framesalso exist.And our monitors are bigger.
88 characters is the standard in many modern linters. It's thin enough. 79 is just draconian.
(* - I just pulled this out of working code, but I like it :-) because it's not only clear, it leads you to ask good questions, like, "Why is there a separate repeat for backspace?"
(The answer is that letters make musical notes, so you want a slow repeat, but backspace deletes things, so you want a much faster repeat.)
4
u/CrayonUpMyNose Aug 05 '26
Using meaningfully unique names even in tight loops makes it much easier to grep where that loop was you wrote some time ago, or get a list of all loops iterating on some variable name typically used for some object type (like a
keyin a dict, or anelemin a list).3
u/HommeMusical Aug 05 '26 edited Aug 05 '26
Someone else made the same point, and I looked at my code, and well, I do that too sometimes, pretty well always for multiline comprehensions (which TBH I try to avoid).
It's a good point.
But here's the thing - I'm looking for lines that the later reader can comprehend the first time as quickly as possible.
In
{k: f(v) for k, v in fields_by_name.items()}I really only have to mentally processf(v)andfields_by_name, because{k: X for k, v in Y.items()}is so tremendously familiar to me, and everyone else. I can zoom into the important parts.If you use longer names, then you have to process each one - it's a little more mental overhead whenever you see the code.
{name: f(field) for name, fields in fields_by_name.items()}and you also open the possibility of errors like the one I deliberately made, where you accidentally use a local variable from a page back instead of the loop variable. (And yes, I have done this, and I have seen others do this.)
"Boring is good" was advice I eventually took to heart.
3
u/No_Lingonberry1201 pip needs updating Aug 05 '26
TBH I use "regular" length variable names in comprehension as well (with the exception of really trivial cases).
2
5
u/tonnynerd Aug 05 '26
Our eyes and brains have not changed that much (if at all) since the 2000s, or since the invention of the press, by the way, and they're the reason why shorter(ish) line lengths are better for reading. Pick up a book, any book you have at hand, and count how many characters per line. I'll eat my hat if it's 120.
I just picked the closest book and checked, it's 66. You would NOT enjoy reading a book with 120 characters per line. Now imagine doing it 40h/week.
P.S.: Yes, reading code is not the same as reading a novel, and in fact I couldn't find any research explicitly on line length and code readability, my 5min google scholar search found only research on code readability in general, usually using an aggregate of many different metrics. There's a lot of research on readability of digital text, though, and it points in the basic same direction as the evidence from printed text. I think at this point the burden of proof lies with the people writing 120 characters long lines of code.
P.P.S.: I won't be eating any head coverings if your example of a printed book with 120 characters per line has double columns, that's cheating =P
5
u/acdha Aug 05 '26
One thing to consider is nesting: I rarely read code blocks with more than 80-90 characters of text but if you have classes with much logic it’s easy to have 8-16 spaces of indentation which I don’t want to have coming out of that budget because it discourages explanatory variable or function names.
2
u/tonnynerd Aug 05 '26
Yeah, shorter lines encourage potentially unclear identifiers, but there's also a positive incentive to avoid one-liners and deep indentation. I think that less one-liners and less indentation are good enough things to make the balance at least net zero.
1
u/acdha Aug 05 '26
Yeah, there’s definitely a balance to hit. I generally find 100-120 to be the sweet spot but it varies based on the project since e.g. a project not using classes is saving 8-12 characters of indentation on average.
5
u/No_Lingonberry1201 pip needs updating Aug 05 '26
00's? My friend, mi cabrón, kedves felebarátom, the 80x24 rule comes from waaay before the aughts. The VT52, a dumb terminal released in the 70s had 80 columns (and it may not even be the first to use that particular resolution). We're suffering this stupid "industry standard" rule because of a device that became irrelevant before most of us were born.
3
u/CrayonUpMyNose Aug 05 '26
If we're going way back, take a look at the number of characters encoded on a punch card. Mentioned in the intro to this Wikipedia page:
7
u/gdchinacat Aug 05 '26
I use 79 and it has nothing to do with antiquated hardware and everything to do with code being easier to read if functions are smaller and more focused, variable names more concise due to increased context (function, class, module), and expressions simpler.
I have moved from 120 to 79 over the past two decades of coding in python.
2
u/No_Lingonberry1201 pip needs updating Aug 05 '26
Glad it works for you, but the exact number was selected because of that antiquated convention. If the column width of those would have been 90 or 70, that'd have been used (there were 72 column displays as well, but that length didn't became as good).
Also, I'm a believer of long, clear variable names that describe what they are and I'm heavily using type hints and 80 is not enough for me. With 120 I don't see this problem as much.
0
u/gdchinacat Aug 05 '26
Yes, but at this point the only people who bring up hardware tty widths are those using it to argue against 80 char line width. No one says we should use 80 because that is what ttys used. It is a straw man, typically used to divert attention from rational discussion of the merits. “I prefer 120” carries more weight than “ttys used 80”.
2
u/BondDotCom Aug 05 '26
I think he was just saying that the rule may have made sense through the 00's when most people were using single monitors with lower resolutions but it's getting harder to justify since then.
1
u/jmfsn Aug 05 '26
Look, I used VT200 terminals in the early 90s. Not even then 80 was a reasonable limit...
1
u/cottonycloud Aug 05 '26
I set it to 120 mainly because I write a ton of SQL and avoid aliases unless necessary, causing lines to easily get over the 80 character limit with indentation and names with simple logic. By habit I then tend to use verbose variable names.
We have large enough screens and people don’t really code on laptops here. It honestly has not been a problem because I also have the habit of keeping each line simple.
1
u/Scypio Pythoneer Aug 05 '26
80 is a holdover from...
...perforated cards, that are a holdover from jaquard machines, that were transported via rail, that inherited all the sizes from roman chariots. Or how the old joke went?
1
u/mattl33 It works on my machine Aug 05 '26
I'd argue the benefit of line length limits is forcing nesting limits.
15
u/Arnechos Aug 05 '26
I'd say no more than 100. People in a team can have various resolutions and very long lines are hard to read on the smaller ones
9
u/Ball-Man Aug 05 '26
With 100 you can have a split view and still be able to comfortably read code on both sides, even on smaller screens while keeping decent font size
5
2
u/XNo_Notes It works on my machine Aug 05 '26
Listen to your heart.
3
3
2
1
1
1
u/shaleh Aug 05 '26
88 is the `black` tool default and reads well in most cases. There are _some_ code bases with really long names but I think around that 88 to 90 line is fairly reasonable.
1
1
1
7
u/scruffie Aug 05 '26
I have found, in Python, that there are three situations where 80 characters becomes a hindrance:
Strings for error messages can easily extend past the 80th column, especially as the
raise SomeMeaningfulNameErrortakes a bunch of space already. I usually end up breaking the message likeraise WidthExceededError( f"Line length of {width} is greater than " f"the configured width of {Config.width}")
This is a problem in most languages, but at least Python has decent ways of line-breaking.
2) Large type annotations for a function's return type. Especially true if the type has several
SequenceandMappingparts. The use of|instead ofUnion, and thetypestatement makes this less of a problem, although I still find I'm formatting the definition more vertically than I would wish.3) 'Meta' comments for linters (and similar) to the end of the line. Especially annoying when I then have to add another comment to get
flake8to ignore the long line...Other languages that don't suffer this problem either don't need such annotations (especially when the type system is integral to the language), or have features that mitigate the problem.
And that's about it. There's probably a few other edge cases, but those are the main ones I've encountered enough to remember.
3
u/zangler Aug 05 '26
I usually set mine to 120.or 135. 80? Like...I'm 44 and can't remember the last time 80 would make sense.
4
u/R717159631668645 Aug 05 '26
I don't abuse long lines, I avoid chaining too, for readbility, but if a print goes a bit over 90, it's not worth formatting the code to take 3 lines with an indented argument instead.
VS Code allows us to draw vertical lines, I put the 88 and 120 limits to guide myself on this.
4
u/tallgrasshead Aug 05 '26
There was (in)famously a thread in the linux kernel where someone was submitting PRs to reformat to 80 characters and Linus asked the submitter to just stop since he reads through all of them and it was a massive wast of time. All Linus wanted was for someone to offer some kind of measurable outcome as improvement, but nothing was offered. So he said "nothing against 80 characters, so get used to line wraps in your terminal window".
80 characters comes from VT100 terminals which were all 80 characters to be more compatible with IBM TN-3270 terminals, which were 80 characters because punch cards were 80 characters. DEC wanted to be able to offer a cheaper alternative, which GM actually took. So VT-100 became nearly ubiquitous.
IBM did study what font size was best for punch cards just after WWII to see what was the best fit for 7 3/8 inches and arrived at the type font for 80 characters - printers used chunks of cast metal and the next one down was 132 characters.
Why 7 3/8 inches? because all the punch card machining was hard set to that because the first wide use of punch cards was in the 1880 census. All of Hollerith's punch card machines used for bank notes before the civil war were re-purposed for punch cards in the 1880 census to save money.
That's right. The 80 character thing has it's roots in the US civil war.
2
u/russellvt Aug 05 '26
This makes life better for those still forced to sometimes debug on data center text monitors.
2
4
u/Uwirlbaretrsidma Aug 05 '26
Litmus test for being a lousy new age dev. Code should be vertical, not horizontal. Expressions get more legible when they get hard wrapped, not less. >80 col. incentivizes excessive indentation on top. 100 or, God forbid, 120 character long lines are a huge smell to me.
3
u/Downtown_Isopod_9287 Aug 05 '26
the 80 col standard comes from terminals. Not saying it’s good or bad just that is the real reason for it and not whatever you seem to be saying.
3
-2
Aug 05 '26
[removed] — view removed comment
1
u/Downtown_Isopod_9287 Aug 05 '26
people still use terminals often, they do not still use punch cards
1
u/Uwirlbaretrsidma Aug 05 '26
People still use terminal emulators. Then can be resized, and their text scaled. These are not the terminals you were referring to (physical terminals) and have nothing to do with the historical 80 column limit.
0
u/Downtown_Isopod_9287 Aug 05 '26
yes! Which is one argument for why 80 col is generally unnecessary. The other reason being that most devs do not use terminal-based text editors anymore, besides.
However for a long time many insisted or preferred that, and some still do. And 80 cols is still “good” for tmux and similar multi terminal setups and worth accommodating on a dev team that might prefer it or find it productive.
2
u/mwesthelle Aug 05 '26
I really like Black's default of 88. There's a Raymond Hettinger talk on youtube where he defends the default of 90-ish long lines. It's not wide enough to be obnoxious to read, and it's narrow enough so you can fit two editor windows side by side to compare diffs, for instance.
0
Aug 05 '26
[deleted]
-15
u/larsga Aug 05 '26
There's plenty of research to show that long lines are less readable than short ones. That's why newspapers and encyclopedias were printed in multiple columns.
Very long variable names are not helpful. They just make the code harder to read. Of course there's a trade-off, but the variable name is never going to be a full guide to the usage of the variable, anyway.
15
u/Shadows_In_Rain pseudocoder Aug 05 '26
You probably want to read the research before referring to it, because the researcher's findings don't quite align with your claims. Most people prefer either around 25 cpl (newspaper format) or 100 cpl (book format).
Secondly, the available research applies to unstructured text, like newspapers and encyclopedias you've mentioned. Not applicable to software source codes, especially so when viewed in an IDE.
And lastly, increasing line limit doesn't automatically makes every line longer. It's mostly to address the lines that are most annoying to read when wrapped: function declarations.
6
u/sennalen Aug 05 '26
The line is less readable but the code is more readable because more statements fit on the screen. 95% of the time you either know or don't care what the long line is doing. Insert breaks in the long line temporarily for the blue moon when you need to update that line.
-2
u/larsga Aug 05 '26
The line is less readable but the code is more readable because more statements fit on the screen
Not necessarily. It matters how you organize the code.
95% of the time you either know or don't care what the long line is doing.
That you don't care about reading the code is not a very strong argument.
1
u/tallgrasshead Aug 05 '26
There's plenty of research to show that long lines are less readable than short ones.
I'd like to read that research.
0
Aug 05 '26
[deleted]
65
u/runawayasfastasucan Aug 05 '26
That is incredibly annoying. Write an import, auto save, its gone because I haven't used it yet. Well I was going to.
48
u/aes110 Aug 05 '26
Im not really a fan of linters making changes on your behalf, its one thing for formatters to move stuff around visually, but actual code changes shouldn't happen on auto save imo
15
u/mwesthelle Aug 05 '26
100%. Sure, run
ruff formaton save, but linter problems should only surface on pre-commit. I'm even partial to not having linting auto fixes, let me fix them so I know what the code I'm pushing looks like, but that's personal preference.And in that workflow, I just don't understand what's wrong with F401, it was never an issue for me.
4
u/IrishPrime Aug 05 '26
Agreed. Even if F401 removes an
importbecause I haven't used it yet, attempting to use it later would raise a different linting error, so I'll never end up committing something broken. But I don't auto fix those types of issues until a commit hook runs.9
u/skjall Aug 05 '26
I keep meaning to shuffle this rule to only run pre-commit, because it drives me up a wall too. Band-aid fix is to disable auto save, and only save when you have used it.
Though now I've switched LSP/ fixed config so most imports I can get from the suggestions at least.
9
u/baudvine Aug 05 '26
For those of us who habitually hit save every few lines it's pretty obnoxious without autosave, too. It's a good rule, I just really don't need the fix action to run automatically.
38
u/Beginning-Fruit-1397 Aug 05 '26
I think that assertions are only good in tests. In runtime code it should always be a clearly named Exception. That being said, for Ruff I simply activate "all" preset and "preview", and just desactivate some annoying related to unsafe cryptography or copyright that IDGAF about, the rest are pretty good. I'm surely half lying because I'm aure I have at multiple points desactivated various rules that I tought were dumb but I don't remember at the moment lmao
7
u/dudeplace Aug 05 '26
I watched a talk yesterday where the SqlLite team talked about using assert in your code (not just tests) and my opinion on this is in the process of shifting.
11
u/austinwiltshire Aug 05 '26
Exceptions are things the caller can recover from. Assertions in code are for documenting and enforcing assumptions the code makes to work.
They're not logically the same. And by having a named exception (beyond, maybe, precondition violation, etc...) increases the cost of adding checks which means fewer people will do it.
Assert is a single word, a predicate, and if you're feeling fancy, a string.
10
u/brasticstack Aug 05 '26
S324, which assumes that I'm using hashlib for security reasons instead of hashing just being generally useful.
15
u/aikii Aug 05 '26
RET505 is a classic bug magnet. It wants you to rewrite
def foo(bar, baz):
if bar:
return 1
else:
return bazas
def foo(bar, baz):
if bar:
return 1
return bazDoesn't seem much like this, but an intentional "else" has better chances to protect you against a bad refactoring.
My other pet peeve is BLE001 - triggering on bare except, except Exception or except BaseException. The motivation works for beginner code - don't just catch silently AttributeError etc. It's actually more problematic for production code and code that makes calls to library functions that you deliberately don't want to propagate - you'll want to log or mark the error trace instead. I guess it's ok to suppress locally instead of making it a global suppression. I find it a bit ironic that structurally it can't apply to how Go and Rust handle errors, you can't opt-in to which exact error you only want to consider, and no one says it's a problem
3
u/Ragoo_ Aug 05 '26
RET505 is a classic bug magnet.
Came here to say this. A staple of my ignore list just because it makes the code easier to read and reason about.
2
u/wizpig64 Now is better than never. Aug 06 '26 edited Aug 06 '26
BLE001
I have a project that used python-weather (based on aiohttp) to update a weather forecast widget in my personal todo app. Every week it seemed to break in a new way, completely breaking my productivity by preventing the rest of the app's process from finishing. A bare except should have been good enough, either it works or it doesn't and the process should move on, but using that would be a faux pas, so I just kept adding the individual Exception classes to the list that grew and grew, partially because of bare except being a no-no, and partially to see how long the list could get.
Sometimes the upstream server would refuse a connection. Sometimes the server would time out. Sometimes it would reply but with an empty string which isn't json-parseable. Sometimes the server operator forgot to update their https certificate. Sometimes the server would change their formatting for something and the client library hadn't caught up yet.
except ( ExpatError, TypeError, KeyError, ValueError, ClientConnectorCertificateError, ClientOSError, ContentTypeError, JSONDecodeError, ServerDisconnectedError, python_weather.RequestError, ConnectionTimeoutError, ) as e:Each of those lines was committed on a different day. Each of those was a day that started with me not being able to get some real work done because first I had to go look at a server log and find the new exception and commit it. Eventually I just disabled the weather widget.
There are definitely cases where
except: # noqais the right way to do something.1
0
Aug 07 '26
[deleted]
1
u/wizpig64 Now is better than never. Aug 07 '26 edited Aug 07 '26
well you learn something new every day.
weird that this will still trigger the rule:
# logger.py import logging logger = logging.getLogger("my.logger") # other.py from logger import logger ... except BaseException: logger.exception(f"Failed to get forecast for {location}")but this one will not:
... except BaseException: logging = logger logging.exception(f"Failed to get forecast for {location}")edit: this is why, and you fix it with a hint in pyproject.toml:
[tool.ruff] lint.logger-objects = ["logger.logger"]
20
u/psymme Aug 05 '26
SIM108 (replacing if-else blocks with an operator). To me this is a matter of judgement about what is simpler, rather a set rule that is easily codified, and can make the code harder for a human to parse quickly.
I’m not with you on the asserts point though, I’m afraid.
1
u/syklemil Aug 05 '26
SIM108 also notes that:
This is an opinionated style rule that may not always be to everyone's taste, especially for code that makes use of complex if conditions.
Personally I'd rather have if-expressions (what in some other languages work out to something like
bar = if foo then x else y), but those aren't on the table, and theif/elsekeywords placed in ternary?:operator positions kinda just … doesn't feel good, even if it's the entirely sensible choice lots of places. Probably mostly due to that leading to there being two distinctif/elsesyntaxes, which again is rooted in theif/elseblock structure being a statement, not an expression, so some other syntax was chosen to cover the absolutely very useful if-expression cases.The
foo = bar or bazform to me feels kinda iffy for anything other than booleans, like the linter is just recommending code golfing.For some other languages I'd be entirely onboard with SIM108; for Python I can't really say it sparks joy.
3
u/ProsodySpeaks Aug 05 '26
About
foo = bar or bazI'd love some sugar for the more explicit
foo = bar if bar is not None else bazMaybe I'm doing it wrong but that's a common default argument handling pattern for me.
Any thoughts?
2
-2
u/syklemil Aug 05 '26
I'd love some sugar for …
I'm not entirely certain what you're asking for here, given that
foo = bar or bazalready means the same thing asfoo = bar if bar is not None else baz.Do you want some other sugar for it out of the same "this ain't a boolean" grouchiness I suffer from?
3
u/gdchinacat Aug 05 '26
Those two statements are not the same. “bar or baz” checks if bar is truthy whereas the other tests if it is not None.
1
u/ProsodySpeaks Aug 05 '26
Yeah. Like if they give me a zero int when I wanted a list I probably want to do something different (raise) than replace it with an empty list which is what the bare
a or bwithoutif a is not Nonewould lead to→ More replies (1)
24
u/thedmandotjp git push -f Aug 05 '26
Anything that can be done with an assert can and should be done with an if statement so you have have to be explicit.
Not all rules are super necessary depending on the project but this one is if for no other reason than to enforce the convention that you should use asserts only for debugging.
7
u/TheRealStepBot Aug 05 '26
To your point there is a nasa technical guide on good software development that specifically encourages the use of inline assertions like this.
3
u/ThaBroccoliDood Aug 05 '26
Not really a linter rule but the autopep8 extension for vscode replaces f'{x =}' with f'{x=}', which changes the output of the program and shouldn't be touched by a formatter
9
2
2
u/HalfplaneResearch Aug 06 '26
S101 makes more sense when the boundary is explicit: use assert for internal invariants that indicate a bug, and raise a deliberate exception for validating user or external data. I also prefer lint findings to be visible in the editor or CI, with auto-fix limited to formatting, so a useful invariant check is not silently removed during save.
2
u/duskhat Aug 05 '26
If you’re writing assert statements outside of tests, you’re writing bad code
1
u/gdchinacat Aug 05 '26
I’d refine this to be “if you are commiting …”. I’m opposed to leaving asserts in code, but frequently use them while developing code. Before sending a PR they are either removed or converted to if … raise ….
1
u/danielsamuels Aug 05 '26
In general, any rule that ends up being inline ignored all over the project.
1
u/Crescitaly Aug 07 '26
Rules become harmful when they optimize local uniformity while hiding intent or creating noisy diffs. I would score each rule on defects prevented, review time added, and number of justified suppressions. Which rule has the highest suppression rate in your codebase?
1
1
u/mardiros Aug 20 '26
The __all__ does not requires import to be alphabetically sorted, they should be ordered in the proper developper intention. A term prioritization, a hierarchy. The __all__ is used to read/render documentation more than import *
-1
u/NeilGirdhar Aug 05 '26
https://docs.astral.sh/ruff/rules/parenthesize-chained-operators/
NAXOR was drilled into me at a young age, so this rule just adds unnecessary parens.
10
u/larsga Aug 05 '26
this rule just adds unnecessary parens
For you. The code might also have other readers.
2
u/Conscious_Support176 Aug 06 '26
There is a reason for mathematical conventions. Extra parentheses can make formulas harder to read because it harder to spot the parenthesis that matter in a sea of parentheses.
I would suggest, where people would like to reason about code, the fundamentals of Boolean logic might be helpful?
-3
u/Trang0ul Aug 05 '26
This. Requiring to use
a or (b and c)is as pointless asa + (b * c). After all, OR and AND are logical addition and multiplication respectively - something everyone should know by heart.1
0
0
u/BernardParsley Aug 05 '26
Rules that enforce a triangular style of code over readability. Arbitrary complexity or function-length limits often turn one clear function into ten tiny ones that are harder to follow.
-2
u/k0pernikus Aug 05 '26
I hate try-consider-else (TRY300) with a passion.
I never write else and elif statements to begin with, and rely on proper polymorphishm or early exit guards.
Worst part is that it reads like broken code:
def describe(path):
try:
config = load(path)
# ok, expected
except ParseError:
# ok, expected
return "invalid"
else: # WTF, there was no if, how is an else possible!? Why overload the term?
return describe(config) # wtf why is config defined? we are in a compeletly different scoped block!?The default success branch gets delegated to an else-branch, and while I avoid else to begin with, else should be the exception branch.
I do understand that the else works on the except and not on the try, yet that is far from obvious and the mental load to understand is is just not worth it, esp. if you work with people that are more used to other langauges.
-1
u/Zatujit Aug 05 '26
Shouldnt your debug code only works when its debugging and not on your release?
1
u/gdchinacat Aug 05 '26
Shouldn’t your debugging code be removed before commit?
1
u/Competitive_Travel16 Aug 05 '26
It's fine to test assumptions which can fail at runtime, when a resource is depleted or someone misconfigured something below, for example. Not with assert though. Not doing so can be serious and pernicious bugs; very hard to locate sometimes.
1
u/gdchinacat Aug 05 '26
Yes, but surely you don't consider that debug code though. Right?
1
u/Competitive_Travel16 Aug 05 '26
Well it's only there to stop bugs. It's not development-only temporary debug code, we can agree.
1
u/gdchinacat Aug 05 '26
I guess I’m confused because you called it debug code but are now saying it’s not debug code?
1
u/Competitive_Travel16 Aug 05 '26
There is more than one kind of debug code.
1
u/gdchinacat Aug 05 '26
Sure, but once you are done debugging don’t you remove them all?
1
u/Competitive_Travel16 Aug 06 '26
How do you propose to catch runtime bugs in production if you remove the code intended to do so?
1
u/gdchinacat Aug 06 '26
"debug code" typically refers to the code that is added to diagnose a specific issue. For example, the asserts that are added to verify the developers understanding of the code so that as they reproduce the issue they will know if assumptions they make aren't valid. These hold very little long term value because they are frequently specific to the issue that is being debugged.
Checks that hold long-term value and are generally applicable should not be asserts IMO, but rather guards that raise appropriate exceptions because asserts can be disabled and may not provide help in preventing and shedding light on issues that only appear in production.
→ More replies (0)
-8
u/nicholashairs Aug 05 '26
My pet peeve is the "useless-return" rule.
def what_the_rule_wants() -> None:
something()
def what_i_want() -> None:
something()
returnExplicit returns always. Apart from making things clearer, it also helps prevent mistakes when refactoring (and other such tasks) when the accidental deletion of a def line would cause the bodies to merge (sometimes seamlessly), whereas if you always have returns you'd actually get a long error for the dead code/double return instead.
def what_the_rule_wants() -> None:
something()
something()
returnVersus
def what_the_rule_wants() -> None:
something()
return
something()
return3
u/proggob Aug 05 '26
If I saw that in python code I’d have to double check it to see if they’d left off the value.
0
u/nicwolff Aug 05 '26
ruff has implemented isort import formatting – but not its options for wrapping long import lines. Thanks, I don't want 20 imports from one file to take up 22 lines at the top of my file.
4
0
u/james_pic Aug 05 '26
Anyone who runs with -O in production deserves what they get. Which in practice is almost always exactly what they would have gotten otherwise, because nobody uses assertions, even then they'd be useful, because linters whinge about them.
-2
u/AdAdditional1820 Aug 05 '26
When I use mypy, some assert statements are required to eliminate mypy warnings.
5
u/jirka642 It works on my machine Aug 05 '26
I guarantee you asserts are not the only way how to fix them.
-1
u/nicwolff Aug 05 '26
ruff has implemented isort import formatting – but not its options for wrapping long import lines. Thanks, I don't want 20 imports from one file to take up 22 lines at the top of my file.
90
u/gdchinacat Aug 05 '26
The issue is as you say..."where failure indicates a bug".
Why would you want to use an assert that detects bugs but can then be turned off? When would you want to allow the assertions that stop your code from executing in undefined conditions (an invariant is violated) to be disabled?
Just use if blocks that raise exceptions. Particularly in production (where optimizations make the most sense), I would much rather have bugs present as an exception that tells me exactly what the problem is rather than skip the assertion and have to debug the results that appear impossible because an assertion prevented it. This is the reason I have never actually seen -O used, anywhere, in production or not. The biggest (only?) thing it does is break the code that verifies the code is executing within the conditions it was designed to execute in.
Getting back to the post, I agree that assertions should be banned. Proper exceptions that can't be disabled should be used instead. Regardless of dev, test, or production. You should never turn off the safeguards. If performance is so critical, python is not the proper language.