Code Metrics in Python - comprehensive tool for computing various software metrics
—
Analysis of basic code metrics including lines of code (LOC), logical lines of code (LLOC), source lines of code (SLOC), comments, blank lines, and multi-line strings. Provides comprehensive code statistics for understanding codebase structure and composition.
Primary function for analyzing raw code metrics from source code.
def analyze(source):
"""
Analyze raw metrics from Python source code.
Computes comprehensive code statistics including:
- Total lines of code (LOC)
- Logical lines of code (LLOC) - executable statements
- Source lines of code (SLOC) - non-blank, non-comment lines
- Comment lines
- Multi-line strings (typically docstrings)
- Blank lines
- Single-line comments
Parameters:
- source (str): Python source code to analyze
Returns:
Module: Named tuple with fields (loc, lloc, sloc, comments, multi, blank, single_comments)
"""Low-level utilities for processing Python tokens during analysis.
def _generate(code):
"""
Pass code into tokenize.generate_tokens and convert to list.
Parameters:
- code (str): Python source code
Returns:
list: List of token tuples from tokenize.generate_tokens
"""
def _fewer_tokens(tokens, remove):
"""
Process tokenize output removing specified token types.
Parameters:
- tokens (list): List of token tuples
- remove (set): Set of token types to remove
Yields:
tuple: Token tuples not in remove set
"""
def _find(tokens, token, value):
"""
Find position of last token with specified (token, value) pair.
Parameters:
- tokens (list): List of token tuples
- token (int): Token type to find
- value (str): Token value to find
Returns:
int: Position of rightmost matching token
Raises:
ValueError: If (token, value) pair not found
"""
def _logical(tokens):
"""
Find number of logical lines from token list.
Parameters:
- tokens (list): List of token tuples
Returns:
int: Number of logical lines (executable statements)
"""Constants for token types used in analysis, re-exported from Python's tokenize module.
# Token type constants
OP = tokenize.OP # Operators
COMMENT = tokenize.COMMENT # Comment tokens
NL = tokenize.NL # Newline tokens
NEWLINE = tokenize.NEWLINE # Statement-ending newlines
EM = tokenize.ENDMARKER # End of file marker
# Helper for extracting token numbers
TOKEN_NUMBER = operator.itemgetter(0) # Extract token type from token tupleNamed tuple containing all raw metrics results.
Module = namedtuple('Module', [
'loc', # Lines of Code - total lines in the file
'lloc', # Logical Lines of Code - executable statements
'sloc', # Source Lines of Code - non-blank, non-comment lines
'comments', # Comment lines (single and multi-line comments)
'multi', # Multi-line strings (typically docstrings)
'blank', # Blank lines (whitespace-only or empty lines)
'single_comments' # Single-line comments or docstrings
])from radon.raw import analyze
code = '''
"""
This is a module docstring.
It spans multiple lines.
"""
def calculate_sum(a, b):
"""Calculate the sum of two numbers.""" # Single-line docstring
# This is a comment
result = a + b # Another comment
return result
# Standalone comment
def empty_function():
pass
'''
# Analyze the code
metrics = analyze(code)
print(f"Total lines (LOC): {metrics.loc}")
print(f"Logical lines (LLOC): {metrics.lloc}")
print(f"Source lines (SLOC): {metrics.sloc}")
print(f"Comment lines: {metrics.comments}")
print(f"Multi-line strings: {metrics.multi}")
print(f"Blank lines: {metrics.blank}")
print(f"Single comments: {metrics.single_comments}")
# Output:
# Total lines (LOC): 15
# Logical lines (LLOC): 4
# Source lines (SLOC): 8
# Comment lines: 2
# Multi-line strings: 1
# Blank lines: 3
# Single comments: 1from radon.raw import analyze
# Analyze well-documented code
documented_code = '''
"""
Module for mathematical operations.
Provides basic arithmetic functions.
"""
def add(a, b):
"""
Add two numbers together.
Args:
a: First number
b: Second number
Returns:
Sum of a and b
"""
return a + b
'''
# Analyze poorly documented code
undocumented_code = '''
def add(a,b):
return a+b
def sub(a,b):
return a-b
def mul(a,b):
return a*b
'''
doc_metrics = analyze(documented_code)
undoc_metrics = analyze(undocumented_code)
print("Well-documented code:")
print(f" SLOC: {doc_metrics.sloc}, Comments: {doc_metrics.comments}")
print(f" Comment ratio: {doc_metrics.comments / doc_metrics.sloc:.2%}")
print("Poorly documented code:")
print(f" SLOC: {undoc_metrics.sloc}, Comments: {undoc_metrics.comments}")
print(f" Comment ratio: {undoc_metrics.comments / undoc_metrics.sloc:.2%}")from radon.raw import analyze
import os
def analyze_directory(path):
"""Analyze raw metrics for all Python files in directory."""
total_metrics = {
'loc': 0, 'lloc': 0, 'sloc': 0,
'comments': 0, 'multi': 0, 'blank': 0, 'single_comments': 0
}
for filename in os.listdir(path):
if filename.endswith('.py'):
with open(os.path.join(path, filename)) as f:
code = f.read()
metrics = analyze(code)
# Accumulate metrics
for field in total_metrics:
total_metrics[field] += getattr(metrics, field)
print(f"{filename}: {metrics.sloc} SLOC, {metrics.comments} comments")
print(f"Total: {total_metrics['sloc']} SLOC, {total_metrics['comments']} comments")
return total_metrics
# Usage
# total = analyze_directory('./src')from radon.raw import _generate, _fewer_tokens, COMMENT, NL
code = '''
def example(): # Comment
x = 1
# Another comment
return x
'''
# Get all tokens
tokens = _generate(code)
print(f"Total tokens: {len(tokens)}")
# Filter out comments and newlines
filtered = list(_fewer_tokens(tokens, {COMMENT, NL}))
print(f"Tokens without comments/newlines: {len(filtered)}")
# Examine token types
for i, token in enumerate(tokens[:10]): # First 10 tokens
token_type, value, start, end, line = token
print(f"Token {i}: type={token_type}, value='{value}', line={start[0]}")Raw metrics integrate with other radon analysis capabilities:
from radon.raw import analyze
from radon.metrics import mi_compute
from radon.complexity import cc_visit, average_complexity
code = '''
def complex_function(a, b, c):
if a > 0:
if b > 0:
return a + b + c
else:
return a + c
else:
return c
'''
# Get raw metrics
raw = analyze(code)
# Get complexity
blocks = cc_visit(code)
avg_complexity = average_complexity(blocks)
# Calculate maintainability index (simplified)
# Note: This is a simplified example - actual MI calculation requires Halstead volume
print(f"SLOC: {raw.sloc}")
print(f"Comments: {raw.comments}")
print(f"Average Complexity: {avg_complexity}")The raw metrics module handles various edge cases:
_find() raises ValueError for missing tokensThe raw metrics module integrates with radon's command-line interface:
# Command-line equivalent of analyze()
radon raw path/to/code.py
# Summary output
radon raw --summary path/to/code.py
# JSON output for programmatic processing
radon raw --json path/to/code.pyInstall with Tessl CLI
npx tessl i tessl/pypi-radon