CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-radon

Code Metrics in Python - comprehensive tool for computing various software metrics

Pending
Overview
Eval results
Files

complexity.mddocs/

Cyclomatic Complexity Analysis

McCabe's cyclomatic complexity analysis for measuring code complexity and maintainability. Provides complexity scoring with A-F ranking system, detailed block-level analysis for functions, methods, and classes, and utilities for processing and sorting results.

Capabilities

Code Analysis Functions

Main functions for analyzing cyclomatic complexity from source code or AST nodes.

def cc_visit(code, **kwargs):
    """
    Visit the given code with ComplexityVisitor.
    
    Parameters:
    - code (str): Python source code to analyze
    - **kwargs: Keyword arguments passed to ComplexityVisitor
        - to_method (bool): Whether to treat top-level functions as methods
        - classname (str): Class name for method analysis
        - off (bool): Whether to include decorator complexity
        - no_assert (bool): Whether to exclude assert statements from complexity
    
    Returns:
    list: List of Function and Class objects representing code blocks
    """

def cc_visit_ast(ast_node, **kwargs):
    """
    Visit the AST node with ComplexityVisitor.
    
    Parameters:
    - ast_node: Python AST node to analyze
    - **kwargs: Keyword arguments passed to ComplexityVisitor
    
    Returns:
    list: List of Function and Class objects representing code blocks
    """

Complexity Ranking and Utilities

Functions for ranking complexity scores and processing analysis results.

def cc_rank(cc):
    """
    Rank the complexity score from A to F.
    
    Ranking system:
    - A (1-5): Low risk - simple block
    - B (6-10): Low risk - well structured and stable block  
    - C (11-20): Moderate risk - slightly complex block
    - D (21-30): More than moderate risk - more complex block
    - E (31-40): High risk - complex block, alarming
    - F (41+): Very high risk - error-prone, unstable block
    
    Parameters:
    - cc (int): Complexity score (must be non-negative)
    
    Returns:
    str: Single letter grade (A-F)
    
    Raises:
    ValueError: If complexity is negative
    """

def average_complexity(blocks):
    """
    Compute the average cyclomatic complexity from blocks.
    
    Parameters:
    - blocks (list): List of Function or Class objects
    
    Returns:
    float: Average complexity score, or 0 if blocks is empty
    """

def sorted_results(blocks, order=SCORE):
    """
    Sort blocks by complexity with specified ordering.
    
    Parameters:
    - blocks (list): List of Function or Class objects
    - order (function): Sorting function, one of:
        - SCORE: Sort by complexity score (descending) - default
        - LINES: Sort by line number (ascending)
        - ALPHA: Sort alphabetically by name (ascending)
    
    Returns:
    list: Sorted list of blocks
    """

def add_inner_blocks(blocks):
    """
    Process blocks by adding closures and inner classes as top-level blocks.
    Flattens nested functions and inner classes into the main block list.
    
    Parameters:
    - blocks (list): List of Function or Class objects
    
    Returns:
    list: Expanded list with nested blocks promoted to top-level
    """

Sorting Constants

Predefined sorting functions for use with sorted_results().

# Sort by complexity score (descending)
SCORE = lambda block: -GET_COMPLEXITY(block)

# Sort by line number (ascending)  
LINES = lambda block: block.lineno

# Sort alphabetically by name (ascending)
ALPHA = lambda block: block.name

Usage Examples

Basic Complexity Analysis

from radon.complexity import cc_visit, cc_rank

code = '''
def simple_function():
    return True

def complex_function(x, y, z):
    if x > 0:
        if y > 0:
            if z > 0:
                return x + y + z
            else:
                return x + y
        else:
            return x
    else:
        return 0
'''

# Analyze complexity
blocks = cc_visit(code)

for block in blocks:
    rank = cc_rank(block.complexity)
    print(f"{block.name}: {block.complexity} ({rank})")
# Output:
# simple_function: 1 (A)
# complex_function: 4 (A)

Processing and Sorting Results

from radon.complexity import cc_visit, sorted_results, average_complexity, SCORE, LINES, ALPHA

code = '''
class Calculator:
    def add(self, a, b):
        return a + b
    
    def complex_divide(self, a, b):
        if b == 0:
            raise ValueError("Division by zero")
        elif isinstance(a, str) or isinstance(b, str):
            raise TypeError("String division not supported")
        else:
            return a / b

def utility_function():
    pass
'''

blocks = cc_visit(code)

# Sort by complexity (default)
by_complexity = sorted_results(blocks, SCORE)
print("By complexity:")
for block in by_complexity:
    print(f"  {block.name}: {block.complexity}")

# Sort by line number
by_lines = sorted_results(blocks, LINES)
print("By line number:")
for block in by_lines:
    print(f"  Line {block.lineno}: {block.name}")

# Sort alphabetically
by_name = sorted_results(blocks, ALPHA)
print("By name:")
for block in by_name:
    print(f"  {block.name}: {block.complexity}")

# Calculate average complexity
avg = average_complexity(blocks)
print(f"Average complexity: {avg:.2f}")

Analyzing Methods vs Functions

from radon.complexity import cc_visit

# Analyze as standalone functions
blocks_as_functions = cc_visit(class_code, to_method=False)

# Analyze as methods within a class context
blocks_as_methods = cc_visit(class_code, to_method=True, classname="MyClass")

for block in blocks_as_methods:
    if block.is_method:
        print(f"Method {block.fullname}: {block.complexity}")
    else:
        print(f"Function {block.name}: {block.complexity}")

Handling Nested Functions and Classes

from radon.complexity import cc_visit, add_inner_blocks

code_with_nested = '''
class OuterClass:
    def method(self):
        def inner_function():
            return True
        return inner_function()
    
    class InnerClass:
        def inner_method(self):
            pass
'''

# Get blocks including nested structures
blocks = cc_visit(code_with_nested)

# Flatten nested blocks to top level
all_blocks = add_inner_blocks(blocks)

print("All blocks (including nested):")
for block in all_blocks:
    print(f"  {block.name}: {block.complexity}")

Error Handling

The complexity analysis functions handle various error conditions:

  • Invalid complexity scores: cc_rank() raises ValueError for negative complexity values
  • Empty block lists: average_complexity() returns 0 for empty lists
  • Malformed code: AST parsing errors are propagated from underlying Python ast module
  • Invalid sorting functions: TypeError if order parameter is not callable

Integration with CLI

The complexity module integrates with radon's command-line interface:

# Command-line equivalent of cc_visit()
radon cc path/to/code.py

# With ranking and complexity display
radon cc --show-complexity --min A --max F path/to/code.py

# JSON output for programmatic processing
radon cc --json path/to/code.py

Install with Tessl CLI

npx tessl i tessl/pypi-radon

docs

cli.md

complexity.md

halstead.md

index.md

maintainability.md

raw-metrics.md

visitors.md

tile.json