ruff icon indicating copy to clipboard operation
ruff copied to clipboard

Implement `flake8-cognitive-complexity`

Open ngnpope opened this issue 2 years ago • 10 comments
trafficstars

GitHub, PyPI.

  • [ ] 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.

ngnpope avatar Jan 31 '23 21:01 ngnpope

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.

sbrugman avatar Feb 01 '23 11:02 sbrugman

I was looking to replace my flake8 plugins list with ruff and there's 2 plugins I couldn't replace yet:

  • flake8-cognitive-complexity
  • flake8-expression-complexity

Hope to see them implemented in ruff.

KerberosMorphy avatar Feb 21 '23 22:02 KerberosMorphy

Also in the same terms it would be nice to implement flake8-cohesion

mariosker avatar Apr 27 '23 08:04 mariosker

Also in the same terms it would be nice to implement flake8-cohesion

Didn't know that one, thanks for sharing ;)

KerberosMorphy avatar Apr 27 '23 15:04 KerberosMorphy

It'd be also nice to implement https://github.com/Miserlou/JonesComplexity that WPS uses, for example..

webknjaz avatar May 18 '23 14:05 webknjaz

Are you planning to use these rules alongside the mccabe complexity rule or instead of it?

MichaReiser avatar May 19 '23 07:05 MichaReiser

I usually have all enabled, unless they are exact duplicates, of course..

webknjaz avatar May 19 '23 12:05 webknjaz

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

KerberosMorphy avatar May 24 '23 17:05 KerberosMorphy

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.

tylerlaprade avatar Aug 08 '23 23:08 tylerlaprade

Anyone want to look at my PR? #9812

AndersSteenNilsen avatar Feb 21 '24 14:02 AndersSteenNilsen

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?

MichaReiser avatar Mar 28 '24 16:03 MichaReiser

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.

tylerlaprade avatar Mar 28 '24 16:03 tylerlaprade

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.

MichaReiser avatar Mar 28 '24 16:03 MichaReiser

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.

tylerlaprade avatar Mar 28 '24 18:03 tylerlaprade