Introduction to Cyclomatic Complexity

Introduction to Cyclomatic Complexity

Software Engineering

What is Cyclomatic Complexity?

Cyclomatic complexity is a metric used to indicate the complexity of a program. It measures the number of linearly independent paths through a program's source code. In simple terms, it gauges how complicated your code is by counting the number of branching points—such as if, else, while, and for statements.

Cyclomatic Complexity was developed by Thomas J. McCabe in 1976. The complexity M is defined as:

\[ M = E - N + 2P \]

  • E = the number of edges
  • N = the number of nodes
  • P = the number of connected components

Examples

  • Low Complexity: A simple function that adds two numbers.Cyclomatic Complexity: 1
  • Medium Complexity: A function that includes a if-else statement.Cyclomatic Complexity: 2
  • High Complexity: A function with multiple control structures.Cyclomatic Complexity: 4

Why Measure Cyclomatic Complexity?

  • Maintainability: Higher complexity often results in harder-to-maintain code.
  • Testing: More complex code requires more extensive testing.
  • Readability: Simplified code is generally easier to read and understand.

Analysing Python Code with Abstract Syntax Trees (AST)

The Python ast module allows us to interact with Python code in its abstract syntax tree representation. This tree structure represents the grammatical constructs in the code and allows for a more straightforward programmatic analysis.

Implementation: Calculating Cyclomatic Complexity

Let's build a Python script to measure the cyclomatic complexity of functions and methods in a Python file.

Importing Required Modules

import ast

Define Function to Calculate Complexity

def calculate_cyclomatic_complexity(tree):
    complexity = 0
    for node in ast.walk(tree):
        if isinstance(node, (ast.If, ast.While, ast.For, ast.And, ast.Or)):
            complexity += 1
    return complexity + 1

Analyse a Python File

def analyse_file(file_path):
    with open(file_path, 'r') as f:
        tree = ast.parse(f.read())
    
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
            func_name = node.name
            complexity = calculate_cyclomatic_complexity(node)
            print(f"Function {func_name} has a Cyclomatic Complexity of {complexity}")

Run the Analysis

if __name__ == "__main__":
    analyse_file("your_file.py")

Conclusion

Calculating cyclomatic complexity can be valuable in evaluating your code's readability, maintainability, and testability. Utilising Python's ast module, we can automate this process to review any Python file for complexity. With this information, you can make more informed decisions about refactoring and quality assurance.