python-code-quality 0.1.7__py3-none-any.whl → 0.1.9__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.
@@ -8,11 +8,33 @@ process return code so downstream components can uniformly consume results
8
8
  from multiple test tools. It is part of the test-collection framework and
9
9
  enables consistent handling of pytest output across the system."""
10
10
 
11
- import re
11
+ import re as _re
12
12
 
13
13
  from py_cq.localtypes import AbstractParser, RawResult, ToolResult
14
14
 
15
15
 
16
+ def _extract_failure(stdout: str, test_name: str, max_lines: int) -> str:
17
+ """Extract the failure section for test_name from pytest stdout."""
18
+ lines = stdout.splitlines()
19
+ pattern = _re.compile(rf"_{{4,}}\s+{_re.escape(test_name)}\s+_{{4,}}")
20
+ start = None
21
+ for i, line in enumerate(lines):
22
+ if pattern.search(line):
23
+ start = i + 1
24
+ break
25
+ if start is None:
26
+ return ""
27
+ collected = []
28
+ for line in lines[start:]:
29
+ if line.strip().startswith("_") or line.strip().startswith("="):
30
+ break
31
+ collected.append(line)
32
+ if len(collected) >= max_lines:
33
+ break
34
+ text = "\n".join(collected).strip()
35
+ return f"\n```\n{text}\n```" if text else ""
36
+
37
+
16
38
  class PytestParser(AbstractParser):
17
39
  """Parses raw pytest output into a structured `ToolResult`.
18
40
 
@@ -51,14 +73,14 @@ class PytestParser(AbstractParser):
51
73
  lines = raw_result.stdout.splitlines()
52
74
  tr = ToolResult(raw=raw_result)
53
75
  if "no tests ran" in raw_result.stdout:
54
- pass
76
+ tr.metrics["tests"] = 0.0
55
77
  else:
56
78
  tests_found = dict()
57
79
  num_tests = 0
58
80
  passed_tests = 0
59
81
  for line in lines:
60
82
  # tests/test_common.py::test_name[param] PASSED [ 8%]
61
- tests_match = re.search(r"(.*\.py)::([\w\[\].,+\- ]+) (PASSED|FAILED|ERROR|SKIPPED|XFAIL|XPASS)", line)
83
+ tests_match = _re.search(r"(.*\.py)::([\w\[\].,+\- ]+) (PASSED|FAILED|ERROR|SKIPPED|XFAIL|XPASS)", line)
62
84
  if tests_match:
63
85
  test_file = tests_match.group(1)
64
86
  test_name = tests_match.group(2).strip()
@@ -67,15 +89,44 @@ class PytestParser(AbstractParser):
67
89
  num_tests += 1
68
90
  if test_status == "PASSED":
69
91
  passed_tests += 1
92
+ if num_tests == 0:
93
+ # No individual test lines found (e.g. non-verbose output);
94
+ # fall back to parsing the pytest summary line.
95
+ summary = _re.search(r"(\d+) passed(?:.*?(\d+) failed)?", raw_result.stdout)
96
+ if summary:
97
+ passed_tests = int(summary.group(1))
98
+ failed_tests = int(summary.group(2)) if summary.group(2) else 0
99
+ num_tests = passed_tests + failed_tests
70
100
  tr.metrics["tests"] = passed_tests / num_tests if num_tests else 0
71
101
  tr.details = tests_found
72
102
  return tr
73
103
 
74
- def format_llm_message(self, tr: ToolResult) -> str:
75
- """Return the first failing test as a defect description."""
104
+ def format_llm_message(self, tr: ToolResult, *, context_lines: int = 15) -> str:
105
+ """Return the first failing test with function body and failure output."""
106
+ from py_cq.parsers.common import find_function_source
76
107
  for file, tests in tr.details.items():
77
- if isinstance(tests, dict):
78
- for test_name, status in tests.items():
79
- if status == "FAILED":
80
- return f"`{file}::{test_name}` test **FAILED**"
108
+ if not isinstance(tests, dict):
109
+ continue
110
+ for test_name, status in tests.items():
111
+ if status != "FAILED":
112
+ continue
113
+ header = f"`{file}::{test_name}` — test **FAILED**"
114
+ body = find_function_source(file, test_name, max_lines=context_lines)
115
+ failure = _extract_failure(tr.raw.stdout, test_name, max_lines=context_lines)
116
+ parts = [header]
117
+ if body:
118
+ parts.append(body)
119
+ if failure:
120
+ parts.append(failure)
121
+ return "\n".join(parts)
122
+ if "no tests ran" in tr.raw.stdout:
123
+ return (
124
+ "**No tests found.** This project has no pytest test suite.\n\n"
125
+ "Add a `tests/` directory with at least one test file (e.g. `tests/test_basic.py`) "
126
+ "and write a first test covering a core function."
127
+ )
128
+ output = (tr.raw.stdout + tr.raw.stderr).strip()
129
+ if output:
130
+ tail = "\n".join(output.splitlines()[-30:])
131
+ return f"pytest reported failures:\n\n```\n{tail}\n```"
81
132
  return "pytest reported failures (no details available)"
@@ -0,0 +1,35 @@
1
+ """Parser that counts stdout lines matching a regex pattern."""
2
+
3
+ import re
4
+
5
+ from py_cq.localtypes import AbstractParser, RawResult, ToolResult
6
+ from py_cq.parsers.common import score_logistic_variant
7
+
8
+
9
+ class RegexCountParser(AbstractParser):
10
+ """Score based on the number of stdout lines matching a regex.
11
+
12
+ parser_config keys:
13
+ pattern (str, required): regex pattern to match against each line.
14
+ scale_factor (int, default 15): passed to score_logistic_variant.
15
+ """
16
+
17
+ def parse(self, raw_result: RawResult) -> ToolResult:
18
+ pattern = re.compile(self.parser_config["pattern"])
19
+ scale = self.parser_config.get("scale_factor", 15)
20
+ lines = (raw_result.stdout or "").splitlines()
21
+ matches = [ln for ln in lines if pattern.search(ln)]
22
+ count = len(matches)
23
+ score = score_logistic_variant(count, scale_factor=scale)
24
+ return ToolResult(
25
+ raw=raw_result,
26
+ metrics={"violations": score},
27
+ details={"count": count, "matches": matches},
28
+ )
29
+
30
+ def format_llm_message(self, tr: ToolResult, *, context_lines: int = 15) -> str:
31
+ matches = tr.details.get("matches", [])
32
+ if not matches:
33
+ return "No violations found"
34
+ shown = matches[:context_lines]
35
+ return "\n".join(shown)
@@ -45,7 +45,7 @@ class RuffParser(AbstractParser):
45
45
  )
46
46
  return ToolResult(raw=raw_result, metrics={"lint": score}, details=files)
47
47
 
48
- def format_llm_message(self, tr: ToolResult) -> str:
48
+ def format_llm_message(self, tr: ToolResult, *, context_lines: int = 15) -> str:
49
49
  """Return the first lint violation as a defect description."""
50
50
  if not tr.details:
51
51
  return "ruff reported issues (no details available)"
@@ -54,4 +54,4 @@ class RuffParser(AbstractParser):
54
54
  line = issue.get("line", "?")
55
55
  code = issue.get("code", "")
56
56
  message = issue.get("message", "")
57
- return f"`{file}:{line}` — **{code}**: {message}{format_source_context(file, line)}"
57
+ return f"`{file}:{line}` — **{code}**: {message}{format_source_context(file, line, count=context_lines)}"
py_cq/parsers/typarser.py CHANGED
@@ -49,7 +49,7 @@ class TyParser(AbstractParser):
49
49
  score = score_logistic_variant(weighted, scale_factor=10)
50
50
  return ToolResult(raw=raw_result, metrics={"type_check": score}, details=files)
51
51
 
52
- def format_llm_message(self, tr: ToolResult) -> str:
52
+ def format_llm_message(self, tr: ToolResult, *, context_lines: int = 15) -> str:
53
53
  """Return the first type-check diagnostic as a defect description."""
54
54
  if not tr.details:
55
55
  return "ty reported issues (no details available)"
@@ -58,4 +58,4 @@ class TyParser(AbstractParser):
58
58
  line = issue.get("line", "?")
59
59
  code = issue.get("code", "")
60
60
  message = issue.get("message", "")
61
- return f"`{file}:{line}` — **{code}**: {message}{format_source_context(file, line)}"
61
+ return f"`{file}:{line}` — **{code}**: {message}{format_source_context(file, line, count=context_lines)}"
@@ -36,7 +36,7 @@ class VultureParser(AbstractParser):
36
36
  score = score_logistic_variant(count, scale_factor=15)
37
37
  return ToolResult(raw=raw_result, metrics={"dead_code": score}, details=files)
38
38
 
39
- def format_llm_message(self, tr: ToolResult) -> str:
39
+ def format_llm_message(self, tr: ToolResult, *, context_lines: int = 15) -> str:
40
40
  if not tr.details:
41
41
  return "vulture reported issues (no details available)"
42
42
  file, issues = next(iter(tr.details.items()))
@@ -45,4 +45,4 @@ class VultureParser(AbstractParser):
45
45
  kind = issue.get("type", "unused")
46
46
  name = issue.get("name", "")
47
47
  confidence = issue.get("confidence", "?")
48
- return f"`{file}:{line}` — **{kind}** `{name}` ({confidence}% confidence){format_source_context(file, line)}"
48
+ return f"`{file}:{line}` — **{kind}** `{name}` ({confidence}% confidence){format_source_context(file, line, count=context_lines)}"
py_cq/tool_registry.py CHANGED
@@ -9,26 +9,27 @@ from py_cq.localtypes import ToolConfig
9
9
 
10
10
 
11
11
  def load_tool_configs() -> dict[str, ToolConfig]:
12
- """Load tool configurations from the bundled tools.yaml and return a registry.
12
+ """Load tool configurations from the bundled config.yaml and return a registry.
13
13
 
14
14
  Returns:
15
15
  dict[str, ToolConfig]: A mapping from tool ID to its configuration instance."""
16
- yaml_text = files("py_cq.config").joinpath("tools.yaml").read_text(encoding="utf-8")
16
+ yaml_text = files("py_cq.config").joinpath("config.yaml").read_text(encoding="utf-8")
17
17
  config = yaml.safe_load(yaml_text)
18
18
  registry = {}
19
- for tool_id, tool_data in config["tools"].items():
19
+ for tool_id, tool_data in config["python"].items():
20
20
  # Dynamically import parser class
21
21
  module = import_module(f"py_cq.parsers.{tool_data['parser'].lower()}")
22
22
  parser_class = getattr(module, tool_data["parser"])
23
23
  registry[tool_id] = ToolConfig(
24
- name=tool_data["name"],
24
+ name=tool_id,
25
25
  command=tool_data["command"],
26
26
  parser_class=parser_class,
27
- priority=tool_data["priority"],
27
+ order=tool_data["order"],
28
28
  warning_threshold=tool_data["warning_threshold"],
29
29
  error_threshold=tool_data["error_threshold"],
30
30
  run_in_target_env=tool_data.get("run_in_target_env", False),
31
31
  extra_deps=tool_data.get("extra_deps", []),
32
+ parser_config=tool_data.get("parser_config", {}),
32
33
  )
33
34
  return registry
34
35
 
@@ -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.9
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
@@ -28,14 +28,16 @@ Description-Content-Type: text/markdown
28
28
 
29
29
  Feed the results from 11+ code quality tools to an LLM. Minimal tokens.
30
30
 
31
+ Why? It removes the mental burden of understanding all these tools and parsing their results.
32
+
31
33
  The primary workflow is:
32
34
 
33
35
  ```bash
34
36
  # get the single most critical defect as markdown
35
37
  cq check . -o llm
36
38
  ```
37
- Outputs the top error from the first priority tool where the score < warning_threshold,. The code context is expanded if available.
38
- ```md
39
+
40
+ ```python
39
41
  `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
40
42
 
41
43
  18: min_dist = float("inf")
@@ -51,10 +53,13 @@ Please fix only this issue. After fixing, run `cq check . -o llm` to verify.
51
53
  ```
52
54
  Feed to an LLM with edit tools and repeat until there are no issues, e.g.
53
55
 
54
- ```
56
+ ```python
55
57
  cq check . -o llm | claude -p "fix this"
58
+ # or
59
+ cq check . -o llm | ollama run gpt-oss:20b "Explain how to fix this"
56
60
  ```
57
61
 
62
+
58
63
  ## Install
59
64
 
60
65
  ```bash
@@ -62,21 +67,22 @@ cq check . -o llm | claude -p "fix this"
62
67
  uv tool install python-code-quality
63
68
 
64
69
  # or, clone it then install
65
- git pull https://github.com/rhiza-fr/py-cq.git
70
+ git clone https://github.com/rhiza-fr/py-cq.git
66
71
  cd py-cq
67
72
  uv tool install .
68
73
  ```
69
74
 
70
75
  ## Tools
71
76
 
72
- These tools are run in **parallel**:
77
+ These tools are run in **parallel** except:
78
+ When running '-o llm', we run sequentially and exit early at the first error.
73
79
 
74
- | Priority | Tool | Measures |
80
+ | Order | Tool | Measures |
75
81
  |----------|------|----------|
76
82
  | 1 | compileall | Syntax errors |
77
- | 2 | bandit | Security vulnerabilities |
78
- | 3 | ruff | Lint / style |
79
- | 4 | ty | Type errors |
83
+ | 2 | ruff | Lint / style |
84
+ | 3 | ty | Type errors |
85
+ | 4 | bandit | Security vulnerabilities |
80
86
  | 5 | pytest | Test pass rate |
81
87
  | 6 | coverage | Test coverage |
82
88
  | 7 | radon cc | Cyclomatic complexity |
@@ -85,37 +91,28 @@ These tools are run in **parallel**:
85
91
  | 10 | vulture | Dead code |
86
92
  | 11 | interrogate | Docstring coverage |
87
93
 
88
- Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle
94
+ Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
89
95
 
90
96
 
91
97
  ## Usage
92
98
 
93
99
  ```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
100
+ cq check . # Table overview of scores for humans
101
+ cq check . -o llm # Top defect as markdown for LLMs
102
+ cq check . -o score # Numeric score only for CI
103
+ cq check . -o json # Detailed parsed JSON output for jq
104
+ cq check . -o raw # Raw tool output for debug
105
+ cq check path/to/file.py # Just one file (skips pytest and coverage)
106
+ cq check . --workers 1 # Run sequentially if you like things slow
107
+ cq check . --clear-cache # Clear cached results before running (rarely needed)
108
+ cq config path/to/project/ # Show effective tool configuration
109
+ ```
112
110
 
113
- # Clear cached results before running (rarely needed)
114
- cq check . --clear-cache
111
+ **Exit codes:** `cq check` exits with code `1` if any tool metric falls below its `error_threshold`, making it suitable as a CI gate:
115
112
 
116
- # Show effective tool configuration (thresholds, enabled/disabled status)
117
- cq config
118
- cq config path/to/project/
113
+ ```bash
114
+ cq check . && deploy # block deploy on errors
115
+ cq check . -o score # print score, exit 1 on errors
119
116
  ```
120
117
 
121
118
  ## Table output
@@ -129,9 +126,9 @@ cq config path/to/project/
129
126
  ┃ Tool ┃ Time ┃ Metric ┃ Score ┃ Status ┃
130
127
  ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━┩
131
128
  │ compile │ 0.42s │ compile │ 1.000 │ OK │
132
- │ bandit │ 0.56s │ security │ 1.000 │ OK │
133
129
  │ ruff │ 0.17s │ lint │ 1.000 │ OK │
134
130
  │ ty │ 0.33s │ type_check │ 1.000 │ OK │
131
+ │ bandit │ 0.56s │ security │ 1.000 │ OK │
135
132
  │ pytest │ 0.91s │ tests │ 1.000 │ OK │
136
133
  │ coverage │ 1.26s │ coverage │ 0.910 │ OK │
137
134
  │ radon cc │ 0.32s │ simplicity │ 0.982 │ OK │
@@ -160,30 +157,36 @@ cq config path/to/project/
160
157
  ```
161
158
 
162
159
  ```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
160
+ [
161
+ {
162
+ "tool_name": "compile",
163
+ "metrics": {
164
+ "compile": 1.0
179
165
  },
180
- {
181
- "metrics": {
182
- "security": 1.0
183
- }
184
- // many more lines ...
185
- }
186
- ]}
166
+ "details": {},
167
+ "duration_s": 0.05611889995634556
168
+ }
169
+ ...
170
+ ]
171
+ ```
172
+
173
+ ## Raw output
174
+ ```bash
175
+ > cq check . -o raw
176
+ ```
177
+
178
+ ```json
179
+ [
180
+ {
181
+ "tool_name": "compile",
182
+ "command": "D:\\ai\\py-cq\\.venv\\Scripts\\python.exe -m compileall -r 10 -j 8 . -x .*venv",
183
+ "stdout": "",
184
+ "stderr": "",
185
+ "return_code": 0,
186
+ "timestamp": "2026-02-20 10:01:22"
187
+ }
188
+ ...
189
+ ]
187
190
  ```
188
191
 
189
192
  ## Configuration
@@ -195,69 +198,66 @@ Add a `[tool.cq]` section to your project's `pyproject.toml`:
195
198
  # Skip tools that are slow or not relevant to your project
196
199
  disable = ["coverage", "interrogate"]
197
200
 
201
+ # Lines of source context shown around each defect in LLM output (default: 15)
202
+ context_lines = 15
203
+
198
204
  # Override warning/error thresholds per tool
199
205
  [tool.cq.thresholds.coverage]
200
206
  warning = 0.9
201
207
  error = 0.7
202
208
  ```
203
209
 
204
- Tool IDs match the keys in `config/tools.yaml`: `compilation`, `bandit`, `ruff`, `ty`, `pytest`, `coverage`, `complexity`, `maintainability`, `halstead`, `vulture`, `interrogate`.
210
+ Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
205
211
 
206
212
 
207
213
  ### Default config
208
214
 
209
215
  ```yaml
210
- tools:
216
+ python:
211
217
 
212
- compilation:
213
- name: "compile"
218
+ compile:
214
219
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
215
220
  parser: "CompileParser"
216
- priority: 1
221
+ order: 1
217
222
  warning_threshold: 0.9999
218
223
  error_threshold: 0.9999
219
224
 
220
- bandit:
221
- name: "bandit"
222
- 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
- parser: "BanditParser"
224
- priority: 2
225
- warning_threshold: 0.9999
226
- error_threshold: 0.8
227
-
228
225
  ruff:
229
- name: "ruff"
230
226
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
231
227
  parser: "RuffParser"
232
- priority: 3
228
+ order: 2
233
229
  warning_threshold: 0.9999
234
230
  error_threshold: 0.9
235
231
 
236
232
  ty:
237
- name: "ty"
238
233
  command: "{python} -m ty check --output-format concise --color never {context_path}"
239
234
  parser: "TyParser"
240
- priority: 4
235
+ order: 3
241
236
  warning_threshold: 0.9999
242
237
  error_threshold: 0.8
243
238
  run_in_target_env: true
244
239
  extra_deps:
245
240
  - ty
246
241
 
242
+ bandit:
243
+ command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
244
+ parser: "BanditParser"
245
+ order: 4
246
+ warning_threshold: 0.9999
247
+ error_threshold: 0.8
248
+
247
249
  pytest:
248
- name: "pytest"
249
250
  command: "{python} -m pytest -v {context_path}"
250
251
  parser: "PytestParser"
251
- priority: 5
252
- warning_threshold: 0.7
253
- error_threshold: 0.5
252
+ order: 5
253
+ warning_threshold: 1.0
254
+ error_threshold: 1.0
254
255
  run_in_target_env: true
255
256
 
256
257
  coverage:
257
- name: "coverage"
258
- command: "{python} -m coverage run -m pytest {context_path} && {python} -m coverage report"
258
+ command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
259
259
  parser: "CoverageParser"
260
- priority: 6
260
+ order: 6
261
261
  warning_threshold: 0.9
262
262
  error_threshold: 0.5
263
263
  run_in_target_env: true
@@ -265,43 +265,38 @@ tools:
265
265
  - coverage
266
266
  - pytest
267
267
 
268
- complexity:
269
- name: "radon cc"
268
+ radon-cc:
270
269
  command: "{python} -m radon cc --json {context_path}"
271
270
  parser: "ComplexityParser"
272
- priority: 7
271
+ order: 7
273
272
  warning_threshold: 0.6
274
273
  error_threshold: 0.4
275
274
 
276
- maintainability:
277
- name: "radon mi"
275
+ 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
 
284
- halstead:
285
- name: "radon hal"
282
+ radon-hal:
286
283
  command: "{python} -m radon hal -f --json {context_path}"
287
284
  parser: "HalsteadParser"
288
- priority: 9
285
+ order: 9
289
286
  warning_threshold: 0.5
290
287
  error_threshold: 0.3
291
288
 
292
289
  vulture:
293
- name: "vulture"
294
290
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
295
291
  parser: "VultureParser"
296
- priority: 10
292
+ order: 10
297
293
  warning_threshold: 0.9999
298
294
  error_threshold: 0.8
299
295
 
300
296
  interrogate:
301
- name: "interrogate"
302
297
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
303
298
  parser: "InterrogateParser"
304
- priority: 11
299
+ order: 11
305
300
  warning_threshold: 0.8
306
301
  error_threshold: 0.3
307
302
 
@@ -0,0 +1,34 @@
1
+ py_cq/__init__.py,sha256=rS7kf1RU1zZskvJlkbZaMqEpdRYPspwDPAFbxzF3tXg,373
2
+ py_cq/cli.py,sha256=wu1GlxSDxS835i9-mO4-xmyBLfr6puU-ES-26T7Mty0,11007
3
+ py_cq/context_hash.py,sha256=h-i7Rhd7AUfLv9SkQvE79bjJvTsm_ZwoVwSmUKXWmfM,2977
4
+ py_cq/execution_engine.py,sha256=tgNGFOO3h-EyetCEzC_RS2K-b9OkOFpOwGwrEAHIpZA,7477
5
+ py_cq/language_detector.py,sha256=6av5HaimcZ54RkN69xQmGgC0mxtTvGzPV3SL8NGG8Uc,1116
6
+ py_cq/llm_formatter.py,sha256=l74O5iqNWMR16789Xncoi93xOqgQJhW5IQK9_OyA9mE,1632
7
+ py_cq/localtypes.py,sha256=jCX1ZuwTixQooEk2N1Gi1XRuDcdBc2NlOdQBKysEybk,6128
8
+ py_cq/main.py,sha256=VKoXI8R8rB2fEROBYoTVURfinLqyh8XTNIIWAOtH7dw,380
9
+ py_cq/metric_aggregator.py,sha256=M2ymo62S7p7qPUqjjoiPg4IVyXQhLMuTr9-jxLiFjCY,853
10
+ py_cq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ py_cq/tool_registry.py,sha256=oMEkFHkU3gg5UpeGD4zHtynOYmWieRgDN5kTwZ5KsE8,1584
12
+ py_cq/config/__init__.py,sha256=f0wc51O_3kGDTZUnCbGv8_zWnC5yYGl4NWcf2buSImQ,670
13
+ py_cq/config/config.yaml,sha256=R8CjZH8erBQSbJFKgHt-CCwD1Mgj6ZCGQ3OtWH0Ebig,2402
14
+ py_cq/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ py_cq/parsers/banditparser.py,sha256=Ju_CkuXtkVn1Th9aQ6mv6fTUTpb9pc1YD8Nzt_nMgFQ,2326
16
+ py_cq/parsers/common.py,sha256=thbziNjqaf-SY3Y0W-KmPwzlcC-YCV98Z8jubUJM7_4,5203
17
+ py_cq/parsers/compileparser.py,sha256=YsT7ePUDRjsUHTLqgRv4x6jJBCTt6cbm1mgxplzxETg,6256
18
+ py_cq/parsers/complexityparser.py,sha256=2t1-wmNjUu65fULcIm5jcgv7ZLWwjakyiY_r-Fx1QQg,3983
19
+ py_cq/parsers/coverageparser.py,sha256=n4h_RCxKvJoYgp356PA4laAmFcngjQ41b_GXZvKkVgc,4041
20
+ py_cq/parsers/exitcodeparser.py,sha256=ZFL3EbPhGvFjm2qZhfLD1_5dNjR9ULYNQpKgy0Z_dGo,729
21
+ py_cq/parsers/halsteadparser.py,sha256=ZCN7LP1iUZ91tf3tlspKAPQrTEa78XVdE9Fjn9yPvYk,9008
22
+ py_cq/parsers/interrogateparser.py,sha256=G07beMaGR0Mq-pGVozAbwK0xOx9MdMaNVGq2CGrUb7w,2225
23
+ py_cq/parsers/linecountparser.py,sha256=nxWvFntuR8T6FDGpafrlevvt-jR3rwkbVo_t2JX4TF0,1067
24
+ py_cq/parsers/maintainabilityparser.py,sha256=Ax0ZFA6zzqYIWZH1hP1_GUtdVn2LIJ8SKWtqVNdszYs,3411
25
+ py_cq/parsers/pytestparser.py,sha256=3N1X4wKhJ2h-2U1GuM3gHWHpHxU9f0LPGcPaojra9W4,6377
26
+ py_cq/parsers/regexcountparser.py,sha256=KSoNh2spucXU06pxxr2QW0LrPLfJkFMAsmSgjooaFv0,1316
27
+ py_cq/parsers/ruffparser.py,sha256=ZdIya4sct2PrsOyfKfMGmXHLh3Qu7HtFqXNY9IuNFog,2275
28
+ py_cq/parsers/typarser.py,sha256=zrI0KS65MUGPxYPP74B6BRyTbjcPhKyQPjH2KZIyxN0,2495
29
+ py_cq/parsers/vultureparser.py,sha256=U6zC7P0ATA_N4SB90BahKF5QHMITf_z-NsOgrh_Q5rA,1995
30
+ python_code_quality-0.1.9.dist-info/METADATA,sha256=MtSk0DgDh6bkeATKhz03Y8thLZvxxuOcYlvfvOmHkD8,10149
31
+ python_code_quality-0.1.9.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
32
+ python_code_quality-0.1.9.dist-info/entry_points.txt,sha256=j5Q_gGr0b7389lddt1JlZ7gcL4Z7RHxuDhij_G-IhBY,39
33
+ python_code_quality-0.1.9.dist-info/licenses/LICENSE,sha256=Bpuh8tbf37so8M5NtRGTLmT5ue7diJ17223L9f9nsT0,1086
34
+ python_code_quality-0.1.9.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.29.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any