0
# Coverage Analysis
1
2
Core functionality for analyzing docstring coverage in Python codebases. The coverage analysis engine traverses Python Abstract Syntax Trees (AST) to identify missing docstrings across modules, classes, functions, and methods.
3
4
## Capabilities
5
6
### Main Coverage Class
7
8
The primary class for running docstring coverage analysis on Python files and directories.
9
10
```python { .api }
11
class InterrogateCoverage:
12
"""Main class for analyzing docstring coverage."""
13
14
# Class constants
15
COMMON_EXCLUDE: List[str] = [".tox", ".venv", "venv", ".git", ".hg"]
16
VALID_EXT: List[str] = [".py", ".pyi"]
17
18
def __init__(self, paths, conf=None, excluded=None, extensions=None):
19
"""
20
Initialize coverage analyzer.
21
22
Args:
23
paths: List of file/directory paths to analyze
24
conf: Configuration object with analysis options (optional)
25
excluded: Tuple of files and directories to exclude (optional)
26
extensions: Set of file extensions to analyze (optional)
27
"""
28
29
def get_filenames_from_paths(self) -> List[str]:
30
"""
31
Get all Python filenames from configured paths.
32
33
Returns:
34
List[str]: Python file paths to analyze
35
"""
36
37
def get_coverage(self) -> InterrogateResults:
38
"""
39
Analyze docstring coverage for configured paths.
40
41
Returns:
42
InterrogateResults: Aggregated coverage results
43
"""
44
45
def print_results(self, results: InterrogateResults, output_file: Optional[str] = None) -> None:
46
"""
47
Print formatted coverage results.
48
49
Args:
50
results: Coverage analysis results
51
output_file: Optional file path for output (defaults to stdout)
52
"""
53
```
54
55
### Results Classes
56
57
Data structures for storing and managing coverage analysis results.
58
59
```python { .api }
60
class BaseInterrogateResult:
61
"""Base class for coverage results."""
62
63
total: int # Total number of items analyzed
64
covered: int # Number of items with docstrings
65
missing: int # Number of items missing docstrings
66
perc_covered: float # Percentage coverage (0-100)
67
68
class InterrogateFileResult(BaseInterrogateResult):
69
"""Coverage results for a single file."""
70
71
filename: str # Path to analyzed file
72
ignore_module: bool # Whether module-level docstring was ignored
73
nodes: List[CovNode] # List of analyzed AST nodes
74
75
def combine(self) -> None:
76
"""
77
Tally results from each AST node visited.
78
79
Updates the total, covered, and missing counts based on nodes.
80
"""
81
82
class InterrogateResults(BaseInterrogateResult):
83
"""Aggregated coverage results for all analyzed files."""
84
85
ret_code: int # Exit code based on coverage
86
file_results: Dict[str, InterrogateFileResult] # Per-file results
87
88
def combine(self) -> None:
89
"""
90
Tally results from each file.
91
92
Updates the total, covered, and missing counts from all file results.
93
"""
94
```
95
96
### AST Coverage Nodes
97
98
Data structures representing coverage information for individual code elements.
99
100
```python { .api }
101
class CovNode:
102
"""Coverage information for an AST node."""
103
104
name: str # Name of the code element
105
path: str # File path containing the element
106
level: int # Nesting level (0 = module, 1 = class/function, etc.)
107
lineno: int # Line number in source file
108
covered: bool # Whether element has docstring
109
node_type: str # Type of AST node ("module", "class", "function", "method")
110
is_nested_func: bool # Whether function is nested inside another function
111
is_nested_cls: bool # Whether class is nested inside another class
112
parent: Optional[CovNode] # Parent node (for nested elements)
113
```
114
115
### AST Visitor
116
117
The visitor class that traverses Python AST to collect docstring coverage information.
118
119
```python { .api }
120
class CoverageVisitor(ast.NodeVisitor):
121
"""AST visitor for collecting docstring coverage data."""
122
123
def __init__(self, filename: str, config: InterrogateConfig):
124
"""
125
Initialize AST visitor.
126
127
Args:
128
filename: Path to file being analyzed
129
config: Configuration with analysis options
130
"""
131
132
def visit_Module(self, node: ast.Module) -> None:
133
"""Visit module-level node."""
134
135
def visit_ClassDef(self, node: ast.ClassDef) -> None:
136
"""Visit class definition node."""
137
138
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
139
"""Visit function definition node."""
140
141
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
142
"""Visit async function definition node."""
143
```
144
145
## Usage Examples
146
147
### Basic Coverage Analysis
148
149
```python
150
from interrogate.coverage import InterrogateCoverage
151
from interrogate.config import InterrogateConfig
152
153
# Configure analysis
154
config = InterrogateConfig(
155
ignore_init_method=True,
156
ignore_private=True,
157
fail_under=80.0
158
)
159
160
# Run analysis
161
coverage = InterrogateCoverage(["src/myproject"], config)
162
results = coverage.get_coverage()
163
164
# Check results
165
print(f"Coverage: {results.perc_covered:.1f}%")
166
print(f"Files analyzed: {len(results.file_results)}")
167
print(f"Missing docstrings: {results.missing}")
168
169
# Print detailed results
170
coverage.print_results(results)
171
```
172
173
### Programmatic Result Processing
174
175
```python
176
from interrogate.coverage import InterrogateCoverage
177
from interrogate.config import InterrogateConfig
178
179
config = InterrogateConfig()
180
coverage = InterrogateCoverage(["src/"], config)
181
results = coverage.get_coverage()
182
183
# Process individual file results
184
for filename, file_result in results.file_results.items():
185
print(f"{filename}: {file_result.perc_covered:.1f}% coverage")
186
187
# Examine individual nodes
188
for node in file_result.nodes:
189
if not node.covered:
190
print(f" Missing docstring: {node.name} ({node.node_type}) at line {node.lineno}")
191
192
# Check if coverage meets threshold
193
if results.ret_code != 0:
194
print(f"Coverage {results.perc_covered:.1f}% below required threshold")
195
```
196
197
### Custom File Filtering
198
199
```python
200
from interrogate.coverage import InterrogateCoverage
201
from interrogate.config import InterrogateConfig
202
203
# Create coverage analyzer
204
config = InterrogateConfig()
205
coverage = InterrogateCoverage(["src/"], config)
206
207
# Get all Python files that would be analyzed
208
python_files = list(coverage.get_filenames_from_paths())
209
print(f"Files to analyze: {len(python_files)}")
210
211
for filepath in python_files:
212
print(f" {filepath}")
213
```