python-code-quality 0.2.2__tar.gz → 0.3.0__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.2 → python_code_quality-0.3.0}/PKG-INFO +116 -103
  2. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/README.md +114 -101
  3. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/pyproject.toml +18 -15
  4. python_code_quality-0.3.0/pyproject.toml.orig +71 -0
  5. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/api.py +26 -5
  6. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/cli.py +21 -11
  7. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/config/config.toml +30 -22
  8. python_code_quality-0.3.0/src/py_cq/context_hash.py +137 -0
  9. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/execution_engine.py +175 -38
  10. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/localtypes.py +10 -7
  11. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/common.py +17 -18
  12. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/complexityparser.py +2 -0
  13. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/coverageparser.py +11 -18
  14. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/halsteadparser.py +4 -3
  15. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/interrogateparser.py +20 -21
  16. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/pytestparser.py +1 -1
  17. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/ruffparser.py +1 -1
  18. python_code_quality-0.3.0/src/py_cq/source_file.py +199 -0
  19. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/table_formatter.py +1 -1
  20. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/tool_registry.py +3 -0
  21. python_code_quality-0.2.2/src/py_cq/context_hash.py +0 -91
  22. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/__init__.py +0 -0
  23. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/config/__init__.py +0 -0
  24. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/language_detector.py +0 -0
  25. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/llm_formatter.py +0 -0
  26. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/main.py +0 -0
  27. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/metric_aggregator.py +0 -0
  28. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/__init__.py +0 -0
  29. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/banditparser.py +0 -0
  30. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/compileparser.py +0 -0
  31. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/exitcodeparser.py +0 -0
  32. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/linecountparser.py +0 -0
  33. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/maintainabilityparser.py +0 -0
  34. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/regexcountparser.py +0 -0
  35. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/typarser.py +0 -0
  36. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/parsers/vultureparser.py +0 -0
  37. {python_code_quality-0.2.2 → python_code_quality-0.3.0}/src/py_cq/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-code-quality
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens.
5
5
  Author: Chris Kilner
6
6
  Author-email: Chris Kilner <chris@rhiza.fr>
@@ -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
@@ -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,11 @@ 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 |
82
84
 
83
85
  Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
84
86
 
@@ -337,106 +339,117 @@ warning = 0.9
337
339
  error = 0.7
338
340
  ```
339
341
 
340
- Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
342
+ 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`.
341
343
 
342
344
 
343
345
  ### Default config
344
346
 
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
347
+ Defined in `src/py_cq/config/config.toml` (sections are namespaced by language):
348
+
349
+ ```toml
350
+ [python.compile]
351
+ command = "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv|\\.claude"
352
+ parser = "CompileParser"
353
+ order = 1
354
+ warning_threshold = 0.9999
355
+ error_threshold = 0.9999
356
+ cache_invariant = "ast"
357
+
358
+ [python.ruff]
359
+ command = "{python} -m ruff check --output-format concise --no-cache \"{context_path}\" --exclude .claude{exclude}"
360
+ exclude_format = " --exclude {path}"
361
+ parser = "RuffParser"
362
+ order = 2
363
+ warning_threshold = 0.9999
364
+ error_threshold = 0.9
365
+
366
+ [python.ty]
367
+ command = "{python} -m ty check --output-format concise --color never \"{context_path}\" --exclude .claude{exclude}"
368
+ exclude_format = " --exclude {path}"
369
+ parser = "TyParser"
370
+ order = 3
371
+ warning_threshold = 0.9999
372
+ error_threshold = 0.8
373
+ run_in_target_env = true
374
+ extra_deps = ["ty"]
375
+
376
+ [python.bandit]
377
+ command = "{python} -m bandit -r {scan_targets} -f json -q -s B101 --severity-level medium{exclude}"
378
+ scan_exclude_names = [".venv", ".claude", "tests"]
379
+ exclude_format = " --exclude {abs_native_path}"
380
+ parser = "BanditParser"
381
+ order = 4
382
+ warning_threshold = 0.9999
383
+ error_threshold = 0.8
384
+
385
+ [python.pytest]
386
+ command = "{python} -m pytest -vv \"{context_path}\" --ignore .claude{exclude}"
387
+ exclude_format = " --ignore {path}"
388
+ parser = "PytestParser"
389
+ order = 5
390
+ warning_threshold = 1.0
391
+ error_threshold = 1.0
392
+ run_in_target_env = true
393
+ extra_deps = ["pytest"]
394
+ skip_for_file = true
395
+ cache_invariant = "ast"
396
+
397
+ [python.coverage]
398
+ 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"
399
+ exclude_format = " --ignore {path}"
400
+ parser = "CoverageParser"
401
+ order = 6
402
+ warning_threshold = 0.9
403
+ error_threshold = 0.5
404
+ run_in_target_env = true
405
+ extra_deps = ["coverage", "pytest"]
406
+ skip_for_file = true
407
+ skip_if = "pytest"
408
+ cache_invariant = "ast"
409
+
410
+ [python.vulture]
411
+ command = "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git,.claude{exclude}"
412
+ exclude_format = ",{path}"
413
+ parser = "VultureParser"
414
+ order = 7
415
+ warning_threshold = 0.9999
416
+ error_threshold = 0.8
417
+ cache_invariant = "ast"
418
+
419
+ [python.interrogate]
420
+ command = "{python} -m interrogate \"{context_path}\" -e .claude{exclude} -v --fail-under 0"
421
+ exclude_format = " -e {path}"
422
+ parser = "InterrogateParser"
423
+ order = 8
424
+ warning_threshold = 0.8
425
+ error_threshold = 0.5
426
+
427
+ [python.interrogate.parser_config]
428
+ skip_empty_init = true
429
+
430
+ [python.radon-cc]
431
+ command = "{python} -m radon cc --json --exclude '.claude/**' \"{context_path}\""
432
+ parser = "ComplexityParser"
433
+ order = 9
434
+ gate_strict = false
435
+ warning_threshold = 0.6
436
+ error_threshold = 0.4
437
+
438
+ [python.radon-mi]
439
+ command = "{python} -m radon mi -s --json --exclude '.claude/**' \"{context_path}\""
440
+ parser = "MaintainabilityParser"
441
+ order = 10
442
+ gate_strict = false
443
+ warning_threshold = 0.6
444
+ error_threshold = 0.4
445
+
446
+ [python.radon-hal]
447
+ command = "{python} -m radon hal -f --json --exclude '.claude/**' \"{context_path}\""
448
+ parser = "HalsteadParser"
449
+ order = 11
450
+ gate_strict = false
451
+ warning_threshold = 0.35
452
+ error_threshold = 0.3
440
453
  ```
441
454
 
442
455
  ## Respect
@@ -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,11 @@ 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 |
53
55
 
54
56
  Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
55
57
 
@@ -308,106 +310,117 @@ warning = 0.9
308
310
  error = 0.7
309
311
  ```
310
312
 
311
- Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
313
+ 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`.
312
314
 
313
315
 
314
316
  ### Default config
315
317
 
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
318
+ Defined in `src/py_cq/config/config.toml` (sections are namespaced by language):
319
+
320
+ ```toml
321
+ [python.compile]
322
+ command = "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv|\\.claude"
323
+ parser = "CompileParser"
324
+ order = 1
325
+ warning_threshold = 0.9999
326
+ error_threshold = 0.9999
327
+ cache_invariant = "ast"
328
+
329
+ [python.ruff]
330
+ command = "{python} -m ruff check --output-format concise --no-cache \"{context_path}\" --exclude .claude{exclude}"
331
+ exclude_format = " --exclude {path}"
332
+ parser = "RuffParser"
333
+ order = 2
334
+ warning_threshold = 0.9999
335
+ error_threshold = 0.9
336
+
337
+ [python.ty]
338
+ command = "{python} -m ty check --output-format concise --color never \"{context_path}\" --exclude .claude{exclude}"
339
+ exclude_format = " --exclude {path}"
340
+ parser = "TyParser"
341
+ order = 3
342
+ warning_threshold = 0.9999
343
+ error_threshold = 0.8
344
+ run_in_target_env = true
345
+ extra_deps = ["ty"]
346
+
347
+ [python.bandit]
348
+ command = "{python} -m bandit -r {scan_targets} -f json -q -s B101 --severity-level medium{exclude}"
349
+ scan_exclude_names = [".venv", ".claude", "tests"]
350
+ exclude_format = " --exclude {abs_native_path}"
351
+ parser = "BanditParser"
352
+ order = 4
353
+ warning_threshold = 0.9999
354
+ error_threshold = 0.8
355
+
356
+ [python.pytest]
357
+ command = "{python} -m pytest -vv \"{context_path}\" --ignore .claude{exclude}"
358
+ exclude_format = " --ignore {path}"
359
+ parser = "PytestParser"
360
+ order = 5
361
+ warning_threshold = 1.0
362
+ error_threshold = 1.0
363
+ run_in_target_env = true
364
+ extra_deps = ["pytest"]
365
+ skip_for_file = true
366
+ cache_invariant = "ast"
367
+
368
+ [python.coverage]
369
+ 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"
370
+ exclude_format = " --ignore {path}"
371
+ parser = "CoverageParser"
372
+ order = 6
373
+ warning_threshold = 0.9
374
+ error_threshold = 0.5
375
+ run_in_target_env = true
376
+ extra_deps = ["coverage", "pytest"]
377
+ skip_for_file = true
378
+ skip_if = "pytest"
379
+ cache_invariant = "ast"
380
+
381
+ [python.vulture]
382
+ command = "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git,.claude{exclude}"
383
+ exclude_format = ",{path}"
384
+ parser = "VultureParser"
385
+ order = 7
386
+ warning_threshold = 0.9999
387
+ error_threshold = 0.8
388
+ cache_invariant = "ast"
389
+
390
+ [python.interrogate]
391
+ command = "{python} -m interrogate \"{context_path}\" -e .claude{exclude} -v --fail-under 0"
392
+ exclude_format = " -e {path}"
393
+ parser = "InterrogateParser"
394
+ order = 8
395
+ warning_threshold = 0.8
396
+ error_threshold = 0.5
397
+
398
+ [python.interrogate.parser_config]
399
+ skip_empty_init = true
400
+
401
+ [python.radon-cc]
402
+ command = "{python} -m radon cc --json --exclude '.claude/**' \"{context_path}\""
403
+ parser = "ComplexityParser"
404
+ order = 9
405
+ gate_strict = false
406
+ warning_threshold = 0.6
407
+ error_threshold = 0.4
408
+
409
+ [python.radon-mi]
410
+ command = "{python} -m radon mi -s --json --exclude '.claude/**' \"{context_path}\""
411
+ parser = "MaintainabilityParser"
412
+ order = 10
413
+ gate_strict = false
414
+ warning_threshold = 0.6
415
+ error_threshold = 0.4
416
+
417
+ [python.radon-hal]
418
+ command = "{python} -m radon hal -f --json --exclude '.claude/**' \"{context_path}\""
419
+ parser = "HalsteadParser"
420
+ order = 11
421
+ gate_strict = false
422
+ warning_threshold = 0.35
423
+ error_threshold = 0.3
411
424
  ```
412
425
 
413
426
  ## Respect
@@ -1,17 +1,15 @@
1
1
  [project]
2
2
  name = "python-code-quality"
3
- version = "0.2.2"
3
+ version = "0.3.0"
4
4
  description = "Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  license = "MIT"
8
- authors = [{name = "Chris Kilner", email = "chris@rhiza.fr"}]
9
8
  classifiers = [
10
9
  "Programming Language :: Python :: 3.12",
11
10
  "Programming Language :: Python :: 3.13",
12
11
  "Programming Language :: Python :: 3.14",
13
12
  ]
14
-
15
13
  dependencies = [
16
14
  "bandit>=1.8.0",
17
15
  "coverage>=7.8.2",
@@ -22,43 +20,48 @@ dependencies = [
22
20
  "pytest-json-report>=1.5.0",
23
21
  "radon>=6.0.1",
24
22
  "rich>=14.0.0",
25
- "ruff>=0.14.1",
23
+ "ruff>=0.14.1,<0.17",
26
24
  "tomlkit>=0.13.0",
27
25
  "ty>=0.0.17",
28
26
  "typer>=0.16.0",
29
27
  "vulture>=2.14",
30
28
  ]
31
29
 
30
+ [[project.authors]]
31
+ name = "Chris Kilner"
32
+ email = "chris@rhiza.fr"
33
+
32
34
  [project.urls]
33
35
  Homepage = "https://github.com/rhiza-fr/py-cq"
34
36
  Repository = "https://github.com/rhiza-fr/py-cq"
35
37
 
38
+ [project.scripts]
39
+ cq = "py_cq.main:main"
40
+
36
41
  [build-system]
37
- requires = ["uv_build"]
42
+ requires = ["uv_build>=0.12.0,<0.13.0"]
38
43
  build-backend = "uv_build"
39
44
 
40
45
  [tool.uv.build-backend]
41
46
  module-name = "py_cq"
42
47
 
43
- [project.scripts]
44
- cq = "py_cq.main:main"
45
-
46
48
  [tool.pytest.ini_options]
47
49
  testpaths = ["tests"]
48
- norecursedirs = [".venv", "dist"]
49
- markers = [
50
- "slow: tests that have real side effects (e.g. clearing disk cache)",
50
+ norecursedirs = [
51
+ ".venv",
52
+ "dist",
51
53
  ]
54
+ markers = ["slow: tests that have real side effects (e.g. clearing disk cache)"]
52
55
  addopts = "-m 'not slow'"
53
56
 
54
57
  [[tool.ty.overrides]]
55
58
  include = ["demo/**"]
56
- rules = {unresolved-import = "ignore"}
59
+
60
+ [tool.ty.overrides.rules]
61
+ unresolved-import = "ignore"
57
62
 
58
63
  [tool.cq]
59
64
  exclude = ["demo"]
60
65
 
61
66
  [dependency-groups]
62
- dev = [
63
- "hypothesis>=6.151.10",
64
- ]
67
+ dev = ["hypothesis>=6.151.10"]
@@ -0,0 +1,71 @@
1
+ [project]
2
+ name = "python-code-quality"
3
+ version = "0.3.0"
4
+ description = "Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ authors = [{name = "Chris Kilner", email = "chris@rhiza.fr"}]
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3.12",
11
+ "Programming Language :: Python :: 3.13",
12
+ "Programming Language :: Python :: 3.14",
13
+ ]
14
+
15
+ dependencies = [
16
+ "bandit>=1.8.0",
17
+ "coverage>=7.8.2",
18
+ "diskcache>=5.6.3",
19
+ "interrogate>=1.7.0",
20
+ "pytest>=8.4.0",
21
+ "pytest-cov>=6.1.1",
22
+ "pytest-json-report>=1.5.0",
23
+ "radon>=6.0.1",
24
+ "rich>=14.0.0",
25
+ # Upper-capped: ruff's default rule set is not stable across minor versions
26
+ # (0.15 enabled ~60 rules, 0.16 enables 413). cq runs ruff from its own
27
+ # environment, so an unbounded bump silently changes every user's lint score.
28
+ # Raise this deliberately, with a changelog note.
29
+ "ruff>=0.14.1,<0.17",
30
+ "tomlkit>=0.13.0",
31
+ "ty>=0.0.17",
32
+ "typer>=0.16.0",
33
+ "vulture>=2.14",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/rhiza-fr/py-cq"
38
+ Repository = "https://github.com/rhiza-fr/py-cq"
39
+
40
+ [build-system]
41
+ # Upper-capped for the same reason as ruff: an unbounded backend requirement
42
+ # lets a future breaking uv_build release break the sdist build with no change
43
+ # here. Raise deliberately alongside the uv version this is built with.
44
+ requires = ["uv_build>=0.12.0,<0.13.0"]
45
+ build-backend = "uv_build"
46
+
47
+ [tool.uv.build-backend]
48
+ module-name = "py_cq"
49
+
50
+ [project.scripts]
51
+ cq = "py_cq.main:main"
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ norecursedirs = [".venv", "dist"]
56
+ markers = [
57
+ "slow: tests that have real side effects (e.g. clearing disk cache)",
58
+ ]
59
+ addopts = "-m 'not slow'"
60
+
61
+ [[tool.ty.overrides]]
62
+ include = ["demo/**"]
63
+ rules = {unresolved-import = "ignore"}
64
+
65
+ [tool.cq]
66
+ exclude = ["demo"]
67
+
68
+ [dependency-groups]
69
+ dev = [
70
+ "hypothesis>=6.151.10",
71
+ ]