McCabe complexity checker for Python

Overview

McCabe complexity checker

Ned's script to check McCabe complexity.

This module provides a plugin for flake8, the Python code checker.

Installation

You can install, upgrade, or uninstall mccabe with these commands:

$ pip install mccabe
$ pip install --upgrade mccabe
$ pip uninstall mccabe

Standalone script

The complexity checker can be used directly:

$ python -m mccabe --min 5 mccabe.py
("185:1: 'PathGraphingAstVisitor.visitIf'", 5)
("71:1: 'PathGraph.to_dot'", 5)
("245:1: 'McCabeChecker.run'", 5)
("283:1: 'main'", 7)
("203:1: 'PathGraphingAstVisitor.visitTryExcept'", 5)
("257:1: 'get_code_complexity'", 5)

Plugin for Flake8

When both flake8 2+ and mccabe are installed, the plugin is available in flake8:

$ flake8 --version
2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2)

By default the plugin is disabled. Use the --max-complexity switch to enable it. It will emit a warning if the McCabe complexity of a function is higher than the provided value:

$ flake8 --max-complexity 10 coolproject
...
coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)

This feature is quite useful for detecting over-complex code. According to McCabe, anything that goes beyond 10 is too complex.

Flake8 has many features that mccabe does not provide. Flake8 allows users to ignore violations reported by plugins with # noqa. Read more about this in their documentation. To silence violations reported by mccabe, place your # noqa: C901 on the function definition line, where the error is reported for (possibly a decorator).

Links

Changes

0.6.1 - 2017-01-26

  • Fix signature for PathGraphingAstVisitor.default to match the signature for ASTVisitor

0.6.0 - 2017-01-23

  • Add support for Python 3.6
  • Fix handling for missing statement types

0.5.3 - 2016-12-14

  • Report actual column number of violation instead of the start of the line

0.5.2 - 2016-07-31

  • When opening files ourselves, make sure we always name the file variable

0.5.1 - 2016-07-28

  • Set default maximum complexity to -1 on the class itself

0.5.0 - 2016-05-30

  • PyCon 2016 PDX release
  • Add support for Flake8 3.0

0.4.0 - 2016-01-27

  • Stop testing on Python 3.2
  • Add support for async/await keywords on Python 3.5 from PEP 0492

0.3.1 - 2015-06-14

  • Include test_mccabe.py in releases.
  • Always coerce the max_complexity value from Flake8's entry-point to an integer.

0.3 - 2014-12-17

  • Computation was wrong: the mccabe complexity starts at 1, not 2.
  • The max-complexity value is now inclusive. E.g.: if the value is 10 and the reported complexity is 10, then it passes.
  • Add tests.

0.2.1 - 2013-04-03

  • Do not require setuptools in setup.py. It works around an issue with pip and Python 3.

0.2 - 2013-02-22

  • Rename project to mccabe.
  • Provide flake8.extension setuptools entry point.
  • Read max-complexity from the configuration file.
  • Rename argument min_complexity to threshold.

0.1 - 2013-02-11

  • First release
Comments
  • def inside def

    def inside def

    Hi!

    Why def inside def increase complexity of outer function? def inside def looks like method inside class. Maybe, complexity of inner function should be independent from outer function?

    For example, complexity of test_nested_functions_snippet equals to 1 instead of 3.

    opened by evvers 14
  • unify visitors for if and loop statements

    unify visitors for if and loop statements

    This addresses the issue I noted in #10 as well as closing a longstanding TODO (handle else clause of for).

    It also adds the support for global constructs (that loops had) to if statements.

    The core insight is that if statements are not really different from for loops in structure, so I've refactored such that they reuse code. In the course of events, I factored out the misfeatures of the two code paths.

    opened by bukzor 11
  • ignoring functions via special comments

    ignoring functions via special comments

    like # noqa for PyFlakes and # pragma: no cover for coverage it would be great if mccabe had a way of ignoring certain functions.

    Usage:

    1. functions are necessarily greater than the complexity level I want to enforce elsewhere.
    2. Starting to use mccabe to improve existing code bases without having to either have mccabe always fail or have to set a max-complexity which is way to high to have a real impact.

    (I kind of assumed there would be an existing issue about this but I couldn't see it, sorry if I'm being blind)

    opened by samuelcolvin 9
  • Use start of identifier as start position

    Use start of identifier as start position

    Would it be possible to have the ranges reported by this tool updated to start at the start of the offending identifier?

    For example with the following code:

    from example import (
        f401_unused as unused_module)
    
    
    def c901_too_complex(x):
        if x > 1:
            if x > 2:
                if x > 3:
                    if x > 4:
                        if x > 5:
                            if x > 6:
                                if x > 7:
                                    if x > 8:
                                        if x > 9:
                                            if x > 10:
                                                if x > 11:
                                                    pass
    
    
    class Foo:
        def foo_c901_too_complex(x):
            if x > 1:
                if x > 2:
                    if x > 3:
                        if x > 4:
                            if x > 5:
                                if x > 6:
                                    if x > 7:
                                        if x > 8:
                                            if x > 9:
                                                if x > 10:
                                                    if x > 11:
                                                        pass
    
    
    def indent_unaligned():
        try:
            print('H501 %(self)s' % locals())
        except:  # <- H201
            pass
    

    This module simply reports the start of the line as the problem location:

    > python -m mccabe --min 10 customRange.py
    5:1: 'c901_too_complex' 12
    21:1: 'Foo.foo_c901_too_complex' 12
    

    If the identifier start was reported instead, the positions would be 5:5 and 21:9 respectively. As it stands right now the column number is just plain wrong.

    opened by Arcanemagus 9
  • Why does adding a for loop increase cyclomatic complexity?

    Why does adding a for loop increase cyclomatic complexity?

    According to this article cyclomatic complexity directly measures the number of linearly independent paths through a program's source code.

    Now observe the following code:

    def f():
        for i in range(10):
            print i
    

    This code outputs a complexity of 2 with the program.

    However, there is only one possible path of execution here.

    opened by robrechtdr 9
  • Add console script 'mccabe' for usability

    Add console script 'mccabe' for usability

    Hi! Thanks for useful tool! I use it at git-pre-commit-hook

    For debug reasons I often run mccabe alone without flake8. For me run

    mccabe file.py
    

    is easier than

    python -m mccabe file.py
    

    Yes, bash alias can help. But every developer must define that alias.

    opened by evvers 8
  • Add multiple files support

    Add multiple files support

    resolves #94 I've added support for entering multiple arguments as files to check for complexity. I also added handling of the case when the user does not enter anything as an argument for CLI.

    opened by Yourun-proger 7
  • max-complexity overwritten with string or bool in pep8.py

    max-complexity overwritten with string or bool in pep8.py

    Using flake8, mccabe and setuptools leads to setting wrong max_complexity option.

    When you set setup.cfg to

    [flake8]
    max-complexity = 2
    

    the command

    ./setup.py flake8 
    

    will not produce correct output, while calling

    flake8
    

    does.

    Setting it to max-complexity=1 works, but the type of the option is set to boolean. When set to 2 it's a string instead of the requested int thus leading to always False comparisons at the mccabe code.

    The option is parsed correctly at pep8 but is overwritten at pep8.py in class StyleGuide:1629:

           if options_dict:
                options.__dict__.update(options_dict)
    

    I don't know if the problem lies in the mccabe code, pep8 or flake8, so reporting it here.

    Versions used:

    Python 2.7.6

    argparse (1.2.1) autopep8 (0.9.1) coverage (3.7.1) flake8 (2.4.0) matplotlib (1.3.1) mccabe (0.3) nose (1.3.1) numpy (1.8.2) pandas (0.13.1) pep8 (1.5.7) pexpect (3.1) Pillow (2.3.0) pip (1.5.4) ply (3.4) Pmw (1.3.2) pyflakes (0.8.1) Pygments (1.6) pyinotify (0.9.4) pyparsing (2.0.1) python-dateutil (1.5) pytz (2012c) PyX (0.12.1) requests (2.2.1) ScientificPython (2.9.4) scikit-learn (0.14.1) scipy (0.13.3) SciTools (0.9.0) setuptools (2.2) Shapely (1.3.0) simplegeneric (0.8.1) simplejson (3.3.1) six (1.5.2) SQLAlchemy (0.8.4) ssh-import-id (3.21) statsmodels (0.5.0) sympy (0.7.4.1) tables (3.1.1) Twisted-Core (13.2.0) UFL (1.5.0) urllib3 (1.7.1) virtualenv (1.11.4) VTK (5.8.0) Werkzeug (0.9.4) wheel (0.24.0) wsgiref (0.1.2) xlrd (0.9.2) xlwt (0.7.5)

    opened by PyGuy2 6
  • Python 3.7.0b4 test failures

    Python 3.7.0b4 test failures

    $ tox -e py37
    ...
    ============================= test session starts ==============================
    platform linux -- Python 3.7.0b4, pytest-3.6.0, py-1.5.3, pluggy-0.6.0
    rootdir: .../mccabe, inifile:
    collected 14 items                                                             
    
    test_mccabe.py ..F...........                                            [100%]
    
    =================================== FAILURES ===================================
    ____________________ McCabeTestCase.test_expr_as_statement _____________________
    
    self = <test_mccabe.McCabeTestCase testMethod=test_expr_as_statement>
    
        def test_expr_as_statement(self):
            complexity = get_complexity_number(expr_as_statement, self.strio)
    >       self.assertEqual(complexity, 1)
    E       AssertionError: 2 != 1
    
    test_mccabe.py:192: AssertionError
    ===================== 1 failed, 13 passed in 0.06 seconds ======================
    ERROR: InvocationError: '.../mccabe/.tox/py37/bin/python setup.py test -q'
    ___________________________________ summary ____________________________________
    ERROR:   py37: commands failed
    
    • pytest 3.6.0
    • six 1.11.0
    • py 1.5.3
    • pluggy 0.6.0
    • more_itertools 4.2.0
    • attrs 18.1.0
    • atomicwrites 1.1.5
    opened by hroncok 5
  • Cyclomatic complexity of code with path that will never be executed

    Cyclomatic complexity of code with path that will never be executed

    Is cyclomatic complexity supposed to count paths that will never be executed?

    Observe the following code:

    def f(n):
        if n > 4:
            return "bigger than four"
        elif n > 5:
            return "is never executed"
        else:
            return "smaller than or equal to four"
    

    The program outputs a cyclomatic complexity of 3 even though the elif path will never be executed.

    Regardless of if cyclomatic complexity as described in this article does (type I) or does not (type II) count code paths that will never be executed I think it is useful to make this distinction more clear than it is now.

    Depending on what cyclomatic complexity is supposed to represent you could name their specific types. Some suggestions:

    Type I cc : 'unminimized cc' or 'unreduced cc' (this is what the mccabe program currently evaluates for) Type II cc : 'minimized cc' or 'reduced cc'

    opened by robrechtdr 5
  • Add property-based fuzz test

    Add property-based fuzz test

    A test is added that runs mccabe against plenty of random yet valid Python source code. Right now, the test does not do much - it only tests whether mccabe accepts the code. Already this is an achievement because now weird edge cases are covered.

    Having this test suite integrated will also allow to build up on that. The author of Hypothesis claimed to work on a coverage guided test runner. Also more general properties to hold on any valid source code now can be tested.

    opened by MaxG87 4
  • [docs] Incorrect date on release note for 0.7.0

    [docs] Incorrect date on release note for 0.7.0

    Really minor issue, but I noticed that in the changes section of the README it shows the release date of 0.7.0 to be 2021-01-23 when presumably it should be 2022-01-23?

    opened by amrishparmar 1
  • [Proposal] Allow disabling

    [Proposal] Allow disabling "try-except" clauses evaluation.

    When using third party services libraries, or even on your own code, you may find a scenario were a function may return several different types of exceptions each of them requiring a simple but different action to be taken.

    Exhaustive Exception treatment on those cases leads to a high complexity result which is right, as per the complexity calculation strategy, but an undesired result.

    def foo(bar):
        try:
            do_whatever(bar)
        except TypeA:
            do_something_related_to_TypeA()
        except TypeB:
            do_something_related_to_TypeB()
        except TypeC:
            do_something_related_to_TypeC()
        except TypeD:
            do_something_related_to_TypeD()
        except TypeE:
            do_something_related_to_TypeE()
        except TypeF:
            do_something_related_to_TypeF()
        except TypeG:
            do_something_related_to_TypeG()
        except TypeH:
            do_something_related_to_TypeH()
        except TypeI:
            do_something_related_to_TypeI()
        except TypeJ:
            do_something_related_to_TypeJ()
    
    python -Bm mccabe deffoobar.py                     
    1:0: 'foo' 12
    

    It will be interesting being able to ignore exceptions branching to properly compute the complexity of the actual function logic without the burden of the exception treatment one.

    opened by CaBazaga 1
  • [Proposal] Add a check for none/multiple positional arguments

    [Proposal] Add a check for none/multiple positional arguments

    The current behaviour of python -m mccabe (no positional argument provided) is an IndexError

    File "/home/junior/.local/lib/python3.10/site-packages/mccabe.py", line 327, in main
        code = _read(args[0])
    IndexError: list index out of range
    

    Also, for python -m mccabe file1 file2 it just ignores file2.

    Would it be possible to add a check for a missing positional argument, and another for multiple ones?

    opened by jrom99 0
  • 0.7: test failure

    0.7: test failure

    When running the self tests on the pypi sdist file, I see:

    # setup.py  test
    running test
    WARNING: Testing via this command is deprecated and will be removed in a future version. Users looking for a generic test entry point independent of test runner are encouraged to use tox.
    running egg_info
    writing manifest file 'mccabe.egg-info/SOURCES.txt'
    running build_ext
    test_mccabe (unittest.loader._FailedTest) ... ERROR
    
    ======================================================================
    ERROR: test_mccabe (unittest.loader._FailedTest)
    ----------------------------------------------------------------------
    ImportError: Failed to import test module: test_mccabe
    Traceback (most recent call last):
      File "/usr/pkg/lib/python3.10/unittest/loader.py", line 436, in _find_test_path
        module = self._get_module_from_name(name)
      File "/usr/pkg/lib/python3.10/unittest/loader.py", line 377, in _get_module_from_name
        __import__(name)
      File "/scratch/devel/py-mccabe/work/mccabe-0.7.0/test_mccabe.py", line 244, in <module>
        @settings(
    NameError: name 'settings' is not defined
    
    
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    

    I see that it trys to import hypothesmith, but this is not installed on my system, and it has an except clause just ignoring the problem.

    opened by 0-wiz-0 3
Owner
Python Code Quality Authority
Organization for code quality tools (and plugins) for the Python programming language
Python Code Quality Authority
Various code metrics for Python code

Radon Radon is a Python tool that computes various metrics from the source code. Radon can compute: McCabe's complexity, i.e. cyclomatic complexity ra

Michele Lacchia 1.4k Jan 7, 2023
Inspects Python source files and provides information about type and location of classes, methods etc

prospector About Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and comple

Python Code Quality Authority 1.7k Dec 31, 2022
A tool for measuring Python class cohesion.

Cohesion Cohesion is a tool for measuring Python class cohesion. In computer programming, cohesion refers to the degree to which the elements of a mod

null 177 Jan 4, 2023
McCabe complexity checker for Python

McCabe complexity checker Ned's script to check McCabe complexity. This module provides a plugin for flake8, the Python code checker. Installation You

Python Code Quality Authority 527 Dec 21, 2022
A Python application for tracking, reporting on timing and complexity in Python code

A command-line application for tracking, reporting on complexity of Python tests and applications. wily [a]: quick to think of things, having a very g

Anthony Shaw 1k Dec 29, 2022
AntroPy: entropy and complexity of (EEG) time-series in Python

AntroPy is a Python 3 package providing several time-efficient algorithms for computing the complexity of time-series. It can be used for example to e

Raphael Vallat 153 Dec 27, 2022
Easily train your own text-generating neural network of any size and complexity on any text dataset with a few lines of code.

textgenrnn Easily train your own text-generating neural network of any size and complexity on any text dataset with a few lines of code, or quickly tr

Max Woolf 4.8k Dec 30, 2022
Easily train your own text-generating neural network of any size and complexity on any text dataset with a few lines of code.

textgenrnn Easily train your own text-generating neural network of any size and complexity on any text dataset with a few lines of code, or quickly tr

Max Woolf 4.3k Feb 18, 2021
Relative Positional Encoding for Transformers with Linear Complexity

Stochastic Positional Encoding (SPE) This is the source code repository for the ICML 2021 paper Relative Positional Encoding for Transformers with Lin

Antoine Liutkus 48 Nov 16, 2022
A Low Complexity Speech Enhancement Framework for Full-Band Audio (48kHz) based on Deep Filtering.

DeepFilterNet A Low Complexity Speech Enhancement Framework for Full-Band Audio (48kHz) based on Deep Filtering. libDF contains Rust code used for dat

Hendrik Schröter 292 Dec 25, 2022
Metrinome is an all-purpose tool for working with code complexity metrics.

Overview Metrinome is an all-purpose tool for working with code complexity metrics. It can be used as both a REPL and API, and includes: Converters to

null 26 Dec 26, 2022
Genetic Algorithm for Robby Robot based on Complexity a Guided Tour by Melanie Mitchell

Robby Robot Genetic Algorithm A Genetic Algorithm based Robby the Robot in Chapter 9 of Melanie Mitchell's book Complexity: A Guided Tour Description

Matthew 2 Dec 1, 2022
This application aims to read all wifi passwords and visualizes the complexity in graph formation by taking into account several criteria and help you generate new random passwords.

This application aims to read all wifi passwords and visualizes the complexity in graph formation by taking into account several criteria and help you generate new random passwords.

Njomza Rexhepi 0 May 29, 2022
Estimation of the CEFR complexity score of a given word, sentence or text.

NLP-Swedish … allows to estimate CEFR (Common European Framework of References) complexity score of a given word, sentence or text. CEFR scores come f

null 3 Apr 30, 2022
"Complexity" of Flags of the countries of the world

"Complexity" of Flags of the countries of the world Flags (png) from: https://flagcdn.com/w2560.zip https://flagpedia.net/download/images run: chmod +

Alexander Lelchuk 1 Feb 10, 2022
Simple Python style checker in one Python file

pycodestyle (formerly called pep8) - Python style guide checker pycodestyle is a tool to check your Python code against some of the style conventions

Python Code Quality Authority 4.7k Jan 1, 2023
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 9.4k Jan 7, 2023
Run-time type checker for Python

This library provides run-time type checking for functions defined with PEP 484 argument (and return) type annotations. Four principal ways to do type

Alex Grönholm 1.1k Dec 19, 2022
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 9.2k Jan 3, 2023
Naming Convention checker for Python

PEP 8 Naming Conventions Check your code against PEP 8 naming conventions. This module provides a plugin for flake8, the Python code checker. (It repl

Python Code Quality Authority 411 Dec 23, 2022