Install dependencies

pip install cognitive_complexity astunparse tabulate

Requirement already satisfied: cognitive_complexity in /usr/local/lib/python3.7/dist-packages (1.2.0)
Requirement already satisfied: astunparse in /usr/local/lib/python3.7/dist-packages (1.6.3)
Requirement already satisfied: tabulate in /usr/local/lib/python3.7/dist-packages (0.8.9)
Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (from cognitive_complexity) (57.0.0)
Requirement already satisfied: six<2.0,>=1.6.1 in /usr/local/lib/python3.7/dist-packages (from astunparse) (1.15.0)
Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.7/dist-packages (from astunparse) (0.36.2)

Import and define utils

import ast
import astunparse
from inspect import getsource
from tabulate import tabulate
from cognitive_complexity.api import get_cognitive_complexity_for_node
from cognitive_complexity.utils.ast import has_recursive_calls, is_decorator, process_child_nodes, process_node_itself

def get_cognitive_complexity(func):
    func = func if isinstance(func, str) else getsource(func)
    funcdef = ast.parse(func).body[0]
    if is_decorator(funcdef):
        return get_cognitive_complexity(funcdef.body[0])

    details = []
    complexity = 0
    for node in funcdef.body:
        node_complexity = get_cognitive_complexity_for_node(node)
        complexity += node_complexity
        node_code = astunparse.unparse(node)
        if f"{funcdef.name}(" in node_code: # +1 for recursion
            node_complexity += 1
            complexity += 1
        details.append([node_complexity, node_code])
    details.append([complexity, "Total"])
    return complexity, details

Introduction

Formulated in a Fortran environment in 1976, Cyclomatic Complexity has long been the standard for measuring the complexity of a method’s control flow. It was originally intended β€œto identify software modules that will be difficult to test or maintain”, but while it accurately calculates the minimum number of test cases required to fully cover a method, it is not a satisfactory measure of understandability and it also doesn’t include modern language structures like try/catch, and lambdas.

-- Cognitive Complexity:A new way of measuring understandability, white paper by G. Ann Campbell

Basic criteria and methodology

As a remedy for these problems, Cognitive Complexity has been formulated to address modern language structures, and to produce values that are meaningful at the class and application levels. A Cognitive Complexity score is assessed according to three basic rules:

  1. Ignore structures that allow multiple statements to be readably shorthanded into one
  2. Increment (add one) for each break in the linear flow of the code
  3. Increment when flow-breaking structures are nested Additionally, a complexity score is made up of four different types of increments:

    A. Nesting - assessed for nesting control flow structures inside each other

    B. Structural - assessed on control flow structures that are subject to a nesting increment, and that increase the nesting count

    C. Fundamental - assessed on statements not subject to a nesting increment

    D. Hybrid - assessed on control flow structures that are not subject to a nesting increment, but which do increase the nesting count

-- Cognitive Complexity: A new way of measuring understandability, white paper by G. Ann Campbell

def f(n):
    if n > 10:
        return True
    if n < 5:
        return 20
    else:
        return 2
    return f(n)

total, details = get_cognitive_complexity(f)
print(tabulate(details, headers=["Complexity", "Node"], tablefmt="fancy_grid"))
╒══════════════╀═════════════════╕
β”‚   Complexity β”‚ Node            β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════════║
β”‚            1 β”‚ if (n > 10):    β”‚
β”‚              β”‚     return True β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚            2 β”‚ if (n < 5):     β”‚
β”‚              β”‚     return 20   β”‚
β”‚              β”‚ else:           β”‚
β”‚              β”‚     return 2    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚            1 β”‚ return f(n)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚            4 β”‚ Total           β”‚
β•˜β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•›