python-code-quality 0.2.3__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/PKG-INFO +136 -112
  2. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/README.md +133 -109
  3. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/pyproject.toml +19 -16
  4. python_code_quality-0.3.1/pyproject.toml.orig +71 -0
  5. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/api.py +9 -3
  6. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/cli.py +12 -14
  7. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/config/config.toml +13 -0
  8. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/context_hash.py +58 -15
  9. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/execution_engine.py +172 -61
  10. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/localtypes.py +4 -2
  11. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/common.py +2 -7
  12. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/coverageparser.py +7 -18
  13. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/interrogateparser.py +31 -24
  14. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/pytestparser.py +1 -1
  15. python_code_quality-0.3.1/src/py_cq/parsers/ruffformatparser.py +62 -0
  16. python_code_quality-0.3.1/src/py_cq/source_file.py +199 -0
  17. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/table_formatter.py +1 -1
  18. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/tool_registry.py +1 -0
  19. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/__init__.py +0 -0
  20. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/config/__init__.py +0 -0
  21. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/language_detector.py +0 -0
  22. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/llm_formatter.py +0 -0
  23. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/main.py +0 -0
  24. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/metric_aggregator.py +0 -0
  25. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/__init__.py +0 -0
  26. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/banditparser.py +0 -0
  27. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/compileparser.py +0 -0
  28. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/complexityparser.py +0 -0
  29. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/exitcodeparser.py +0 -0
  30. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/halsteadparser.py +0 -0
  31. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/linecountparser.py +0 -0
  32. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/maintainabilityparser.py +0 -0
  33. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/regexcountparser.py +0 -0
  34. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/ruffparser.py +0 -0
  35. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/typarser.py +0 -0
  36. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/parsers/vultureparser.py +0 -0
  37. {python_code_quality-0.2.3 → python_code_quality-0.3.1}/src/py_cq/py.typed +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-code-quality
3
- Version: 0.2.3
4
- Summary: Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens.
3
+ Version: 0.3.1
4
+ Summary: Python Code Quality Analysis Tool - feed the results from 12 CQ tools straight into an LLM. Minimal tokens.
5
5
  Author: Chris Kilner
6
6
  Author-email: Chris Kilner <chris@rhiza.fr>
7
7
  License-Expression: MIT
@@ -17,7 +17,7 @@ Requires-Dist: pytest-cov>=6.1.1
17
17
  Requires-Dist: pytest-json-report>=1.5.0
18
18
  Requires-Dist: radon>=6.0.1
19
19
  Requires-Dist: rich>=14.0.0
20
- Requires-Dist: ruff>=0.14.1
20
+ Requires-Dist: ruff>=0.14.1,<0.17
21
21
  Requires-Dist: tomlkit>=0.13.0
22
22
  Requires-Dist: ty>=0.0.17
23
23
  Requires-Dist: typer>=0.16.0
@@ -35,7 +35,7 @@ Description-Content-Type: text/markdown
35
35
  [![Python versions](https://img.shields.io/pypi/pyversions/python-code-quality?)](https://pypi.org/project/python-code-quality/)
36
36
  [![License](https://img.shields.io/github/license/rhiza-fr/py-cq)](LICENSE)
37
37
 
38
- Run 11+ code quality tools, aggregate results into one score, and surface the single most critical defect as a focused markdown prompt — ready to pipe to any LLM.
38
+ Run 12 code quality tools, aggregate results into one score, and surface the single most critical defect as a focused markdown prompt — ready to pipe to any LLM.
39
39
 
40
40
  This can dramatically reduce the amount of noise for LLMs (and humans) and remove the need for them to know about these tools.
41
41
 
@@ -65,6 +65,8 @@ uv tool install .
65
65
 
66
66
  These tools are run in **parallel** except:
67
67
  When running '-o llm', we run sequentially and exit early at the first error.
68
+ `coverage` is a special case: it starts alongside `pytest` and is cancelled if
69
+ `pytest` fails, scoring `0%` without a second test run.
68
70
 
69
71
  | Order | Tool | Measures |
70
72
  |----------|------|----------|
@@ -74,11 +76,12 @@ When running '-o llm', we run sequentially and exit early at the first error.
74
76
  | 4 | bandit | Security vulnerabilities |
75
77
  | 5 | pytest | Test pass rate |
76
78
  | 6 | coverage | Test coverage |
77
- | 7 | radon cc | Cyclomatic complexity |
78
- | 8 | radon mi | Maintainability index |
79
- | 9 | radon hal | Halstead volume / bug estimate |
80
- | 10 | vulture | Dead code |
81
- | 11 | interrogate | Docstring coverage |
79
+ | 7 | vulture | Dead code |
80
+ | 8 | interrogate | Docstring coverage |
81
+ | 9 | radon cc | Cyclomatic complexity |
82
+ | 10 | radon mi | Maintainability index |
83
+ | 11 | radon hal | Halstead volume / bug estimate |
84
+ | 12 | ruff format | Formatting (fraction of files already formatted) |
82
85
 
83
86
  Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
84
87
 
@@ -99,7 +102,7 @@ cq check . --exclude demo # Exclude paths from all tools
99
102
  cq check . --workers 1 # Run sequentially if you like things slow
100
103
  cq check . --clear-cache # Clear cached results before running (rarely needed)
101
104
  cq check . -o llm --hint # Append "run cq again to verify" (for human workflows)
102
- cq config path/to/project/ # Show effective tool configuration
105
+ cq config --path path/to/project/ # Show effective tool configuration
103
106
  cq is-fixed <fingerprint> # Check whether a specific issue has been resolved
104
107
  ```
105
108
 
@@ -248,14 +251,15 @@ Then invoke it with `/cq-fix` in Claude Code. The `$(...)` embeds the live `cq`
248
251
  │ bandit │ 0.56s │ security │ 1.000 │ OK │
249
252
  │ pytest │ 0.91s │ tests │ 1.000 │ OK │
250
253
  │ coverage │ 1.26s │ coverage │ 0.910 │ OK │
251
- │ radon cc │ 0.32s │ simplicity │ 0.982 │ OK │
252
- │ radon mi │ 0.38s │ maintainability │ 0.869 │ OK │
253
- │ radon hal │ 0.30s │ file_bug_free │ 0.928 │ OK │
254
- │ radon hal │ │ file_smallness │ 0.851 │ OK │
255
- │ radon hal │ │ functions_bug_free │ 0.913 │ OK │
256
- │ radon hal │ │ functions_smallness │ 0.724 │ OK │
257
254
  │ vulture │ 0.32s │ dead_code │ 1.000 │ OK │
258
255
  │ interrogate │ 0.36s │ doc_coverage │ 1.000 │ OK │
256
+ │ radon-cc │ 0.32s │ simplicity │ 0.982 │ OK │
257
+ │ radon-mi │ 0.38s │ maintainability │ 0.869 │ OK │
258
+ │ radon-hal │ 0.30s │ file_bug_free │ 0.928 │ OK │
259
+ │ radon-hal │ │ file_smallness │ 0.851 │ OK │
260
+ │ radon-hal │ │ functions_bug_free │ 0.913 │ OK │
261
+ │ radon-hal │ │ functions_smallness │ 0.724 │ OK │
262
+ │ ruff-format │ 0.12s │ formatted │ 1.000 │ OK │
259
263
  │ │ │ Score │ 0.965 │ │
260
264
  └──────────────────┴──────────┴───────────────────────────┴─────────┴──────────┘
261
265
  ```
@@ -337,106 +341,126 @@ warning = 0.9
337
341
  error = 0.7
338
342
  ```
339
343
 
340
- Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
344
+ Tool IDs match the keys in `src/py_cq/config/config.toml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`, `ruff-format`.
341
345
 
342
346
 
343
347
  ### Default config
344
348
 
345
- ```yaml
346
- python:
347
-
348
- compile:
349
- command: "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv"
350
- parser: "CompileParser"
351
- order: 1
352
- warning_threshold: 0.9999
353
- error_threshold: 0.9999
354
-
355
- ruff:
356
- command: "{python} -m ruff check --output-format concise --no-cache \"{context_path}\"{exclude}"
357
- exclude_format: " --exclude {path}"
358
- parser: "RuffParser"
359
- order: 2
360
- warning_threshold: 0.9999
361
- error_threshold: 0.9
362
-
363
- ty:
364
- command: "{python} -m ty check --output-format concise --color never \"{context_path}\"{exclude}"
365
- exclude_format: " --exclude {path}"
366
- parser: "TyParser"
367
- order: 3
368
- warning_threshold: 0.9999
369
- error_threshold: 0.8
370
- run_in_target_env: true
371
- extra_deps:
372
- - ty
373
-
374
- bandit:
375
- command: "{python} -m bandit -r \"{context_path}\" -f json -q -s B101 --severity-level medium --exclude \"{input_path_posix}/.venv,{input_path_posix}/tests{exclude}\""
376
- exclude_format: ",{input_path_posix}/{path}"
377
- parser: "BanditParser"
378
- order: 4
379
- warning_threshold: 0.9999
380
- error_threshold: 0.8
381
-
382
- pytest:
383
- command: "{python} -m pytest -v \"{context_path}\"{exclude}"
384
- exclude_format: " --ignore {path}"
385
- parser: "PytestParser"
386
- order: 5
387
- warning_threshold: 1.0
388
- error_threshold: 1.0
389
- run_in_target_env: true
390
- extra_deps:
391
- - pytest
392
-
393
- coverage:
394
- command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest \"{context_path}\" && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
395
- parser: "CoverageParser"
396
- order: 6
397
- warning_threshold: 0.9
398
- error_threshold: 0.5
399
- run_in_target_env: true
400
- extra_deps:
401
- - coverage
402
- - pytest
403
-
404
- radon-cc:
405
- command: "{python} -m radon cc --json \"{context_path}\""
406
- parser: "ComplexityParser"
407
- order: 7
408
- warning_threshold: 0.6
409
- error_threshold: 0.4
410
-
411
- radon-mi:
412
- command: "{python} -m radon mi -s --json \"{context_path}\""
413
- parser: "MaintainabilityParser"
414
- order: 8
415
- warning_threshold: 0.6
416
- error_threshold: 0.4
417
-
418
- radon-hal:
419
- command: "{python} -m radon hal -f --json \"{context_path}\""
420
- parser: "HalsteadParser"
421
- order: 9
422
- warning_threshold: 0.5
423
- error_threshold: 0.3
424
-
425
- vulture:
426
- command: "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git{exclude}"
427
- exclude_format: ",{path}"
428
- parser: "VultureParser"
429
- order: 10
430
- warning_threshold: 0.9999
431
- error_threshold: 0.8
432
-
433
- interrogate:
434
- command: "{python} -m interrogate \"{context_path}\"{exclude} -v --fail-under 0"
435
- exclude_format: " -e {path}"
436
- parser: "InterrogateParser"
437
- order: 11
438
- warning_threshold: 0.8
439
- error_threshold: 0.3
349
+ Defined in `src/py_cq/config/config.toml` (sections are namespaced by language):
350
+
351
+ ```toml
352
+ [python.compile]
353
+ command = "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv|\\.claude"
354
+ parser = "CompileParser"
355
+ order = 1
356
+ warning_threshold = 0.9999
357
+ error_threshold = 0.9999
358
+ cache_invariant = "ast"
359
+
360
+ [python.ruff]
361
+ command = "{python} -m ruff check --output-format concise --no-cache \"{context_path}\" --exclude .claude{exclude}"
362
+ exclude_format = " --exclude {path}"
363
+ parser = "RuffParser"
364
+ order = 2
365
+ warning_threshold = 0.9999
366
+ error_threshold = 0.9
367
+
368
+ [python.ty]
369
+ command = "{python} -m ty check --output-format concise --color never \"{context_path}\" --exclude .claude{exclude}"
370
+ exclude_format = " --exclude {path}"
371
+ parser = "TyParser"
372
+ order = 3
373
+ warning_threshold = 0.9999
374
+ error_threshold = 0.8
375
+ run_in_target_env = true
376
+ extra_deps = ["ty"]
377
+
378
+ [python.bandit]
379
+ command = "{python} -m bandit -r {scan_targets} -f json -q -s B101 --severity-level medium{exclude}"
380
+ scan_exclude_names = [".venv", ".claude", "tests"]
381
+ exclude_format = " --exclude {abs_native_path}"
382
+ parser = "BanditParser"
383
+ order = 4
384
+ warning_threshold = 0.9999
385
+ error_threshold = 0.8
386
+
387
+ [python.pytest]
388
+ command = "{python} -m pytest -vv \"{context_path}\" --ignore .claude{exclude}"
389
+ exclude_format = " --ignore {path}"
390
+ parser = "PytestParser"
391
+ order = 5
392
+ warning_threshold = 1.0
393
+ error_threshold = 1.0
394
+ run_in_target_env = true
395
+ extra_deps = ["pytest"]
396
+ skip_for_file = true
397
+ cache_invariant = "ast"
398
+
399
+ [python.coverage]
400
+ command = "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest \"{context_path}\" --ignore .claude{exclude} && {python} -m coverage report --show-missing --omit=*/tests/*,*/test_*.py"
401
+ exclude_format = " --ignore {path}"
402
+ parser = "CoverageParser"
403
+ order = 6
404
+ warning_threshold = 0.9
405
+ error_threshold = 0.5
406
+ run_in_target_env = true
407
+ extra_deps = ["coverage", "pytest"]
408
+ skip_for_file = true
409
+ skip_if = "pytest"
410
+ cache_invariant = "ast"
411
+
412
+ [python.vulture]
413
+ command = "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git,.claude{exclude}"
414
+ exclude_format = ",{path}"
415
+ parser = "VultureParser"
416
+ order = 7
417
+ warning_threshold = 0.9999
418
+ error_threshold = 0.8
419
+ cache_invariant = "ast"
420
+
421
+ [python.interrogate]
422
+ command = "{python} -m interrogate \"{context_path}\" -e .claude{exclude} -v --fail-under 0"
423
+ exclude_format = " -e {path}"
424
+ parser = "InterrogateParser"
425
+ order = 8
426
+ warning_threshold = 0.8
427
+ error_threshold = 0.5
428
+
429
+ [python.interrogate.parser_config]
430
+ skip_empty_init = true
431
+
432
+ [python.radon-cc]
433
+ command = "{python} -m radon cc --json --exclude '.claude/**' \"{context_path}\""
434
+ parser = "ComplexityParser"
435
+ order = 9
436
+ gate_strict = false
437
+ warning_threshold = 0.6
438
+ error_threshold = 0.4
439
+
440
+ [python.radon-mi]
441
+ command = "{python} -m radon mi -s --json --exclude '.claude/**' \"{context_path}\""
442
+ parser = "MaintainabilityParser"
443
+ order = 10
444
+ gate_strict = false
445
+ warning_threshold = 0.6
446
+ error_threshold = 0.4
447
+
448
+ [python.radon-hal]
449
+ command = "{python} -m radon hal -f --json --exclude '.claude/**' \"{context_path}\""
450
+ parser = "HalsteadParser"
451
+ order = 11
452
+ gate_strict = false
453
+ warning_threshold = 0.35
454
+ error_threshold = 0.3
455
+
456
+ [python.ruff-format]
457
+ command = "{python} -m ruff format --check --output-format concise --no-cache \"{context_path}\" --exclude .claude --config \"include=['*.py','*.pyi','*.ipynb']\"{exclude}"
458
+ exclude_format = " --exclude {path}"
459
+ parser = "RuffFormatParser"
460
+ order = 12
461
+ gate_strict = false
462
+ warning_threshold = 0.9999
463
+ error_threshold = 0.5
440
464
  ```
441
465
 
442
466
  ## Respect
@@ -6,7 +6,7 @@
6
6
  [![Python versions](https://img.shields.io/pypi/pyversions/python-code-quality?)](https://pypi.org/project/python-code-quality/)
7
7
  [![License](https://img.shields.io/github/license/rhiza-fr/py-cq)](LICENSE)
8
8
 
9
- Run 11+ code quality tools, aggregate results into one score, and surface the single most critical defect as a focused markdown prompt — ready to pipe to any LLM.
9
+ Run 12 code quality tools, aggregate results into one score, and surface the single most critical defect as a focused markdown prompt — ready to pipe to any LLM.
10
10
 
11
11
  This can dramatically reduce the amount of noise for LLMs (and humans) and remove the need for them to know about these tools.
12
12
 
@@ -36,6 +36,8 @@ uv tool install .
36
36
 
37
37
  These tools are run in **parallel** except:
38
38
  When running '-o llm', we run sequentially and exit early at the first error.
39
+ `coverage` is a special case: it starts alongside `pytest` and is cancelled if
40
+ `pytest` fails, scoring `0%` without a second test run.
39
41
 
40
42
  | Order | Tool | Measures |
41
43
  |----------|------|----------|
@@ -45,11 +47,12 @@ When running '-o llm', we run sequentially and exit early at the first error.
45
47
  | 4 | bandit | Security vulnerabilities |
46
48
  | 5 | pytest | Test pass rate |
47
49
  | 6 | coverage | Test coverage |
48
- | 7 | radon cc | Cyclomatic complexity |
49
- | 8 | radon mi | Maintainability index |
50
- | 9 | radon hal | Halstead volume / bug estimate |
51
- | 10 | vulture | Dead code |
52
- | 11 | interrogate | Docstring coverage |
50
+ | 7 | vulture | Dead code |
51
+ | 8 | interrogate | Docstring coverage |
52
+ | 9 | radon cc | Cyclomatic complexity |
53
+ | 10 | radon mi | Maintainability index |
54
+ | 11 | radon hal | Halstead volume / bug estimate |
55
+ | 12 | ruff format | Formatting (fraction of files already formatted) |
53
56
 
54
57
  Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
55
58
 
@@ -70,7 +73,7 @@ cq check . --exclude demo # Exclude paths from all tools
70
73
  cq check . --workers 1 # Run sequentially if you like things slow
71
74
  cq check . --clear-cache # Clear cached results before running (rarely needed)
72
75
  cq check . -o llm --hint # Append "run cq again to verify" (for human workflows)
73
- cq config path/to/project/ # Show effective tool configuration
76
+ cq config --path path/to/project/ # Show effective tool configuration
74
77
  cq is-fixed <fingerprint> # Check whether a specific issue has been resolved
75
78
  ```
76
79
 
@@ -219,14 +222,15 @@ Then invoke it with `/cq-fix` in Claude Code. The `$(...)` embeds the live `cq`
219
222
  │ bandit │ 0.56s │ security │ 1.000 │ OK │
220
223
  │ pytest │ 0.91s │ tests │ 1.000 │ OK │
221
224
  │ coverage │ 1.26s │ coverage │ 0.910 │ OK │
222
- │ radon cc │ 0.32s │ simplicity │ 0.982 │ OK │
223
- │ radon mi │ 0.38s │ maintainability │ 0.869 │ OK │
224
- │ radon hal │ 0.30s │ file_bug_free │ 0.928 │ OK │
225
- │ radon hal │ │ file_smallness │ 0.851 │ OK │
226
- │ radon hal │ │ functions_bug_free │ 0.913 │ OK │
227
- │ radon hal │ │ functions_smallness │ 0.724 │ OK │
228
225
  │ vulture │ 0.32s │ dead_code │ 1.000 │ OK │
229
226
  │ interrogate │ 0.36s │ doc_coverage │ 1.000 │ OK │
227
+ │ radon-cc │ 0.32s │ simplicity │ 0.982 │ OK │
228
+ │ radon-mi │ 0.38s │ maintainability │ 0.869 │ OK │
229
+ │ radon-hal │ 0.30s │ file_bug_free │ 0.928 │ OK │
230
+ │ radon-hal │ │ file_smallness │ 0.851 │ OK │
231
+ │ radon-hal │ │ functions_bug_free │ 0.913 │ OK │
232
+ │ radon-hal │ │ functions_smallness │ 0.724 │ OK │
233
+ │ ruff-format │ 0.12s │ formatted │ 1.000 │ OK │
230
234
  │ │ │ Score │ 0.965 │ │
231
235
  └──────────────────┴──────────┴───────────────────────────┴─────────┴──────────┘
232
236
  ```
@@ -308,106 +312,126 @@ warning = 0.9
308
312
  error = 0.7
309
313
  ```
310
314
 
311
- Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
315
+ Tool IDs match the keys in `src/py_cq/config/config.toml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`, `ruff-format`.
312
316
 
313
317
 
314
318
  ### Default config
315
319
 
316
- ```yaml
317
- python:
318
-
319
- compile:
320
- command: "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv"
321
- parser: "CompileParser"
322
- order: 1
323
- warning_threshold: 0.9999
324
- error_threshold: 0.9999
325
-
326
- ruff:
327
- command: "{python} -m ruff check --output-format concise --no-cache \"{context_path}\"{exclude}"
328
- exclude_format: " --exclude {path}"
329
- parser: "RuffParser"
330
- order: 2
331
- warning_threshold: 0.9999
332
- error_threshold: 0.9
333
-
334
- ty:
335
- command: "{python} -m ty check --output-format concise --color never \"{context_path}\"{exclude}"
336
- exclude_format: " --exclude {path}"
337
- parser: "TyParser"
338
- order: 3
339
- warning_threshold: 0.9999
340
- error_threshold: 0.8
341
- run_in_target_env: true
342
- extra_deps:
343
- - ty
344
-
345
- bandit:
346
- command: "{python} -m bandit -r \"{context_path}\" -f json -q -s B101 --severity-level medium --exclude \"{input_path_posix}/.venv,{input_path_posix}/tests{exclude}\""
347
- exclude_format: ",{input_path_posix}/{path}"
348
- parser: "BanditParser"
349
- order: 4
350
- warning_threshold: 0.9999
351
- error_threshold: 0.8
352
-
353
- pytest:
354
- command: "{python} -m pytest -v \"{context_path}\"{exclude}"
355
- exclude_format: " --ignore {path}"
356
- parser: "PytestParser"
357
- order: 5
358
- warning_threshold: 1.0
359
- error_threshold: 1.0
360
- run_in_target_env: true
361
- extra_deps:
362
- - pytest
363
-
364
- coverage:
365
- command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest \"{context_path}\" && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
366
- parser: "CoverageParser"
367
- order: 6
368
- warning_threshold: 0.9
369
- error_threshold: 0.5
370
- run_in_target_env: true
371
- extra_deps:
372
- - coverage
373
- - pytest
374
-
375
- radon-cc:
376
- command: "{python} -m radon cc --json \"{context_path}\""
377
- parser: "ComplexityParser"
378
- order: 7
379
- warning_threshold: 0.6
380
- error_threshold: 0.4
381
-
382
- radon-mi:
383
- command: "{python} -m radon mi -s --json \"{context_path}\""
384
- parser: "MaintainabilityParser"
385
- order: 8
386
- warning_threshold: 0.6
387
- error_threshold: 0.4
388
-
389
- radon-hal:
390
- command: "{python} -m radon hal -f --json \"{context_path}\""
391
- parser: "HalsteadParser"
392
- order: 9
393
- warning_threshold: 0.5
394
- error_threshold: 0.3
395
-
396
- vulture:
397
- command: "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git{exclude}"
398
- exclude_format: ",{path}"
399
- parser: "VultureParser"
400
- order: 10
401
- warning_threshold: 0.9999
402
- error_threshold: 0.8
403
-
404
- interrogate:
405
- command: "{python} -m interrogate \"{context_path}\"{exclude} -v --fail-under 0"
406
- exclude_format: " -e {path}"
407
- parser: "InterrogateParser"
408
- order: 11
409
- warning_threshold: 0.8
410
- error_threshold: 0.3
320
+ Defined in `src/py_cq/config/config.toml` (sections are namespaced by language):
321
+
322
+ ```toml
323
+ [python.compile]
324
+ command = "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv|\\.claude"
325
+ parser = "CompileParser"
326
+ order = 1
327
+ warning_threshold = 0.9999
328
+ error_threshold = 0.9999
329
+ cache_invariant = "ast"
330
+
331
+ [python.ruff]
332
+ command = "{python} -m ruff check --output-format concise --no-cache \"{context_path}\" --exclude .claude{exclude}"
333
+ exclude_format = " --exclude {path}"
334
+ parser = "RuffParser"
335
+ order = 2
336
+ warning_threshold = 0.9999
337
+ error_threshold = 0.9
338
+
339
+ [python.ty]
340
+ command = "{python} -m ty check --output-format concise --color never \"{context_path}\" --exclude .claude{exclude}"
341
+ exclude_format = " --exclude {path}"
342
+ parser = "TyParser"
343
+ order = 3
344
+ warning_threshold = 0.9999
345
+ error_threshold = 0.8
346
+ run_in_target_env = true
347
+ extra_deps = ["ty"]
348
+
349
+ [python.bandit]
350
+ command = "{python} -m bandit -r {scan_targets} -f json -q -s B101 --severity-level medium{exclude}"
351
+ scan_exclude_names = [".venv", ".claude", "tests"]
352
+ exclude_format = " --exclude {abs_native_path}"
353
+ parser = "BanditParser"
354
+ order = 4
355
+ warning_threshold = 0.9999
356
+ error_threshold = 0.8
357
+
358
+ [python.pytest]
359
+ command = "{python} -m pytest -vv \"{context_path}\" --ignore .claude{exclude}"
360
+ exclude_format = " --ignore {path}"
361
+ parser = "PytestParser"
362
+ order = 5
363
+ warning_threshold = 1.0
364
+ error_threshold = 1.0
365
+ run_in_target_env = true
366
+ extra_deps = ["pytest"]
367
+ skip_for_file = true
368
+ cache_invariant = "ast"
369
+
370
+ [python.coverage]
371
+ command = "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest \"{context_path}\" --ignore .claude{exclude} && {python} -m coverage report --show-missing --omit=*/tests/*,*/test_*.py"
372
+ exclude_format = " --ignore {path}"
373
+ parser = "CoverageParser"
374
+ order = 6
375
+ warning_threshold = 0.9
376
+ error_threshold = 0.5
377
+ run_in_target_env = true
378
+ extra_deps = ["coverage", "pytest"]
379
+ skip_for_file = true
380
+ skip_if = "pytest"
381
+ cache_invariant = "ast"
382
+
383
+ [python.vulture]
384
+ command = "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git,.claude{exclude}"
385
+ exclude_format = ",{path}"
386
+ parser = "VultureParser"
387
+ order = 7
388
+ warning_threshold = 0.9999
389
+ error_threshold = 0.8
390
+ cache_invariant = "ast"
391
+
392
+ [python.interrogate]
393
+ command = "{python} -m interrogate \"{context_path}\" -e .claude{exclude} -v --fail-under 0"
394
+ exclude_format = " -e {path}"
395
+ parser = "InterrogateParser"
396
+ order = 8
397
+ warning_threshold = 0.8
398
+ error_threshold = 0.5
399
+
400
+ [python.interrogate.parser_config]
401
+ skip_empty_init = true
402
+
403
+ [python.radon-cc]
404
+ command = "{python} -m radon cc --json --exclude '.claude/**' \"{context_path}\""
405
+ parser = "ComplexityParser"
406
+ order = 9
407
+ gate_strict = false
408
+ warning_threshold = 0.6
409
+ error_threshold = 0.4
410
+
411
+ [python.radon-mi]
412
+ command = "{python} -m radon mi -s --json --exclude '.claude/**' \"{context_path}\""
413
+ parser = "MaintainabilityParser"
414
+ order = 10
415
+ gate_strict = false
416
+ warning_threshold = 0.6
417
+ error_threshold = 0.4
418
+
419
+ [python.radon-hal]
420
+ command = "{python} -m radon hal -f --json --exclude '.claude/**' \"{context_path}\""
421
+ parser = "HalsteadParser"
422
+ order = 11
423
+ gate_strict = false
424
+ warning_threshold = 0.35
425
+ error_threshold = 0.3
426
+
427
+ [python.ruff-format]
428
+ command = "{python} -m ruff format --check --output-format concise --no-cache \"{context_path}\" --exclude .claude --config \"include=['*.py','*.pyi','*.ipynb']\"{exclude}"
429
+ exclude_format = " --exclude {path}"
430
+ parser = "RuffFormatParser"
431
+ order = 12
432
+ gate_strict = false
433
+ warning_threshold = 0.9999
434
+ error_threshold = 0.5
411
435
  ```
412
436
 
413
437
  ## Respect