ruff
ruff copied to clipboard
Implement `flake8-cognitive-complexity`
- [ ]
CCR001: Cognitive complexity is too high (X > Y)
Configuration options: max-cognitive-complexity
This plugin is focused on cognitive complexity vs cyclomatic complexity which we have via mccabe. Apparently this is the algorithm used by SonarSource, CodeClimate, etc. according to the README. See here for more details.
Cool plugin! ruff would be a nice opportunity to spread this to a wider audience.
It passes over de function body once to computer a complexity score >= 0. Should not be too hard or expensive to add.
I was looking to replace my flake8 plugins list with ruff and there's 2 plugins I couldn't replace yet:
flake8-cognitive-complexityflake8-expression-complexity
Hope to see them implemented in ruff.
Also in the same terms it would be nice to implement flake8-cohesion
Also in the same terms it would be nice to implement
flake8-cohesion
Didn't know that one, thanks for sharing ;)
It'd be also nice to implement https://github.com/Miserlou/JonesComplexity that WPS uses, for example..
Are you planning to use these rules alongside the mccabe complexity rule or instead of it?
I usually have all enabled, unless they are exact duplicates, of course..
Are you planning to use these rules alongside the McCabe complexity rule or instead of it?
I always include cyclomatic + expression + cognitive.
Here a quick example which demonstrate each complexity:
foo
Would be the best option if we only looks for Cyclomatic complexity.
But the expression is complex, especially for a junior developer or someone who just start with Python.
The Cognitive represent, as I interpret it myself, the amount of context you have to store in your brain.
def foo(**kwargs): # Cognitive: 3, Cyclomatic: 1, Max Expression: 5
return [ # Expression: 5
key if key == value else value if value in kwargs else f"{key}+{values}"
for key, value, in kwargs
]
bar
Would be the worst option if we only looks for Cyclomatic complexity.
But the expression is simple, each line is easy to understand.
You could need a higher level of context storage in your brain as you analyse this code, which is why it give a higher Cognitive complexity.
def bar(**kwargs): # Cognitive: 5, Cyclomatic: 4, Max Expression: 2
res = [] # Expression: 2
for key, value, in kwargs.items():] # Expression: 1
if key == value: # Expression: 1
res.append(key) # Expression: 1
continue
if value in kwargs: # Expression: 1
res.append(value) # Expression: 1
continue
res.append(f"{key}+{values}") # Expression: 2
return res
boo using subboo
Cyclomatic complexity is reduce compare to bar but still higher than foo.
Max Expression is the same than bar.
The Cognitive complexity is at 2, each function is pretty simple to analyze without the need to keep too much context in your brain.
def subboo(key, value, keys): # Cognitive: 2, Cyclomatic: 3, Max Expression: 2
if key == value: # Expression: 1
return key
if value in keys: # Expression: 1
return value
return f"{key}+{values}" # Expression: 2
def boo(**kwargs): # Cognitive: 0, Cyclomatic: 1, Max Expression: 2
return [subbar(key, value, kwargs) for key, value, in kwargs.items()] # Expression: 2
FWIW, flake8-cognitive-complexity and flake8-cohesion are the only two rules I still run. If those were added to Ruff, I could fully remove flake8 from my project.
I do run McCabe in Ruff as well, but they serve three different purposes, IMHO.
Anyone want to look at my PR? #9812
I do run McCabe in Ruff as well, but they serve three different purposes, IMHO.
Could you explain how the purpose between Mc Cabe and this rule differ in your view?
Could you explain how the purpose between Mc Cabe and this rule differ in your view? -@MichaReiser
Cognitive complexity is how hard the code is to understand, while cyclomatic complexity is the number of independent branches. For example, a switch case that handles every letter of the alphabet might be very easy to understand, but it would have 26 separate branches.
Thanks, that makes sense. What's the motivation for catching the switch if it is easy to understand?
I'm asking because we're currently leaning towards having one single complexity rule with a configuration option for the metric to avoid overlap (and conflicts). However, that wouldn't support your use case.
Ah, I see your point! The main reason I run McCabe is because I don't want to run flake8 in Github Actions for just a single rule. I personally wouldn't mind replacing it.
However, I do find unique benefit from the related flake8-cohesion, which looks at a class as a whole.