0
# Command Line Interface
1
2
Comprehensive CLI with four analysis commands (cc, raw, mi, hal) supporting multiple output formats, configuration files, and batch processing capabilities. Designed for integration with CI/CD pipelines, code quality tools, and automated analysis workflows.
3
4
## Capabilities
5
6
### Main CLI Program
7
8
Central program instance and configuration management for all radon commands.
9
10
```python { .api }
11
# Main program instance (mando.Program)
12
program = Program(version=radon.__version__)
13
14
class Config:
15
"""
16
Configuration object for analysis parameters.
17
18
Stores analysis settings and provides access to configuration values
19
through attribute access and keyword parameters.
20
"""
21
22
def __init__(self, **kwargs):
23
"""
24
Create configuration with keyword parameters.
25
26
Parameters:
27
- **kwargs: Configuration values as keyword arguments
28
"""
29
30
@classmethod
31
def from_function(cls, func):
32
"""
33
Create Config object from function defaults.
34
35
Extracts default parameter values from a function's signature
36
to create a configuration object.
37
38
Parameters:
39
- func: Function with default parameters
40
41
Returns:
42
Config: Configuration object with function defaults
43
"""
44
45
class FileConfig:
46
"""
47
File-based configuration reader supporting multiple formats.
48
49
Reads configuration from setup.cfg, radon.cfg, pyproject.toml,
50
and environment variables.
51
"""
52
53
def get_value(self, key, type, default):
54
"""
55
Get configuration value with type conversion.
56
57
Parameters:
58
- key (str): Configuration key name
59
- type: Target type (int, bool, str)
60
- default: Default value if key not found
61
62
Returns:
63
Converted configuration value or default
64
"""
65
66
@staticmethod
67
def file_config():
68
"""
69
Read configuration from various file sources.
70
71
Checks for configuration in:
72
- Environment variable RADONCFG
73
- radon.cfg in current directory
74
- pyproject.toml [tool.radon] section
75
- setup.cfg [radon] section
76
- ~/.radon.cfg in user home directory
77
78
Returns:
79
configparser.ConfigParser: Merged configuration
80
"""
81
82
@staticmethod
83
def toml_config():
84
"""
85
Read configuration from pyproject.toml file.
86
87
Returns:
88
dict: Configuration dictionary from TOML file
89
"""
90
```
91
92
### Analysis Commands
93
94
Core commands for different types of code analysis.
95
96
```python { .api }
97
@program.command
98
def cc(paths, min='A', max='F', show_complexity=False, average=False,
99
exclude=None, ignore=None, order='SCORE', json=False,
100
no_assert=False, show_closures=False, total_average=False,
101
xml=False, md=False, codeclimate=False, output_file=None,
102
include_ipynb=False, ipynb_cells=False):
103
"""
104
Analyze cyclomatic complexity (CC) of Python modules.
105
106
Computes McCabe's cyclomatic complexity and provides A-F rankings
107
for functions, methods, and classes.
108
109
Parameters:
110
- paths (list): Paths to Python files or directories to analyze
111
- min (str): Minimum complexity grade to display ('A'-'F')
112
- max (str): Maximum complexity grade to display ('A'-'F')
113
- show_complexity (bool): Show numeric complexity scores
114
- average (bool): Display average complexity at end
115
- exclude (str): Glob patterns for files to exclude
116
- ignore (str): Glob patterns for directories to ignore
117
- order (str): Sort order ('SCORE', 'LINES', 'ALPHA')
118
- json (bool): Output results as JSON
119
- no_assert (bool): Don't count assert statements
120
- show_closures (bool): Include nested functions/classes
121
- total_average (bool): Average unfiltered by min/max
122
- xml (bool): Output results as XML (CCM compatible)
123
- md (bool): Output results as Markdown
124
- codeclimate (bool): Output Code Climate format
125
- output_file (str): Write output to file instead of stdout
126
- include_ipynb (bool): Include Jupyter notebook files
127
- ipynb_cells (bool): Report individual notebook cells
128
"""
129
130
@program.command
131
def raw(paths, exclude=None, ignore=None, summary=False, json=False,
132
output_file=None, include_ipynb=False, ipynb_cells=False):
133
"""
134
Analyze raw code metrics (LOC, LLOC, comments, etc.).
135
136
Computes lines of code, logical lines, comments, blank lines,
137
and other basic code statistics.
138
139
Parameters:
140
- paths (list): Paths to Python files or directories to analyze
141
- exclude (str): Glob patterns for files to exclude
142
- ignore (str): Glob patterns for directories to ignore
143
- summary (bool): Display summary statistics
144
- json (bool): Output results as JSON
145
- output_file (str): Write output to file instead of stdout
146
- include_ipynb (bool): Include Jupyter notebook files
147
- ipynb_cells (bool): Report individual notebook cells
148
"""
149
150
@program.command
151
def mi(paths, min='A', max='C', multi=True, exclude=None, ignore=None,
152
show=False, json=False, sort=False, output_file=None,
153
include_ipynb=False, ipynb_cells=False):
154
"""
155
Analyze Maintainability Index of Python modules.
156
157
Computes compound maintainability metric combining Halstead volume,
158
cyclomatic complexity, and lines of code.
159
160
Parameters:
161
- paths (list): Paths to Python files or directories to analyze
162
- min (str): Minimum MI grade to display ('A', 'B', 'C')
163
- max (str): Maximum MI grade to display ('A', 'B', 'C')
164
- multi (bool): Count multi-line strings as comments
165
- exclude (str): Glob patterns for files to exclude
166
- ignore (str): Glob patterns for directories to ignore
167
- show (bool): Show actual MI numeric values
168
- json (bool): Output results as JSON
169
- sort (bool): Sort results in ascending order
170
- output_file (str): Write output to file instead of stdout
171
- include_ipynb (bool): Include Jupyter notebook files
172
- ipynb_cells (bool): Report individual notebook cells
173
"""
174
175
@program.command
176
def hal(paths, exclude=None, ignore=None, json=False, functions=False,
177
output_file=None, include_ipynb=False, ipynb_cells=False):
178
"""
179
Analyze Halstead complexity metrics of Python modules.
180
181
Computes software science metrics including volume, difficulty,
182
effort, time estimates, and bug predictions.
183
184
Parameters:
185
- paths (list): Paths to Python files or directories to analyze
186
- exclude (str): Glob patterns for files to exclude
187
- ignore (str): Glob patterns for directories to ignore
188
- json (bool): Output results as JSON
189
- functions (bool): Analyze by top-level functions instead of files
190
- output_file (str): Write output to file instead of stdout
191
- include_ipynb (bool): Include Jupyter notebook files
192
- ipynb_cells (bool): Report individual notebook cells
193
"""
194
```
195
196
### Output and Logging Functions
197
198
Functions for formatting and displaying analysis results.
199
200
```python { .api }
201
def log_result(harvester, **kwargs):
202
"""
203
Log results from a Harvester object with format selection.
204
205
Automatically selects output format based on keyword arguments
206
and delegates to appropriate formatter.
207
208
Parameters:
209
- harvester: Harvester instance with analysis results
210
- json (bool): Format as JSON
211
- xml (bool): Format as XML
212
- md (bool): Format as Markdown
213
- codeclimate (bool): Format for Code Climate
214
- stream: Output stream (default stdout)
215
- **kwargs: Additional formatting options
216
"""
217
218
def log(msg, *args, **kwargs):
219
"""
220
Log a message with formatting and indentation support.
221
222
Parameters:
223
- msg (str): Message template
224
- *args: Arguments for message formatting
225
- indent (int): Indentation level (4 spaces per level)
226
- delimiter (str): Line delimiter (default newline)
227
- noformat (bool): Skip string formatting
228
- stream: Output stream (default stdout)
229
"""
230
231
def log_list(lst, *args, **kwargs):
232
"""
233
Log a list of messages line by line.
234
235
Parameters:
236
- lst (list): List of messages to log
237
- *args, **kwargs: Passed to log() function
238
"""
239
240
def log_error(msg, *args, **kwargs):
241
"""
242
Log an error message with colored formatting.
243
244
Parameters:
245
- msg (str): Error message
246
- *args, **kwargs: Passed to log() function
247
"""
248
249
@contextmanager
250
def outstream(outfile=None):
251
"""
252
Context manager for output stream management.
253
254
Parameters:
255
- outfile (str): Output filename, or None for stdout
256
257
Yields:
258
file: Open file handle or stdout
259
"""
260
```
261
262
## Command-Line Usage Examples
263
264
### Cyclomatic Complexity Analysis
265
266
```bash
267
# Basic complexity analysis
268
radon cc src/
269
270
# Show numeric complexity scores
271
radon cc --show-complexity src/
272
273
# Filter by complexity range
274
radon cc --min B --max F src/
275
276
# Include average complexity
277
radon cc --average src/
278
279
# Sort by different criteria
280
radon cc --order LINES src/ # Sort by line number
281
radon cc --order ALPHA src/ # Sort alphabetically
282
283
# Different output formats
284
radon cc --json src/ # JSON output
285
radon cc --xml src/ # XML output (CCM compatible)
286
radon cc --md src/ # Markdown output
287
radon cc --codeclimate src/ # Code Climate format
288
289
# Save to file
290
radon cc --output-file complexity.json --json src/
291
292
# Include Jupyter notebooks
293
radon cc --include-ipynb --ipynb-cells notebooks/
294
```
295
296
### Raw Metrics Analysis
297
298
```bash
299
# Basic raw metrics
300
radon raw src/
301
302
# Show summary statistics
303
radon raw --summary src/
304
305
# JSON output for processing
306
radon raw --json src/
307
308
# Exclude test files
309
radon raw --exclude "*/tests/*" src/
310
311
# Ignore specific directories
312
radon raw --ignore "__pycache__,*.egg-info" src/
313
314
# Include Jupyter notebooks
315
radon raw --include-ipynb notebooks/
316
```
317
318
### Maintainability Index
319
320
```bash
321
# Basic MI analysis
322
radon mi src/
323
324
# Show numeric MI values
325
radon mi --show src/
326
327
# Filter by maintainability grade
328
radon mi --min B --max A src/ # Only high maintainability
329
330
# Sort results
331
radon mi --sort src/
332
333
# Don't count docstrings as comments
334
radon mi --no-multi src/
335
336
# JSON output
337
radon mi --json src/
338
```
339
340
### Halstead Metrics
341
342
```bash
343
# Basic Halstead analysis
344
radon hal src/
345
346
# Analyze by functions instead of files
347
radon hal --functions src/
348
349
# JSON output for detailed metrics
350
radon hal --json src/
351
352
# Save detailed analysis
353
radon hal --output-file halstead.json --json src/
354
```
355
356
## Configuration Files
357
358
Radon supports configuration through multiple file formats:
359
360
### setup.cfg
361
362
```ini
363
[radon]
364
exclude = */tests/*,*/migrations/*
365
ignore = __pycache__,.git
366
cc_min = B
367
cc_max = F
368
show_complexity = true
369
average = true
370
```
371
372
### pyproject.toml
373
374
```toml
375
[tool.radon]
376
exclude = "*/tests/*"
377
ignore = "__pycache__"
378
cc_min = "B"
379
show_complexity = true
380
average = true
381
```
382
383
### radon.cfg
384
385
```ini
386
[radon]
387
exclude = build/*,dist/*
388
cc_min = A
389
cc_max = F
390
show_complexity = true
391
```
392
393
## Programmatic CLI Usage
394
395
Using the CLI components from Python code:
396
397
```python
398
from radon.cli import Config, program
399
from radon.cli.harvest import CCHarvester
400
from radon.cli.tools import iter_filenames
401
import sys
402
403
# Create configuration
404
config = Config(
405
min='B',
406
max='F',
407
show_complexity=True,
408
exclude='*/tests/*',
409
order='SCORE'
410
)
411
412
# Use file discovery
413
paths = ['src/']
414
filenames = list(iter_filenames(paths, exclude=config.exclude))
415
print(f"Found {len(filenames)} Python files")
416
417
# Run complexity analysis
418
harvester = CCHarvester(paths, config)
419
harvester.run()
420
421
# Get results programmatically
422
results = harvester.results
423
for filename, analysis in results.items():
424
print(f"{filename}: {len(analysis)} blocks analyzed")
425
426
# Format as JSON
427
json_output = harvester.as_json()
428
print("JSON output generated")
429
430
# Format for terminal
431
for msg, args, kwargs in harvester.to_terminal():
432
if not kwargs.get('error', False):
433
print(msg.format(*args) if args else msg)
434
```
435
436
### Custom Harvester Usage
437
438
```python
439
from radon.cli.harvest import RawHarvester, MIHarvester, HCHarvester
440
from radon.cli import Config
441
442
config = Config(summary=True, exclude='*/tests/*')
443
444
# Raw metrics harvester
445
raw_harvester = RawHarvester(['src/'], config)
446
raw_harvester.run()
447
448
print("Raw metrics summary:")
449
raw_results = raw_harvester.results
450
total_loc = sum(metrics.loc for metrics in raw_results.values())
451
total_sloc = sum(metrics.sloc for metrics in raw_results.values())
452
print(f"Total LOC: {total_loc}")
453
print(f"Total SLOC: {total_sloc}")
454
455
# Maintainability index harvester
456
mi_config = Config(show=True, sort=True)
457
mi_harvester = MIHarvester(['src/'], mi_config)
458
mi_harvester.run()
459
460
# Halstead harvester
461
hal_config = Config(by_function=True)
462
hal_harvester = HCHarvester(['src/'], hal_config)
463
hal_harvester.run()
464
```
465
466
## Integration Examples
467
468
### CI/CD Pipeline Integration
469
470
```bash
471
#!/bin/bash
472
# ci-quality-check.sh
473
474
# Check complexity - fail if any function/class has F grade
475
if radon cc --min F --json src/ | grep -q '"rank": "F"'; then
476
echo "Error: Code contains F-grade complexity"
477
exit 1
478
fi
479
480
# Check maintainability - fail if average MI is too low
481
MI_SCORE=$(radon mi --json src/ | jq '.[] | .mi' | awk '{sum+=$1; count++} END {print sum/count}')
482
if (( $(echo "$MI_SCORE < 20" | bc -l) )); then
483
echo "Error: Maintainability index too low: $MI_SCORE"
484
exit 1
485
fi
486
487
echo "Code quality checks passed"
488
```
489
490
### Code Quality Reports
491
492
```python
493
import json
494
import subprocess
495
from pathlib import Path
496
497
def generate_quality_report(src_path):
498
"""Generate comprehensive code quality report."""
499
500
# Run all radon analyses
501
analyses = {}
502
503
# Complexity analysis
504
cc_result = subprocess.run(
505
['radon', 'cc', '--json', src_path],
506
capture_output=True, text=True
507
)
508
analyses['complexity'] = json.loads(cc_result.stdout)
509
510
# Raw metrics
511
raw_result = subprocess.run(
512
['radon', 'raw', '--json', src_path],
513
capture_output=True, text=True
514
)
515
analyses['raw'] = json.loads(raw_result.stdout)
516
517
# Maintainability index
518
mi_result = subprocess.run(
519
['radon', 'mi', '--json', src_path],
520
capture_output=True, text=True
521
)
522
analyses['maintainability'] = json.loads(mi_result.stdout)
523
524
# Generate summary report
525
report = {
526
'total_files': len(analyses['raw']),
527
'total_loc': sum(m['loc'] for m in analyses['raw'].values()),
528
'total_sloc': sum(m['sloc'] for m in analyses['raw'].values()),
529
'avg_complexity': calculate_avg_complexity(analyses['complexity']),
530
'avg_maintainability': calculate_avg_mi(analyses['maintainability']),
531
'quality_grade': determine_quality_grade(analyses)
532
}
533
534
return report
535
536
def calculate_avg_complexity(cc_data):
537
"""Calculate average complexity across all functions."""
538
total_complexity = 0
539
total_functions = 0
540
541
for file_data in cc_data.values():
542
for block in file_data:
543
total_complexity += block['complexity']
544
total_functions += 1
545
546
return total_complexity / total_functions if total_functions > 0 else 0
547
548
def calculate_avg_mi(mi_data):
549
"""Calculate average maintainability index."""
550
scores = [data['mi'] for data in mi_data.values()]
551
return sum(scores) / len(scores) if scores else 0
552
553
def determine_quality_grade(analyses):
554
"""Determine overall quality grade."""
555
avg_complexity = calculate_avg_complexity(analyses['complexity'])
556
avg_mi = calculate_avg_mi(analyses['maintainability'])
557
558
if avg_complexity <= 5 and avg_mi >= 20:
559
return 'A'
560
elif avg_complexity <= 10 and avg_mi >= 15:
561
return 'B'
562
elif avg_complexity <= 20 and avg_mi >= 10:
563
return 'C'
564
else:
565
return 'D'
566
567
# Usage
568
# report = generate_quality_report('src/')
569
# print(f"Quality Grade: {report['quality_grade']}")
570
```
571
572
## Error Handling and Debugging
573
574
The CLI handles various error conditions gracefully:
575
576
- **File not found**: Clear error messages for missing files/directories
577
- **Syntax errors**: Reports files with parsing errors and continues
578
- **Permission errors**: Handles files that cannot be read
579
- **Configuration errors**: Validates configuration values and provides defaults
580
- **Output errors**: Handles file write permissions and disk space issues
581
582
### Debug Mode
583
584
```bash
585
# Enable verbose output for troubleshooting
586
RADON_DEBUG=1 radon cc src/
587
588
# Check configuration loading
589
radon cc --help # Shows all available options and defaults
590
```
591
592
## Performance Considerations
593
594
- **Large codebases**: Use `--exclude` and `--ignore` to skip unnecessary files
595
- **Parallel processing**: Radon processes files sequentially; use shell scripting for parallelization
596
- **Memory usage**: JSON output can be memory-intensive for very large projects
597
- **Network filesystems**: Local analysis is faster than network-mounted directories
598
599
The CLI provides a comprehensive interface for all radon analysis capabilities while maintaining compatibility with automated workflows and integration tools.