python-code-quality 0.1.7__tar.gz → 0.1.8__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 (56) hide show
  1. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/CLAUDE.md +5 -5
  2. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/PKG-INFO +63 -65
  3. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/README.md +62 -64
  4. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/pyproject.toml +1 -1
  5. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/cli.py +13 -9
  6. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/config/tools.yaml +12 -12
  7. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/execution_engine.py +14 -2
  8. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/llm_formatter.py +1 -1
  9. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/localtypes.py +4 -4
  10. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/halsteadparser.py +1 -1
  11. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/pytestparser.py +8 -0
  12. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/tool_registry.py +1 -1
  13. python_code_quality-0.1.8/tests/test_cli.py +228 -0
  14. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_common.py +18 -0
  15. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_config.py +2 -2
  16. python_code_quality-0.1.8/tests/test_execution_engine.py +291 -0
  17. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_llm_formatter.py +14 -14
  18. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_localtypes.py +17 -2
  19. python_code_quality-0.1.8/tests/test_parser_halstead.py +116 -0
  20. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_pytest.py +14 -0
  21. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/uv.lock +1 -1
  22. python_code_quality-0.1.7/tests/test_execution_engine.py +0 -131
  23. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/.github/workflows/python-publish.yml +0 -0
  24. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/.gitignore +0 -0
  25. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/.python-version +0 -0
  26. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/LICENSE +0 -0
  27. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/data/problems/travelling_salesman/ts_bad.py +0 -0
  28. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/data/problems/travelling_salesman/ts_good.py +0 -0
  29. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/__init__.py +0 -0
  30. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/config/__init__.py +0 -0
  31. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/context_hash.py +0 -0
  32. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/main.py +0 -0
  33. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/metric_aggregator.py +0 -0
  34. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/__init__.py +0 -0
  35. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/banditparser.py +0 -0
  36. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/common.py +0 -0
  37. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/compileparser.py +0 -0
  38. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/complexityparser.py +0 -0
  39. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/coverageparser.py +0 -0
  40. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/interrogateparser.py +0 -0
  41. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/maintainabilityparser.py +0 -0
  42. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/ruffparser.py +0 -0
  43. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/typarser.py +0 -0
  44. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/parsers/vultureparser.py +0 -0
  45. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/src/py_cq/py.typed +0 -0
  46. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/conftest.py +0 -0
  47. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_context_hash.py +0 -0
  48. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_bandit.py +0 -0
  49. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_compile.py +0 -0
  50. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_complexity.py +0 -0
  51. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_coverage.py +0 -0
  52. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_interrogate.py +0 -0
  53. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_maintainability.py +0 -0
  54. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_ruff.py +0 -0
  55. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_ty.py +0 -0
  56. {python_code_quality-0.1.7 → python_code_quality-0.1.8}/tests/test_parser_vulture.py +0 -0
@@ -11,7 +11,7 @@ cq check -o llm # returns the single most critical defect as markdown
11
11
  ```
12
12
 
13
13
  The LLM fixes it, the user re-runs, and repeats until all tools pass. CQ runs
14
- 11 static analysis tools in priority order (compile → security → lint → types →
14
+ 11 static analysis tools in execution order (compile → security → lint → types →
15
15
  tests → coverage → complexity → dead code → style) and aggregates results into
16
16
  a single score.
17
17
 
@@ -40,15 +40,15 @@ uv run ruff check src/
40
40
 
41
41
  - **`cli.py`**: Typer app with a single `check` command. Output mode selected via `--output`/`-o` enum (`table`, `score`, `json`, `llm`). Runs tools in parallel by default. Reads `[tool.cq]` from the target project's `pyproject.toml` and applies overrides before running.
42
42
  - **`tool_registry.py`**: Loads `src/cq/config/tools.yaml` at import time via `importlib.resources`, dynamically imports parser classes, builds a `dict[str, ToolConfig]` registry.
43
- - **`config/tools.yaml`** (at `src/cq/config/tools.yaml`): Declares each analysis tool: shell command template (with `{context_path}` placeholder), parser class name, priority, warning/error thresholds. Tools are listed and executed in priority order.
44
- - **`execution_engine.py`**: Runs shell commands via `subprocess.run`, caches results with `diskcache` using a content-based hash. Parallel execution via `ThreadPoolExecutor`; results are sorted by priority before returning.
43
+ - **`config/tools.yaml`** (at `src/cq/config/tools.yaml`): Declares each analysis tool: shell command template (with `{context_path}` placeholder), parser class name, order, warning/error thresholds. Tools are listed and executed in order.
44
+ - **`execution_engine.py`**: Runs shell commands via `subprocess.run`, caches results with `diskcache` using a content-based hash. Parallel execution via `ThreadPoolExecutor`; results are sorted by order before returning.
45
45
  - **`parsers/`**: Each parser subclasses `AbstractParser` (from `localtypes.py`), implementing `parse(RawResult) -> ToolResult` and optionally `format_llm_message(ToolResult) -> str`. Parser module names must match the lowercase parser class name (e.g., `PytestParser` → `pytestparser.py`).
46
46
  - **`localtypes.py`**: Core dataclasses — `ToolConfig`, `RawResult`, `ToolResult`, `CombinedToolResults`, and `AbstractParser` ABC.
47
47
  - **`metric_aggregator.py`**: Wraps results into `CombinedToolResults`, which computes an overall score as the average of per-tool mean metrics.
48
- - **`llm_formatter.py`**: Selects the worst-scoring tool by severity tier then priority, formats its top defect as markdown for LLM consumption.
48
+ - **`llm_formatter.py`**: Selects the worst-scoring tool by severity tier then order, formats its top defect as markdown for LLM consumption.
49
49
 
50
50
  ## Adding a New Analysis Tool
51
51
 
52
- 1. Add tool entry in `config/tools.yaml` with command template, parser name, priority, and thresholds.
52
+ 1. Add tool entry in `config/tools.yaml` with command template, parser name, order, and thresholds.
53
53
  2. Create `src/cq/parsers/<parsername>.py` with a class matching the `parser` field in YAML.
54
54
  3. The parser must subclass `AbstractParser` and implement `parse(RawResult) -> ToolResult`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-code-quality
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens.
5
5
  Project-URL: Homepage, https://github.com/rhiza-fr/py-cq
6
6
  Project-URL: Repository, https://github.com/rhiza-fr/py-cq
@@ -34,7 +34,13 @@ The primary workflow is:
34
34
  # get the single most critical defect as markdown
35
35
  cq check . -o llm
36
36
  ```
37
- Outputs the top error from the first priority tool where the score < warning_threshold,. The code context is expanded if available.
37
+ Selects the single most critical defect using this priority order:
38
+
39
+ 1. **Severity** — tools with score below `error_threshold` come before those only below `warning_threshold`
40
+ 2. **Order** — among tools at the same severity, lower-order tools win (compile before lint before style)
41
+ 3. **Score** — among ties, the lower score wins
42
+
43
+ The code context is expanded if available.
38
44
  ```md
39
45
  `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
40
46
 
@@ -51,8 +57,10 @@ Please fix only this issue. After fixing, run `cq check . -o llm` to verify.
51
57
  ```
52
58
  Feed to an LLM with edit tools and repeat until there are no issues, e.g.
53
59
 
54
- ```
60
+ ```python
55
61
  cq check . -o llm | claude -p "fix this"
62
+ # or
63
+ cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
56
64
  ```
57
65
 
58
66
  ## Install
@@ -69,9 +77,9 @@ uv tool install .
69
77
 
70
78
  ## Tools
71
79
 
72
- These tools are run in **parallel**:
80
+ These tools are run in **parallel** except when looking for the first error in -o llm mode:
73
81
 
74
- | Priority | Tool | Measures |
82
+ | Order | Tool | Measures |
75
83
  |----------|------|----------|
76
84
  | 1 | compileall | Syntax errors |
77
85
  | 2 | bandit | Security vulnerabilities |
@@ -91,31 +99,15 @@ Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults
91
99
  ## Usage
92
100
 
93
101
  ```bash
94
- # LLM workflow: get the top defect as markdown (primary use case)
95
- cq check -o llm
96
-
97
- # Rich table with all metrics
98
- cq check .
99
-
100
- # Numeric score only useful in CI or scripts
101
- cq check . -o score
102
-
103
- # Full JSON output, including raw test results
104
- cq check . -o json
105
-
106
- # Explicit path
107
- cq check path/to/project/
108
- cq check path/to/file.py
109
-
110
- # Run sequentially if you like things slow
111
- cq check . --workers 1
112
-
113
- # Clear cached results before running (rarely needed)
114
- cq check . --clear-cache
115
-
116
- # Show effective tool configuration (thresholds, enabled/disabled status)
117
- cq config
118
- cq config path/to/project/
102
+ cq check . # Table overview of scores for humans
103
+ cq check -o llm # Top defect as markdown for LLMs
104
+ cq check . -o score # Numeric score only for CI
105
+ cq check . -o json # Detailed parsed JSON output for jq
106
+ cq check . -o raw # Raw tool output for debug
107
+ cq check path/to/file.py # Just one file (skips pytest and coverage)
108
+ cq check . --workers 1 # Run sequentially if you like things slow
109
+ cq check . --clear-cache # Clear cached results before running (rarely needed)
110
+ cq config path/to/project/ # Show effective tool configuration
119
111
  ```
120
112
 
121
113
  ## Table output
@@ -160,30 +152,36 @@ cq config path/to/project/
160
152
  ```
161
153
 
162
154
  ```json
163
- {
164
- "metrics": [
165
- {
166
- "metrics": {
167
- "compile": 1.0
168
- },
169
- "details": {},
170
- "raw": {
171
- "tool_name": "compile",
172
- "command": ".venv\\Scripts\\python.exe -m compileall -r 10 -j 8 . -x .*venv",
173
- "stdout": "Compiling './src/project/file.py'...",
174
- "stderr": "",
175
- "return_code": 0,
176
- "timestamp": "2026-02-19 05:03:11"
177
- },
178
- "duration_s": 0.08294440002646297
155
+ [
156
+ {
157
+ "tool_name": "compile",
158
+ "metrics": {
159
+ "compile": 1.0
179
160
  },
180
- {
181
- "metrics": {
182
- "security": 1.0
183
- }
184
- // many more lines ...
185
- }
186
- ]}
161
+ "details": {},
162
+ "duration_s": 0.05611889995634556
163
+ }
164
+ ...
165
+ ]
166
+ ```
167
+
168
+ ## Raw output
169
+ ```bash
170
+ > cq check -o raw
171
+ ```
172
+
173
+ ```json
174
+ [
175
+ {
176
+ "tool_name": "compile",
177
+ "command": "D:\\ai\\py-cq\\.venv\\Scripts\\python.exe -m compileall -r 10 -j 8 . -x .*venv",
178
+ "stdout": "",
179
+ "stderr": "",
180
+ "return_code": 0,
181
+ "timestamp": "2026-02-20 10:01:22"
182
+ }
183
+ ...
184
+ ]
187
185
  ```
188
186
 
189
187
  ## Configuration
@@ -213,7 +211,7 @@ tools:
213
211
  name: "compile"
214
212
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
215
213
  parser: "CompileParser"
216
- priority: 1
214
+ order: 1
217
215
  warning_threshold: 0.9999
218
216
  error_threshold: 0.9999
219
217
 
@@ -221,7 +219,7 @@ tools:
221
219
  name: "bandit"
222
220
  command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
223
221
  parser: "BanditParser"
224
- priority: 2
222
+ order: 2
225
223
  warning_threshold: 0.9999
226
224
  error_threshold: 0.8
227
225
 
@@ -229,7 +227,7 @@ tools:
229
227
  name: "ruff"
230
228
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
231
229
  parser: "RuffParser"
232
- priority: 3
230
+ order: 3
233
231
  warning_threshold: 0.9999
234
232
  error_threshold: 0.9
235
233
 
@@ -237,7 +235,7 @@ tools:
237
235
  name: "ty"
238
236
  command: "{python} -m ty check --output-format concise --color never {context_path}"
239
237
  parser: "TyParser"
240
- priority: 4
238
+ order: 4
241
239
  warning_threshold: 0.9999
242
240
  error_threshold: 0.8
243
241
  run_in_target_env: true
@@ -248,16 +246,16 @@ tools:
248
246
  name: "pytest"
249
247
  command: "{python} -m pytest -v {context_path}"
250
248
  parser: "PytestParser"
251
- priority: 5
249
+ order: 5
252
250
  warning_threshold: 0.7
253
251
  error_threshold: 0.5
254
252
  run_in_target_env: true
255
253
 
256
254
  coverage:
257
255
  name: "coverage"
258
- command: "{python} -m coverage run -m pytest {context_path} && {python} -m coverage report"
256
+ command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
259
257
  parser: "CoverageParser"
260
- priority: 6
258
+ order: 6
261
259
  warning_threshold: 0.9
262
260
  error_threshold: 0.5
263
261
  run_in_target_env: true
@@ -269,7 +267,7 @@ tools:
269
267
  name: "radon cc"
270
268
  command: "{python} -m radon cc --json {context_path}"
271
269
  parser: "ComplexityParser"
272
- priority: 7
270
+ order: 7
273
271
  warning_threshold: 0.6
274
272
  error_threshold: 0.4
275
273
 
@@ -277,7 +275,7 @@ tools:
277
275
  name: "radon mi"
278
276
  command: "{python} -m radon mi -s --json {context_path}"
279
277
  parser: "MaintainabilityParser"
280
- priority: 8
278
+ order: 8
281
279
  warning_threshold: 0.6
282
280
  error_threshold: 0.4
283
281
 
@@ -285,7 +283,7 @@ tools:
285
283
  name: "radon hal"
286
284
  command: "{python} -m radon hal -f --json {context_path}"
287
285
  parser: "HalsteadParser"
288
- priority: 9
286
+ order: 9
289
287
  warning_threshold: 0.5
290
288
  error_threshold: 0.3
291
289
 
@@ -293,7 +291,7 @@ tools:
293
291
  name: "vulture"
294
292
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
295
293
  parser: "VultureParser"
296
- priority: 10
294
+ order: 10
297
295
  warning_threshold: 0.9999
298
296
  error_threshold: 0.8
299
297
 
@@ -301,7 +299,7 @@ tools:
301
299
  name: "interrogate"
302
300
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
303
301
  parser: "InterrogateParser"
304
- priority: 11
302
+ order: 11
305
303
  warning_threshold: 0.8
306
304
  error_threshold: 0.3
307
305
 
@@ -8,7 +8,13 @@ The primary workflow is:
8
8
  # get the single most critical defect as markdown
9
9
  cq check . -o llm
10
10
  ```
11
- Outputs the top error from the first priority tool where the score < warning_threshold,. The code context is expanded if available.
11
+ Selects the single most critical defect using this priority order:
12
+
13
+ 1. **Severity** — tools with score below `error_threshold` come before those only below `warning_threshold`
14
+ 2. **Order** — among tools at the same severity, lower-order tools win (compile before lint before style)
15
+ 3. **Score** — among ties, the lower score wins
16
+
17
+ The code context is expanded if available.
12
18
  ```md
13
19
  `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
14
20
 
@@ -25,8 +31,10 @@ Please fix only this issue. After fixing, run `cq check . -o llm` to verify.
25
31
  ```
26
32
  Feed to an LLM with edit tools and repeat until there are no issues, e.g.
27
33
 
28
- ```
34
+ ```python
29
35
  cq check . -o llm | claude -p "fix this"
36
+ # or
37
+ cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
30
38
  ```
31
39
 
32
40
  ## Install
@@ -43,9 +51,9 @@ uv tool install .
43
51
 
44
52
  ## Tools
45
53
 
46
- These tools are run in **parallel**:
54
+ These tools are run in **parallel** except when looking for the first error in -o llm mode:
47
55
 
48
- | Priority | Tool | Measures |
56
+ | Order | Tool | Measures |
49
57
  |----------|------|----------|
50
58
  | 1 | compileall | Syntax errors |
51
59
  | 2 | bandit | Security vulnerabilities |
@@ -65,31 +73,15 @@ Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults
65
73
  ## Usage
66
74
 
67
75
  ```bash
68
- # LLM workflow: get the top defect as markdown (primary use case)
69
- cq check -o llm
70
-
71
- # Rich table with all metrics
72
- cq check .
73
-
74
- # Numeric score only useful in CI or scripts
75
- cq check . -o score
76
-
77
- # Full JSON output, including raw test results
78
- cq check . -o json
79
-
80
- # Explicit path
81
- cq check path/to/project/
82
- cq check path/to/file.py
83
-
84
- # Run sequentially if you like things slow
85
- cq check . --workers 1
86
-
87
- # Clear cached results before running (rarely needed)
88
- cq check . --clear-cache
89
-
90
- # Show effective tool configuration (thresholds, enabled/disabled status)
91
- cq config
92
- cq config path/to/project/
76
+ cq check . # Table overview of scores for humans
77
+ cq check -o llm # Top defect as markdown for LLMs
78
+ cq check . -o score # Numeric score only for CI
79
+ cq check . -o json # Detailed parsed JSON output for jq
80
+ cq check . -o raw # Raw tool output for debug
81
+ cq check path/to/file.py # Just one file (skips pytest and coverage)
82
+ cq check . --workers 1 # Run sequentially if you like things slow
83
+ cq check . --clear-cache # Clear cached results before running (rarely needed)
84
+ cq config path/to/project/ # Show effective tool configuration
93
85
  ```
94
86
 
95
87
  ## Table output
@@ -134,30 +126,36 @@ cq config path/to/project/
134
126
  ```
135
127
 
136
128
  ```json
137
- {
138
- "metrics": [
139
- {
140
- "metrics": {
141
- "compile": 1.0
142
- },
143
- "details": {},
144
- "raw": {
145
- "tool_name": "compile",
146
- "command": ".venv\\Scripts\\python.exe -m compileall -r 10 -j 8 . -x .*venv",
147
- "stdout": "Compiling './src/project/file.py'...",
148
- "stderr": "",
149
- "return_code": 0,
150
- "timestamp": "2026-02-19 05:03:11"
151
- },
152
- "duration_s": 0.08294440002646297
129
+ [
130
+ {
131
+ "tool_name": "compile",
132
+ "metrics": {
133
+ "compile": 1.0
153
134
  },
154
- {
155
- "metrics": {
156
- "security": 1.0
157
- }
158
- // many more lines ...
159
- }
160
- ]}
135
+ "details": {},
136
+ "duration_s": 0.05611889995634556
137
+ }
138
+ ...
139
+ ]
140
+ ```
141
+
142
+ ## Raw output
143
+ ```bash
144
+ > cq check -o raw
145
+ ```
146
+
147
+ ```json
148
+ [
149
+ {
150
+ "tool_name": "compile",
151
+ "command": "D:\\ai\\py-cq\\.venv\\Scripts\\python.exe -m compileall -r 10 -j 8 . -x .*venv",
152
+ "stdout": "",
153
+ "stderr": "",
154
+ "return_code": 0,
155
+ "timestamp": "2026-02-20 10:01:22"
156
+ }
157
+ ...
158
+ ]
161
159
  ```
162
160
 
163
161
  ## Configuration
@@ -187,7 +185,7 @@ tools:
187
185
  name: "compile"
188
186
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
189
187
  parser: "CompileParser"
190
- priority: 1
188
+ order: 1
191
189
  warning_threshold: 0.9999
192
190
  error_threshold: 0.9999
193
191
 
@@ -195,7 +193,7 @@ tools:
195
193
  name: "bandit"
196
194
  command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
197
195
  parser: "BanditParser"
198
- priority: 2
196
+ order: 2
199
197
  warning_threshold: 0.9999
200
198
  error_threshold: 0.8
201
199
 
@@ -203,7 +201,7 @@ tools:
203
201
  name: "ruff"
204
202
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
205
203
  parser: "RuffParser"
206
- priority: 3
204
+ order: 3
207
205
  warning_threshold: 0.9999
208
206
  error_threshold: 0.9
209
207
 
@@ -211,7 +209,7 @@ tools:
211
209
  name: "ty"
212
210
  command: "{python} -m ty check --output-format concise --color never {context_path}"
213
211
  parser: "TyParser"
214
- priority: 4
212
+ order: 4
215
213
  warning_threshold: 0.9999
216
214
  error_threshold: 0.8
217
215
  run_in_target_env: true
@@ -222,16 +220,16 @@ tools:
222
220
  name: "pytest"
223
221
  command: "{python} -m pytest -v {context_path}"
224
222
  parser: "PytestParser"
225
- priority: 5
223
+ order: 5
226
224
  warning_threshold: 0.7
227
225
  error_threshold: 0.5
228
226
  run_in_target_env: true
229
227
 
230
228
  coverage:
231
229
  name: "coverage"
232
- command: "{python} -m coverage run -m pytest {context_path} && {python} -m coverage report"
230
+ command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
233
231
  parser: "CoverageParser"
234
- priority: 6
232
+ order: 6
235
233
  warning_threshold: 0.9
236
234
  error_threshold: 0.5
237
235
  run_in_target_env: true
@@ -243,7 +241,7 @@ tools:
243
241
  name: "radon cc"
244
242
  command: "{python} -m radon cc --json {context_path}"
245
243
  parser: "ComplexityParser"
246
- priority: 7
244
+ order: 7
247
245
  warning_threshold: 0.6
248
246
  error_threshold: 0.4
249
247
 
@@ -251,7 +249,7 @@ tools:
251
249
  name: "radon mi"
252
250
  command: "{python} -m radon mi -s --json {context_path}"
253
251
  parser: "MaintainabilityParser"
254
- priority: 8
252
+ order: 8
255
253
  warning_threshold: 0.6
256
254
  error_threshold: 0.4
257
255
 
@@ -259,7 +257,7 @@ tools:
259
257
  name: "radon hal"
260
258
  command: "{python} -m radon hal -f --json {context_path}"
261
259
  parser: "HalsteadParser"
262
- priority: 9
260
+ order: 9
263
261
  warning_threshold: 0.5
264
262
  error_threshold: 0.3
265
263
 
@@ -267,7 +265,7 @@ tools:
267
265
  name: "vulture"
268
266
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
269
267
  parser: "VultureParser"
270
- priority: 10
268
+ order: 10
271
269
  warning_threshold: 0.9999
272
270
  error_threshold: 0.8
273
271
 
@@ -275,7 +273,7 @@ tools:
275
273
  name: "interrogate"
276
274
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
277
275
  parser: "InterrogateParser"
278
- priority: 11
276
+ order: 11
279
277
  warning_threshold: 0.8
280
278
  error_threshold: 0.3
281
279
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-code-quality"
3
- version = "0.1.7"
3
+ version = "0.1.8"
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"
@@ -43,7 +43,8 @@ app = typer.Typer(
43
43
  " cq check . # full table with all metrics (default)\n\n"
44
44
  " cq check . -o llm # top defect as markdown (primary LLM workflow)\n\n"
45
45
  " cq check . -o score # numeric score only\n\n"
46
- " cq check . -o json # full raw json of all tools"
46
+ " cq check . -o json # parsed metrics as json\n\n"
47
+ " cq check . -o raw # unprocessed tool output as json\n\n"
47
48
  " cq config . # show effective tool configuration"
48
49
  ),
49
50
  )
@@ -74,6 +75,7 @@ class OutputMode(str, Enum):
74
75
  SCORE = "score"
75
76
  JSON = "json"
76
77
  LLM = "llm"
78
+ RAW = "raw"
77
79
 
78
80
 
79
81
  @app.callback()
@@ -114,16 +116,18 @@ def check(
114
116
  effective_registry = _apply_user_config(tool_registry, load_user_config(path_obj))
115
117
  if clear_cache:
116
118
  tool_cache.clear()
117
- tool_results = run_tools(effective_registry.values(), path, workers)
118
- for tr in tool_results:
119
- log.debug(json.dumps(tr.to_dict(), indent=2))
119
+ tool_results = run_tools(effective_registry.values(), path, workers, early_exit=(output == OutputMode.LLM))
120
+ # for tr in tool_results:
121
+ # log.debug(json.dumps(tr.to_dict(), indent=2))
120
122
  combined_metrics = aggregate_metrics(path=path, metrics=tool_results)
121
123
  if output == OutputMode.SCORE:
122
124
  console.print(combined_metrics.score)
123
125
  elif output == OutputMode.JSON:
124
- console.print(json.dumps(combined_metrics.to_dict(), indent=2))
126
+ console.print(json.dumps([tr.to_dict() for tr in tool_results], indent=2))
127
+ elif output == OutputMode.RAW:
128
+ console.print(json.dumps([tr.raw.to_dict() for tr in tool_results], indent=2))
125
129
  elif output == OutputMode.LLM:
126
- log.setLevel("CRITICAL")
130
+ # log.setLevel("CRITICAL")
127
131
  from py_cq.llm_formatter import format_for_llm
128
132
  console.print(format_for_llm(effective_registry, combined_metrics))
129
133
  else:
@@ -163,18 +167,18 @@ def config(
163
167
 
164
168
  table = Table()
165
169
  table.add_column("Tool", style="cyan")
166
- table.add_column("Priority", justify="right")
170
+ table.add_column("Order", justify="right")
167
171
  table.add_column("Warning", justify="right")
168
172
  table.add_column("Error", justify="right")
169
173
  table.add_column("Status", justify="center")
170
174
 
171
- for tool_id in sorted(tool_registry, key=lambda t: tool_registry[t].priority):
175
+ for tool_id in sorted(tool_registry, key=lambda t: tool_registry[t].order):
172
176
  tc = effective_registry.get(tool_id, tool_registry[tool_id])
173
177
  is_disabled = tool_id in disabled_ids
174
178
  status = "[red]disabled[/red]" if is_disabled else "[green]enabled[/green]"
175
179
  table.add_row(
176
180
  tc.name,
177
- str(tc.priority),
181
+ str(tc.order),
178
182
  f"{tc.warning_threshold:.2f}",
179
183
  f"{tc.error_threshold:.2f}",
180
184
  status,
@@ -4,7 +4,7 @@ tools:
4
4
  name: "compile"
5
5
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
6
6
  parser: "CompileParser"
7
- priority: 1
7
+ order: 1
8
8
  warning_threshold: 0.9999
9
9
  error_threshold: 0.9999
10
10
 
@@ -12,7 +12,7 @@ tools:
12
12
  name: "bandit"
13
13
  command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
14
14
  parser: "BanditParser"
15
- priority: 2
15
+ order: 2
16
16
  warning_threshold: 0.9999
17
17
  error_threshold: 0.8
18
18
 
@@ -20,7 +20,7 @@ tools:
20
20
  name: "ruff"
21
21
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
22
22
  parser: "RuffParser"
23
- priority: 3
23
+ order: 3
24
24
  warning_threshold: 0.9999
25
25
  error_threshold: 0.9
26
26
 
@@ -28,7 +28,7 @@ tools:
28
28
  name: "ty"
29
29
  command: "{python} -m ty check --output-format concise --color never {context_path}"
30
30
  parser: "TyParser"
31
- priority: 4
31
+ order: 4
32
32
  warning_threshold: 0.9999
33
33
  error_threshold: 0.8
34
34
  run_in_target_env: true
@@ -39,16 +39,16 @@ tools:
39
39
  name: "pytest"
40
40
  command: "{python} -m pytest -v {context_path}"
41
41
  parser: "PytestParser"
42
- priority: 5
42
+ order: 5
43
43
  warning_threshold: 0.7
44
44
  error_threshold: 0.5
45
45
  run_in_target_env: true
46
46
 
47
47
  coverage:
48
48
  name: "coverage"
49
- command: "{python} -m coverage run -m pytest {context_path} && {python} -m coverage report"
49
+ command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
50
50
  parser: "CoverageParser"
51
- priority: 6
51
+ order: 6
52
52
  warning_threshold: 0.9
53
53
  error_threshold: 0.5
54
54
  run_in_target_env: true
@@ -60,7 +60,7 @@ tools:
60
60
  name: "radon cc"
61
61
  command: "{python} -m radon cc --json {context_path}"
62
62
  parser: "ComplexityParser"
63
- priority: 7
63
+ order: 7
64
64
  warning_threshold: 0.6
65
65
  error_threshold: 0.4
66
66
 
@@ -68,7 +68,7 @@ tools:
68
68
  name: "radon mi"
69
69
  command: "{python} -m radon mi -s --json {context_path}"
70
70
  parser: "MaintainabilityParser"
71
- priority: 8
71
+ order: 8
72
72
  warning_threshold: 0.6
73
73
  error_threshold: 0.4
74
74
 
@@ -76,7 +76,7 @@ tools:
76
76
  name: "radon hal"
77
77
  command: "{python} -m radon hal -f --json {context_path}"
78
78
  parser: "HalsteadParser"
79
- priority: 9
79
+ order: 9
80
80
  warning_threshold: 0.5
81
81
  error_threshold: 0.3
82
82
 
@@ -84,7 +84,7 @@ tools:
84
84
  name: "vulture"
85
85
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
86
86
  parser: "VultureParser"
87
- priority: 10
87
+ order: 10
88
88
  warning_threshold: 0.9999
89
89
  error_threshold: 0.8
90
90
 
@@ -92,6 +92,6 @@ tools:
92
92
  name: "interrogate"
93
93
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
94
94
  parser: "InterrogateParser"
95
- priority: 11
95
+ order: 11
96
96
  warning_threshold: 0.8
97
97
  error_threshold: 0.3