Introduction to Cyclomatic Complexity
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.