mccabe: the small complexity checker behind flake8's C901
McCabe complexity checker for Python
At a glance
- What is it?
- mccabe computes cyclomatic complexity for Python functions, either as a standalone command or as the flake8 plugin that reports C901. It is one module file with a long history of small corrections, and its configuration is a single threshold.
- Who is it for?
- mccabe fits Python teams that already run flake8 and want one threshold to expose sprawling functions, and it is a reasonable candidate for a pre-commit hook or a CI gate. It is the wrong tool if you want complexity metrics per class or per module, since it reports function level graphs only.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A single file checker that grew into a flake8 plugin
mccabe exists to answer one narrow question about Python code: how many distinct paths run through a given function. The project describes itself as a McCabe complexity checker and a plugin for flake8, the Python code checker, and its homepage points at the PyPI listing rather than a separate site. The audience is engineers who already accept that machine checked thresholds catch problems review sometimes misses, and who want that threshold to be a number rather than an opinion. The origin is visible in the changes list: version 0.2 from February 2013 renamed the project to mccabe and provided the flake8.extension setuptools entry point, and the first release a few days earlier was called something else. What ships today is still a single module file, mccabe.py, at the repository root, next to setup.py, setup.cfg, tox.ini and test_mccabe.py. That small surface is both the appeal and the limit.
The graph builder walks the AST to count paths
The names visible in the standalone output reveal the internal structure. There is a McCabeChecker class whose run method is the entry point, a PathGraphingAstVisitor that subclasses an ASTVisitor and overrides methods such as visitIf and visitTryExcept, a PathGraph class with a to_dot method, and a get_code_complexity function that produces the number. The checker visits the abstract syntax tree of a function, builds a graph whose nodes are code blocks and whose edges are possible transitions, then reports the number of paths through that graph. The to_dot method suggests the graph can also be serialised for graphviz output. Complexity here is therefore derived from the structure of the code, not from a line count or a keyword heuristic. setup.py registers the plugin under the name C90 with an entry point pointing at mccabe:McCabeChecker, which is why every finding surfaces with the C901 code in flake8 output.
Running the checker on a file without flake8
You do not need flake8 to use mccabe. Because the package ships as a module, it can be invoked directly, and the project documents the call with a minimum threshold of 5 against its own source file. The output is a list of tuples, each giving a line number, a column, the qualified name of a function and its complexity score:
$ 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)Every entry has a score of 5 or more, which is what the threshold asked for. The names in the tuples are the same visitor and checker methods described above, so the output doubles as a map of the module's own structure. Note that the standalone flag is called --min while the plugin reads its value from the flake8 configuration file under the name max-complexity, an inconsistency inherited from the 0.2 release, which renamed the argument min_complexity to threshold.
Installing the checker and the flake8 plugin together
The installation section gives the three package manager commands for a normal install, an upgrade and a removal. The upgrade matters in practice, because a stale copy of mccabe silently keeps reporting the old numbering scheme:
$ pip install mccabe
$ pip install --upgrade mccabe
$ pip uninstall mccabeOnce flake8 and mccabe are both present, the plugin registers itself and shows up in the version banner. The banner in the documentation dates from an old release, but the format is what matters, since the third entry is the mccabe plugin:
$ flake8 --version
2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2)If that third item is missing, the entry point has not been picked up and no complexity violation will ever be reported. The plugin requires flake8 2 or later, and setup.py declares python_requires of 3.6 or newer, with classifiers covering Python 3.6 through 3.10.
The plugin stays silent until you pass --max-complexity
This is the detail that trips up new users. The plugin is disabled by default and only becomes active when the --max-complexity switch is given on the flake8 command line, at which point flake8 emits a warning for any function whose complexity is higher than the value supplied. The example passes 10 against a directory called coolproject and the finding lands on a specific class method:
$ flake8 --max-complexity 10 coolproject
...
coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)The reported value is 14 against a limit of 10, so the warning names both the function and its score. Silencing a finding works the same way as for other flake8 codes, with # noqa, and the project is specific about placement: put # noqa: C901 on the function definition line, since that is where the error is reported, which may be the decorator line rather than the def line. Getting that placement wrong is the usual reason a suppression does not take effect.
Inclusive thresholds and a corrected starting value
Two corrections in the 0.3 release of December 2014 shaped how scores should be read, and both are easy to get wrong when comparing against older articles about complexity tooling. The first is that the mccabe complexity starts at 1, not 2, so a straight-line function with no branches scores 1 rather than 2. The second is that the max-complexity value is inclusive: if the limit is 10 and the reported complexity is 10, the function passes. Later releases tightened the details around this machinery, with 0.5.3 reporting the actual column of the violation instead of the start of the line, 0.6.1 fixing the signature of PathGraphingAstVisitor.default to match ASTVisitor, and 0.6.0 fixing handling for missing statement types. The 0.5.1 entry set the default maximum complexity to -1 on the class itself. The documentation also states the heuristic plainly, that according to McCabe anything going beyond 10 is too complex, which is why 10 shows up in every example.
Support statements and licensing details do not fully line up
Two things to check before standardising on this package. First, Python version support: the changes list for 0.7.0, dated 2022-01-23, drops support for every Python version lower than 3.6 and adds support for 3.8, 3.9 and 3.10, while setup.py still declares python_requires of 3.6 or newer. Version 3.11 and 3.12 are not mentioned anywhere in the visible documentation, so a team running a newer interpreter has to confirm compatibility itself. Second, licensing: the repository license field is classified as a custom licence GitHub cannot categorise, pointing at the LICENSE file, yet setup.py declares the Expat license and carries a classifier for the MIT License. Those are normally the same permissive family, but the authoritative text for your use is the LICENSE file in the repository rather than the packaging metadata. The repository has no GitHub releases, so version history lives in the README changes list rather than in tagged release notes, and the last push was on 2026-10-12.
Editorial conclusion
mccabe fits Python teams that already run flake8 and want one threshold to expose sprawling functions, and it is a reasonable candidate for a pre-commit hook or a CI gate. It is the wrong tool if you want complexity metrics per class or per module, since it reports function level graphs only. Before adding it to a pipeline, check the threshold against your own codebase, because the documentation offers the McCabe figure of 10 as a starting point and the tool reports violations only above the value you pass to --max-complexity.
Frequently asked questions
How do I install mccabe and start using it with flake8?
Run pip install mccabe, and once flake8 is installed the plugin registers itself and appears in the flake8 --version banner. The plugin stays disabled until you pass the --max-complexity switch, which is what makes it report violations.
What does the C901 code mean in flake8 output?
C901 is the mccabe complexity warning produced when a function's cyclomatic complexity is higher than the value given to --max-complexity. The message names the function and its score, for example 'CoolFactory.prepare' is too complex (14).
How do I suppress a complexity warning for a single function?
Place # noqa: C901 on the function definition line, which is where the error is reported. It may be the decorator line rather than the def line, so check the reported line before adding the comment.
Can mccabe be used without flake8 installed?
Yes. The package can be run directly as a module, for example python -m mccabe --min 5 mccabe.py, which prints tuples of line, column, function name and complexity score.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pycqa-mccabe)Was this useful?