python-code-quality 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

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.
@@ -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
@@ -0,0 +1,36 @@
1
+ py_cq/__init__.py,sha256=-C6_QzQeydxft3ndlExTgWnvlqH8fJNPJxfySXULQmU,56
2
+ py_cq/api.py,sha256=AxhTQVX4ACZmgCpdfJgB2_1VXtClxc531NkRoLK1vA0,10118
3
+ py_cq/cli.py,sha256=aaLnBXF81fkUtCvO7DGzmogcq50X5bF5dC4XFC4sbqY,13814
4
+ py_cq/config/__init__.py,sha256=f0wc51O_3kGDTZUnCbGv8_zWnC5yYGl4NWcf2buSImQ,670
5
+ py_cq/config/config.toml,sha256=fco0qKWfIg84e3ex2pmAcSPYBmcHdbiqL-Geo__EIkM,3017
6
+ py_cq/context_hash.py,sha256=uKMkN394v6QYMukZj9usbxnq8px3i1khyIRPKwe09DE,5614
7
+ py_cq/execution_engine.py,sha256=vAbNDCHP5c-2ADWs9SM0eCEpZBiabhpl-WZp6Er3qb4,20644
8
+ py_cq/language_detector.py,sha256=rh5bAA3LyCQR3RODmx_EalqRNYvSYK2SJzh2hIkxOUo,1139
9
+ py_cq/llm_formatter.py,sha256=nCV3nFdxJAi2CnH9FKb5tB-0w-yjLWmjywQI5GJ2cSU,8412
10
+ py_cq/localtypes.py,sha256=mt4sqUg1bz2BenirGA18X4CdtyZahTAuKyOfwFnwfSE,7907
11
+ py_cq/main.py,sha256=5dgr8Zk1LNK5M-0wncYVksBp0qE-jWZYqmOXfb1eOhk,400
12
+ py_cq/metric_aggregator.py,sha256=M2ymo62S7p7qPUqjjoiPg4IVyXQhLMuTr9-jxLiFjCY,853
13
+ py_cq/parsers/__init__.py,sha256=uCy7R0cnvDPNvB5aWMLgM3FB9nDcu1jwUmznFMRROSY,28
14
+ py_cq/parsers/banditparser.py,sha256=YqpWljU5D4IhLdtzat7XVGBm9NXrbOQbr9LI9NKybK8,3298
15
+ py_cq/parsers/common.py,sha256=r8UD7pch-txlJSTsJrOff2o3_0WHRlZhgKbJ-C58L7g,13818
16
+ py_cq/parsers/compileparser.py,sha256=jl0DloyXTKqVHRIUft-t5lvJOndY-PdLPrXB38v9zuA,6927
17
+ py_cq/parsers/complexityparser.py,sha256=mX1WHPRgBBc6kOqHfubowRbYXY0E1k4JVwr908Cj10U,5582
18
+ py_cq/parsers/coverageparser.py,sha256=QGjZ8JJF0G-0uiZ07BzxSnblGiysHtOwq0SYvWrtOhA,7441
19
+ py_cq/parsers/exitcodeparser.py,sha256=b4LWIwwklPwCX3MnqNsGQC1slQU56DKE05vJ0OdWVoA,957
20
+ py_cq/parsers/halsteadparser.py,sha256=789c_WWfomskM4i7SUEbKvB67-F2y5nfqt48aFXOusY,10192
21
+ py_cq/parsers/interrogateparser.py,sha256=55HZ2j1Rz34eremwGx-W9oIMI21DIqwxEG524UgsAhk,11249
22
+ py_cq/parsers/linecountparser.py,sha256=-hqebwG4j7_QugzGLUUQRcPukGHicV2nbIp1pR8ZcAw,1310
23
+ py_cq/parsers/maintainabilityparser.py,sha256=3a4m93XNtUurw3IxEI1Iae9idgJY6qiEt7HMYl1wTu0,4793
24
+ py_cq/parsers/pytestparser.py,sha256=cIaTs2XxgeJlVragLEVTZcrbB-bzfNYYMdUrakRa-rQ,11545
25
+ py_cq/parsers/regexcountparser.py,sha256=zGzHEYDIori6aiWnVkRFixdE7fWt66npAAWjzvpoBF4,1592
26
+ py_cq/parsers/ruffparser.py,sha256=uE0QEaZ4Ch7IsWY_2ZkfHXPU0TYPGsGPfv66hGe5G34,7634
27
+ py_cq/parsers/typarser.py,sha256=x835OrapG1aUteW2NhC5U0bhj1w1pMtbfmbvb74-1AI,8077
28
+ py_cq/parsers/vultureparser.py,sha256=-yGAXzRY4_fzLRTk7dfwDrrRZ-n97j1Ph5_xo3c6TfU,2229
29
+ py_cq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ py_cq/source_file.py,sha256=Uyv-CSVeliFnE6TpRB4Y15ykFQs4n0r9-amzxXXlqN0,7562
31
+ py_cq/table_formatter.py,sha256=-22nzVIOaDe3QOBmZ2iBPH7ewIEIPEn5WYSjCoB4Hj4,1608
32
+ py_cq/tool_registry.py,sha256=0s0sD9_iQW-HU0SIKSS8CFDlrzW0c-vlYrAFODEHiSc,1894
33
+ python_code_quality-0.3.0.dist-info/WHEEL,sha256=ZFFp7t7R4RYQ5KYZkmiFWoQvHay7SrTmn-6ZYfoFZ3U,80
34
+ python_code_quality-0.3.0.dist-info/entry_points.txt,sha256=cfWbTw7eYO6Trv1-Z_odL6Zta9CqYU6Vk9lAHdfB60Q,40
35
+ python_code_quality-0.3.0.dist-info/METADATA,sha256=hQBSoblR8TKmTPRriXRt5Ed-uO9jexstNj0nPU4Li4A,16213
36
+ python_code_quality-0.3.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.11.18
2
+ Generator: uv 0.12.7
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,35 +0,0 @@
1
- py_cq/__init__.py,sha256=-C6_QzQeydxft3ndlExTgWnvlqH8fJNPJxfySXULQmU,56
2
- py_cq/api.py,sha256=iyiL5WDdQDYSwkqmZ_AuFY8-uJ-mM9WWAQhpdjqVk20,9170
3
- py_cq/cli.py,sha256=5Vw3BPyqjKPeWQBXYCMs_ps7WJmjdhxQFgaNS9lAxiQ,13391
4
- py_cq/config/__init__.py,sha256=f0wc51O_3kGDTZUnCbGv8_zWnC5yYGl4NWcf2buSImQ,670
5
- py_cq/config/config.toml,sha256=L8WKLQg4utDvV1ZwcOCSi6uJAtE1mZPvY-_HJVOT2dk,2841
6
- py_cq/context_hash.py,sha256=UKVrKpBYYw68tVcSAs6ZXkHXV3asQvsn6R9NINOHQ4M,3323
7
- py_cq/execution_engine.py,sha256=CvKkP80jt9_IoB7XYbj-vu1QrJrEVX2CbTxRKyAvblE,14458
8
- py_cq/language_detector.py,sha256=rh5bAA3LyCQR3RODmx_EalqRNYvSYK2SJzh2hIkxOUo,1139
9
- py_cq/llm_formatter.py,sha256=nCV3nFdxJAi2CnH9FKb5tB-0w-yjLWmjywQI5GJ2cSU,8412
10
- py_cq/localtypes.py,sha256=ELzOFOFlrLSq5Wgj5LvBIPhWR8wfe7lQYkcbINhqE3A,7559
11
- py_cq/main.py,sha256=5dgr8Zk1LNK5M-0wncYVksBp0qE-jWZYqmOXfb1eOhk,400
12
- py_cq/metric_aggregator.py,sha256=M2ymo62S7p7qPUqjjoiPg4IVyXQhLMuTr9-jxLiFjCY,853
13
- py_cq/parsers/__init__.py,sha256=uCy7R0cnvDPNvB5aWMLgM3FB9nDcu1jwUmznFMRROSY,28
14
- py_cq/parsers/banditparser.py,sha256=YqpWljU5D4IhLdtzat7XVGBm9NXrbOQbr9LI9NKybK8,3298
15
- py_cq/parsers/common.py,sha256=dU5JCbPJdngbtPjSqnP0kxT2Oux5D-iYiaBMz8KcZCw,13672
16
- py_cq/parsers/compileparser.py,sha256=jl0DloyXTKqVHRIUft-t5lvJOndY-PdLPrXB38v9zuA,6927
17
- py_cq/parsers/complexityparser.py,sha256=dOUCpCDcpNaQXZx_xC61X4dTMktDb9x_nh9tDoWjb5g,5509
18
- py_cq/parsers/coverageparser.py,sha256=t15CFfTJv1QorhopyurDvY7Jr5JJOMLfet0fqttU3O0,7848
19
- py_cq/parsers/exitcodeparser.py,sha256=b4LWIwwklPwCX3MnqNsGQC1slQU56DKE05vJ0OdWVoA,957
20
- py_cq/parsers/halsteadparser.py,sha256=cfUNsLUYt74NMQW1sBkktn8AH7jQ_EU5IMFrLgIdk5M,10165
21
- py_cq/parsers/interrogateparser.py,sha256=cc5Q4mOQlLUIPl3XUyH29QJ_JMBzAA7SiPAAiNGDJdM,11546
22
- py_cq/parsers/linecountparser.py,sha256=-hqebwG4j7_QugzGLUUQRcPukGHicV2nbIp1pR8ZcAw,1310
23
- py_cq/parsers/maintainabilityparser.py,sha256=3a4m93XNtUurw3IxEI1Iae9idgJY6qiEt7HMYl1wTu0,4793
24
- py_cq/parsers/pytestparser.py,sha256=rhzFzsBEXv1xloOuEjQyBn8Vp0AyGoq2t5siyAwWWaA,11549
25
- py_cq/parsers/regexcountparser.py,sha256=zGzHEYDIori6aiWnVkRFixdE7fWt66npAAWjzvpoBF4,1592
26
- py_cq/parsers/ruffparser.py,sha256=ofQyVUuc0JK-6pVdwsRZteCWmNBOIfHzZloVPSH7_wI,7608
27
- py_cq/parsers/typarser.py,sha256=x835OrapG1aUteW2NhC5U0bhj1w1pMtbfmbvb74-1AI,8077
28
- py_cq/parsers/vultureparser.py,sha256=-yGAXzRY4_fzLRTk7dfwDrrRZ-n97j1Ph5_xo3c6TfU,2229
29
- py_cq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- py_cq/table_formatter.py,sha256=2_WZ2RHCwsdna0bzP6ZFxB2YtHtW95wxJB1yBXgCIDo,1610
31
- py_cq/tool_registry.py,sha256=kWmGsqc_0ryYiW-hqUh03WdY1SJQ0wn5Z5ydH72VJK4,1718
32
- python_code_quality-0.2.2.dist-info/WHEEL,sha256=V5-3dKee3Zs8C4JP6swr6zdqriLsOpItBEQxe6_oWpY,81
33
- python_code_quality-0.2.2.dist-info/entry_points.txt,sha256=cfWbTw7eYO6Trv1-Z_odL6Zta9CqYU6Vk9lAHdfB60Q,40
34
- python_code_quality-0.2.2.dist-info/METADATA,sha256=TzzyYDzvGY-zmvmsbP36KhUbZFajkxwrtnsr1s4pEC4,15650
35
- python_code_quality-0.2.2.dist-info/RECORD,,