python-code-quality 0.1.11__py3-none-any.whl → 0.1.14__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.
py_cq/__init__.py CHANGED
@@ -4,7 +4,3 @@ The module defines a single function, `hello`, which returns the string
4
4
  `'Hello from py_cq!'`. It can serve as a minimal example, placeholder, or
5
5
  testing stub in larger applications."""
6
6
 
7
-
8
- def hello() -> str:
9
- """Returns the greeting string `'Hello from py_cq!'`."""
10
- return "Hello from py_cq!"
py_cq/cli.py CHANGED
@@ -84,6 +84,7 @@ def _apply_user_config(base: dict[str, ToolConfig], user_cfg: dict) -> dict[str,
84
84
  run_in_target_env=tool_data.get("run_in_target_env", False),
85
85
  extra_deps=tool_data.get("extra_deps", []),
86
86
  parser_config=tool_data.get("parser_config", {}),
87
+ exclude_format=tool_data.get("exclude_format", ""),
87
88
  )
88
89
  except KeyError as e:
89
90
  raise typer.BadParameter(f"[tool.cq.tools.{tool_id}] missing required field {e}")
@@ -125,6 +126,15 @@ def check(
125
126
  language: str | None = typer.Option(
126
127
  None, "--language", "-l", help="Override language detection (e.g. python, typescript, rust)"
127
128
  ),
129
+ only: str | None = typer.Option(
130
+ None, "--only", help="Comma-separated tool IDs to run (e.g. ruff,ty,pytest)"
131
+ ),
132
+ skip: str | None = typer.Option(
133
+ None, "--skip", help="Comma-separated tool IDs to skip (e.g. bandit,vulture)"
134
+ ),
135
+ exclude: str | None = typer.Option(
136
+ None, "--exclude", help="Comma-separated paths to exclude (e.g. demo,docs)"
137
+ ),
128
138
  ):
129
139
  """Feed the results from 11+ code quality tools to an LLM. Try: cq check . -o llm""" # --help
130
140
  path_obj = Path(path)
@@ -153,18 +163,27 @@ def check(
153
163
  user_cfg = load_user_config(path_obj)
154
164
  context_lines: int = int(user_cfg.get("context_lines", 15))
155
165
  effective_registry = _apply_user_config(tool_registry, user_cfg)
166
+ if only:
167
+ keep = set(only.split(","))
168
+ effective_registry = {k: v for k, v in effective_registry.items() if k in keep}
169
+ if skip:
170
+ drop = set(skip.split(","))
171
+ effective_registry = {k: v for k, v in effective_registry.items() if k not in drop}
172
+ config_excludes: list[str] = user_cfg.get("exclude", [])
173
+ cli_excludes: list[str] = [e.strip() for e in exclude.split(",")] if exclude else []
174
+ excludes = list(dict.fromkeys(config_excludes + cli_excludes))
156
175
  if clear_cache:
157
176
  tool_cache.clear()
158
- tool_results = run_tools(effective_registry.values(), path, workers, early_exit=(output == OutputMode.LLM))
177
+ tool_results = run_tools(effective_registry.values(), path, workers, early_exit=(output == OutputMode.LLM), excludes=excludes)
159
178
  # for tr in tool_results:
160
179
  # log.debug(json.dumps(tr.to_dict(), indent=2))
161
180
  combined_metrics = aggregate_metrics(path=path, metrics=tool_results)
162
181
  if output == OutputMode.SCORE:
163
182
  console.print(combined_metrics.score)
164
183
  elif output == OutputMode.JSON:
165
- console.print(json.dumps([tr.to_dict() for tr in tool_results], indent=2))
184
+ print(json.dumps([tr.to_dict() for tr in tool_results], indent=2))
166
185
  elif output == OutputMode.RAW:
167
- console.print(json.dumps([tr.raw.to_dict() for tr in tool_results], indent=2))
186
+ print(json.dumps([tr.raw.to_dict() for tr in tool_results], indent=2))
168
187
  elif output == OutputMode.LLM:
169
188
  # log.setLevel("CRITICAL")
170
189
  from py_cq.llm_formatter import format_for_llm
py_cq/config/config.yaml CHANGED
@@ -8,14 +8,16 @@ python:
8
8
  error_threshold: 0.9999
9
9
 
10
10
  ruff:
11
- command: "{python} -m ruff check --output-format concise --no-cache \"{context_path}\""
11
+ command: "{python} -m ruff check --output-format concise --no-cache \"{context_path}\"{exclude}"
12
+ exclude_format: " --exclude {path}"
12
13
  parser: "RuffParser"
13
14
  order: 2
14
15
  warning_threshold: 0.9999
15
16
  error_threshold: 0.9
16
17
 
17
18
  ty:
18
- command: "{python} -m ty check --output-format concise --color never \"{context_path}\""
19
+ command: "{python} -m ty check --output-format concise --color never \"{context_path}\"{exclude}"
20
+ exclude_format: " --exclude {path}"
19
21
  parser: "TyParser"
20
22
  order: 3
21
23
  warning_threshold: 0.9999
@@ -25,14 +27,16 @@ python:
25
27
  - ty
26
28
 
27
29
  bandit:
28
- command: "{python} -m bandit -r \"{context_path}\" -f json -q -s B101 --severity-level medium --exclude \"{input_path_posix}/.venv,{input_path_posix}/tests\""
30
+ 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}\""
31
+ exclude_format: ",{input_path_posix}/{path}"
29
32
  parser: "BanditParser"
30
33
  order: 4
31
34
  warning_threshold: 0.9999
32
35
  error_threshold: 0.8
33
36
 
34
37
  pytest:
35
- command: "{python} -m pytest -v \"{context_path}\""
38
+ command: "{python} -m pytest -v \"{context_path}\"{exclude}"
39
+ exclude_format: " --ignore {path}"
36
40
  parser: "PytestParser"
37
41
  order: 5
38
42
  warning_threshold: 1.0
@@ -74,14 +78,16 @@ python:
74
78
  error_threshold: 0.3
75
79
 
76
80
  vulture:
77
- command: "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
81
+ command: "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git{exclude}"
82
+ exclude_format: ",{path}"
78
83
  parser: "VultureParser"
79
84
  order: 10
80
85
  warning_threshold: 0.9999
81
86
  error_threshold: 0.8
82
87
 
83
88
  interrogate:
84
- command: "{python} -m interrogate \"{context_path}\" -v --fail-under 0"
89
+ command: "{python} -m interrogate \"{context_path}\" -e tests{exclude} -v --fail-under 0"
90
+ exclude_format: " -e {path}"
85
91
  parser: "InterrogateParser"
86
92
  order: 11
87
93
  warning_threshold: 0.8
py_cq/execution_engine.py CHANGED
@@ -41,7 +41,29 @@ def _find_project_root(path: Path) -> Path | None:
41
41
  return None
42
42
 
43
43
 
44
- def run_tool(tool_config: ToolConfig, context_path: str) -> RawResult:
44
+ def _dep_in_venv(dep: str, project_root: Path) -> bool:
45
+ """Return True if `dep` is installed in the project's .venv."""
46
+ venv = project_root / ".venv"
47
+ if not venv.exists():
48
+ return False
49
+ for subdir in ("Scripts", "bin"):
50
+ for suffix in ("", ".exe", ".cmd"):
51
+ if (venv / subdir / f"{dep}{suffix}").exists():
52
+ return True
53
+ return False
54
+
55
+
56
+ def _build_exclude_str(exclude_format: str, excludes: list[str], **extra_vars: str) -> str:
57
+ if not exclude_format or not excludes:
58
+ return ""
59
+ parts = []
60
+ for exc in excludes:
61
+ abs_posix_path = Path(exc).resolve().as_posix()
62
+ parts.append(exclude_format.format(path=exc, abs_posix_path=abs_posix_path, **extra_vars))
63
+ return "".join(parts)
64
+
65
+
66
+ def run_tool(tool_config: ToolConfig, context_path: str, excludes: list[str] | None = None) -> RawResult:
45
67
  """Runs a tool defined by its configuration and returns the execution result.
46
68
 
47
69
  Args:
@@ -60,7 +82,7 @@ def run_tool(tool_config: ToolConfig, context_path: str) -> RawResult:
60
82
  >>> result.return_code
61
83
  0"""
62
84
  python = sys.executable
63
- path = context_path
85
+ path = str(Path(context_path))
64
86
  if tool_config.run_in_target_env:
65
87
  uv = shutil.which("uv")
66
88
  if uv:
@@ -72,11 +94,15 @@ def run_tool(tool_config: ToolConfig, context_path: str) -> RawResult:
72
94
  project_root = _find_project_root(resolved)
73
95
  abs_dir = str(project_root) if project_root else str(resolved.parent)
74
96
  path = str(resolved)
75
- with_flags = " ".join(f"--with {dep}" for dep in tool_config.extra_deps)
76
- python = f'"{uv}" run --directory "{abs_dir}" {with_flags}'.rstrip()
97
+ project_root_path = Path(abs_dir)
98
+ missing_deps = [d for d in tool_config.extra_deps if not _dep_in_venv(d, project_root_path)]
99
+ with_flags = " ".join(f"--with {dep}" for dep in missing_deps)
100
+ no_sync = "--no-sync" if sys.executable.startswith(abs_dir) else ""
101
+ python = f'"{uv}" run {no_sync} --directory "{abs_dir}" {with_flags}'.strip()
77
102
  abs_context_path = str(Path(context_path).resolve())
78
103
  input_path_posix = Path(context_path).as_posix().rstrip("/")
79
- command = tool_config.command.format(context_path=path, abs_context_path=abs_context_path, input_path_posix=input_path_posix, python=python)
104
+ exclude = _build_exclude_str(tool_config.exclude_format, excludes or [], input_path_posix=input_path_posix)
105
+ command = tool_config.command.format(context_path=path, abs_context_path=abs_context_path, input_path_posix=input_path_posix, python=python, exclude=exclude)
80
106
  cache_key = f"{command}:{get_context_hash(context_path)}"
81
107
  if cache_key in _cache:
82
108
  log.info(f"Cache hit: {command}")
@@ -96,7 +122,7 @@ def run_tool(tool_config: ToolConfig, context_path: str) -> RawResult:
96
122
  return raw_result
97
123
 
98
124
 
99
- def run_tools(tool_configs: Collection[ToolConfig], path: str, max_workers: int = 0, early_exit: bool = False) -> list[ToolResult]:
125
+ def run_tools(tool_configs: Collection[ToolConfig], path: str, max_workers: int = 0, early_exit: bool = False, excludes: list[str] | None = None) -> list[ToolResult]:
100
126
  """Run multiple tools and return their parsed results.
101
127
 
102
128
  Runs each tool specified in *tool_configs* on the file or directory at
@@ -138,7 +164,7 @@ def run_tools(tool_configs: Collection[ToolConfig], path: str, max_workers: int
138
164
  >>> results = run_tools(configs, '/path/to/project', parallel=True)"""
139
165
  def _run_and_parse(tool_config: ToolConfig) -> tuple[int, ToolResult]:
140
166
  t0 = time.perf_counter()
141
- raw_result = run_tool(tool_config, path)
167
+ raw_result = run_tool(tool_config, path, excludes)
142
168
  tr = tool_config.parser_class(tool_config.parser_config).parse(raw_result)
143
169
  tr.duration_s = time.perf_counter() - t0
144
170
  return tool_config.order, tr
py_cq/localtypes.py CHANGED
@@ -22,6 +22,7 @@ class ToolConfig:
22
22
  run_in_target_env: bool = False # If True, run in target project's env via uv
23
23
  extra_deps: list[str] = field(default_factory=list) # Extra deps to inject via uv --with
24
24
  parser_config: dict[str, Any] = field(default_factory=dict)
25
+ exclude_format: str = "" # Per-path template for --exclude injection, e.g. " --exclude {path}"
25
26
 
26
27
 
27
28
  @dataclass
py_cq/parsers/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ """Tool Response parsers"""
@@ -3,8 +3,8 @@
3
3
  Interrogate is invoked with ``-v --fail-under 0``, producing a table of
4
4
  per-file docstring coverage on stdout::
5
5
 
6
- | src/foo.py | 5 | 2 | 60% |
7
- | TOTAL | 5 | 2 | 60% |
6
+ | src/foo.py | 5 | 2 | 3 | 60% |
7
+ | TOTAL | 5 | 2 | 3 | 60.0% |
8
8
 
9
9
  The parser extracts per-file coverage and the TOTAL row, storing the TOTAL
10
10
  as the ``doc_coverage`` metric (0.0–1.0).
@@ -14,7 +14,7 @@ import re
14
14
 
15
15
  from py_cq.localtypes import AbstractParser, RawResult, ToolResult
16
16
 
17
- _ROW_RE = re.compile(r"^\|\s+(.+?)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)%\s+\|")
17
+ _ROW_RE = re.compile(r"^\|\s+(.+?)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+\d+\s+\|\s+(\d+(?:\.\d+)?)%\s*\|")
18
18
 
19
19
 
20
20
  class InterrogateParser(AbstractParser):
@@ -30,7 +30,7 @@ class InterrogateParser(AbstractParser):
30
30
  name = m.group(1).strip()
31
31
  total = int(m.group(2))
32
32
  miss = int(m.group(3))
33
- cover = int(m.group(4))
33
+ cover = float(m.group(4))
34
34
  if name == "TOTAL":
35
35
  total_coverage = cover / 100.0
36
36
  elif total > 0:
py_cq/tool_registry.py CHANGED
@@ -30,6 +30,7 @@ def load_tool_configs() -> dict[str, ToolConfig]:
30
30
  run_in_target_env=tool_data.get("run_in_target_env", False),
31
31
  extra_deps=tool_data.get("extra_deps", []),
32
32
  parser_config=tool_data.get("parser_config", {}),
33
+ exclude_format=tool_data.get("exclude_format", ""),
33
34
  )
34
35
  return registry
35
36
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-code-quality
3
- Version: 0.1.11
3
+ Version: 0.1.14
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>
@@ -31,43 +31,19 @@ Description-Content-Type: text/markdown
31
31
 
32
32
  [![CI](https://img.shields.io/github/actions/workflow/status/rhiza-fr/py-cq/ci.yml?label=CI)](https://github.com/rhiza-fr/py-cq/actions/workflows/ci.yml)
33
33
  [![codecov](https://codecov.io/gh/rhiza-fr/py-cq/graph/badge.svg)](https://codecov.io/gh/rhiza-fr/py-cq)
34
- [![PyPI version](https://img.shields.io/pypi/v/python-code-quality)](https://pypi.org/project/python-code-quality/)
35
- [![Python versions](https://img.shields.io/pypi/pyversions/python-code-quality)](https://pypi.org/project/python-code-quality/)
34
+ [![PyPI version](https://img.shields.io/pypi/v/python-code-quality?)](https://pypi.org/project/python-code-quality/)
35
+ [![Python versions](https://img.shields.io/pypi/pyversions/python-code-quality?)](https://pypi.org/project/python-code-quality/)
36
36
  [![License](https://img.shields.io/github/license/rhiza-fr/py-cq)](LICENSE)
37
37
 
38
- Feed the results from 11+ code quality tools to an LLM. Minimal tokens.
39
-
40
- Why? It removes the mental burden of understanding all these tools and parsing their results.
41
-
42
- The primary workflow is:
38
+ Run 11+ code quality tools, aggregate results into one score, and surface the single most critical defect as a focused markdown prompt — ready to pipe to any LLM.
43
39
 
44
40
  ```bash
45
- # get the single most critical defect as markdown
46
- cq check . -o llm
47
- ```
48
-
49
- ```python
50
- `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
51
-
52
- 18: min_dist = float("inf")
53
- 19: nearest_city = None
54
- 20: for city in cities:
55
- 21: unused_variable = 67
56
- 22: dist = calc_dist(current_city, city)
57
- 23: if dist < min_dist:
58
- 24: min_dist = dist
59
- 25: nearest_city = city
60
-
61
- Please fix only this issue. After fixing, run `cq check . -o llm` to verify.
62
- ```
63
- Feed to an LLM with edit tools and repeat until there are no issues, e.g.
64
-
65
- ```python
66
- cq check . -o llm | claude -p "fix this"
67
- # or
68
- cq check . -o llm | ollama run gpt-oss:20b "Explain how to fix this"
41
+ cq check . -o llm # top defect as markdown, pipe to an LLM
42
+ cq check . # table overview of all scores
43
+ cq check . -o score # numeric score only, exits 1 on errors (CI gate)
69
44
  ```
70
45
 
46
+ ![cq demo](demo/output/demo.gif)
71
47
 
72
48
  ## Install
73
49
 
@@ -112,6 +88,9 @@ cq check . -o score # Numeric score only for CI
112
88
  cq check . -o json # Detailed parsed JSON output for jq
113
89
  cq check . -o raw # Raw tool output for debug
114
90
  cq check path/to/file.py # Just one file (skips pytest and coverage)
91
+ cq check . --only ruff,ty # Run only specific tools
92
+ cq check . --skip bandit # Skip specific tools
93
+ cq check . --exclude demo # Exclude paths from all tools
115
94
  cq check . --workers 1 # Run sequentially if you like things slow
116
95
  cq check . --clear-cache # Clear cached results before running (rarely needed)
117
96
  cq config path/to/project/ # Show effective tool configuration
@@ -131,10 +110,17 @@ Add a stop hook to your project's `.claude/settings.json` so Claude automaticall
131
110
  ```json
132
111
  {
133
112
  "hooks": {
134
- "Stop": [{
135
- "matcher": "",
136
- "hooks": [{"type": "command", "command": "cq check . -o score && echo 'CQ: all clear' || cq check . -o llm"}]
137
- }]
113
+ "Stop": [
114
+ {
115
+ "matcher": "",
116
+ "hooks": [
117
+ {
118
+ "type": "command",
119
+ "command": "cq check . -o score && echo 'CQ: all clear' || cq check . -o llm; true"
120
+ }
121
+ ]
122
+ }
123
+ ]
138
124
  }
139
125
  }
140
126
  ```
@@ -231,6 +217,16 @@ Then invoke it with `/cq-fix` in Claude Code. The `$(...)` embeds the live `cq`
231
217
  ]
232
218
  ```
233
219
 
220
+ Both `json` and `raw` output pipe cleanly to `jq`:
221
+
222
+ ```bash
223
+ # Get the coverage section
224
+ cq check . -o raw | jq '.[] | select(.tool_name == "coverage")'
225
+
226
+ # Get parsed coverage metrics only
227
+ cq check . -o json | jq '.[] | select(.tool_name == "coverage") | .metrics'
228
+ ```
229
+
234
230
  ## Configuration
235
231
 
236
232
  Add a `[tool.cq]` section to your project's `pyproject.toml`:
@@ -240,6 +236,9 @@ Add a `[tool.cq]` section to your project's `pyproject.toml`:
240
236
  # Skip tools that are slow or not relevant to your project
241
237
  disable = ["coverage", "interrogate"]
242
238
 
239
+ # Exclude paths from all tools (merged with --exclude CLI flag)
240
+ exclude = ["demo", "docs"]
241
+
243
242
  # Lines of source context shown around each defect in LLM output (default: 15)
244
243
  context_lines = 15
245
244
 
@@ -258,21 +257,23 @@ Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandi
258
257
  python:
259
258
 
260
259
  compile:
261
- command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
260
+ command: "{python} -m compileall -r 10 -j 8 \"{context_path}\" -x .*venv"
262
261
  parser: "CompileParser"
263
262
  order: 1
264
263
  warning_threshold: 0.9999
265
264
  error_threshold: 0.9999
266
265
 
267
266
  ruff:
268
- command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
267
+ command: "{python} -m ruff check --output-format concise --no-cache \"{context_path}\"{exclude}"
268
+ exclude_format: " --exclude {path}"
269
269
  parser: "RuffParser"
270
270
  order: 2
271
271
  warning_threshold: 0.9999
272
272
  error_threshold: 0.9
273
273
 
274
274
  ty:
275
- command: "{python} -m ty check --output-format concise --color never {context_path}"
275
+ command: "{python} -m ty check --output-format concise --color never \"{context_path}\"{exclude}"
276
+ exclude_format: " --exclude {path}"
276
277
  parser: "TyParser"
277
278
  order: 3
278
279
  warning_threshold: 0.9999
@@ -282,22 +283,26 @@ python:
282
283
  - ty
283
284
 
284
285
  bandit:
285
- command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
286
+ 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}\""
287
+ exclude_format: ",{input_path_posix}/{path}"
286
288
  parser: "BanditParser"
287
289
  order: 4
288
290
  warning_threshold: 0.9999
289
291
  error_threshold: 0.8
290
292
 
291
293
  pytest:
292
- command: "{python} -m pytest -v {context_path}"
294
+ command: "{python} -m pytest -v \"{context_path}\"{exclude}"
295
+ exclude_format: " --ignore {path}"
293
296
  parser: "PytestParser"
294
297
  order: 5
295
298
  warning_threshold: 1.0
296
299
  error_threshold: 1.0
297
300
  run_in_target_env: true
301
+ extra_deps:
302
+ - pytest
298
303
 
299
304
  coverage:
300
- command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
305
+ command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest \"{context_path}\" && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
301
306
  parser: "CoverageParser"
302
307
  order: 6
303
308
  warning_threshold: 0.9
@@ -308,40 +313,41 @@ python:
308
313
  - pytest
309
314
 
310
315
  radon-cc:
311
- command: "{python} -m radon cc --json {context_path}"
316
+ command: "{python} -m radon cc --json \"{context_path}\""
312
317
  parser: "ComplexityParser"
313
318
  order: 7
314
319
  warning_threshold: 0.6
315
320
  error_threshold: 0.4
316
321
 
317
322
  radon-mi:
318
- command: "{python} -m radon mi -s --json {context_path}"
323
+ command: "{python} -m radon mi -s --json \"{context_path}\""
319
324
  parser: "MaintainabilityParser"
320
325
  order: 8
321
326
  warning_threshold: 0.6
322
327
  error_threshold: 0.4
323
328
 
324
329
  radon-hal:
325
- command: "{python} -m radon hal -f --json {context_path}"
330
+ command: "{python} -m radon hal -f --json \"{context_path}\""
326
331
  parser: "HalsteadParser"
327
332
  order: 9
328
333
  warning_threshold: 0.5
329
334
  error_threshold: 0.3
330
335
 
331
336
  vulture:
332
- command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
337
+ command: "{python} -m vulture \"{context_path}\" --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git{exclude}"
338
+ exclude_format: ",{path}"
333
339
  parser: "VultureParser"
334
340
  order: 10
335
341
  warning_threshold: 0.9999
336
342
  error_threshold: 0.8
337
343
 
338
344
  interrogate:
339
- command: "{python} -m interrogate {context_path} -v --fail-under 0"
345
+ command: "{python} -m interrogate \"{context_path}\" -e tests{exclude} -v --fail-under 0"
346
+ exclude_format: " -e {path}"
340
347
  parser: "InterrogateParser"
341
348
  order: 11
342
349
  warning_threshold: 0.8
343
350
  error_threshold: 0.3
344
-
345
351
  ```
346
352
 
347
353
  ## Respect
@@ -1,15 +1,15 @@
1
- py_cq/__init__.py,sha256=rS7kf1RU1zZskvJlkbZaMqEpdRYPspwDPAFbxzF3tXg,373
2
- py_cq/cli.py,sha256=wu1GlxSDxS835i9-mO4-xmyBLfr6puU-ES-26T7Mty0,11007
1
+ py_cq/__init__.py,sha256=U2ysDtSFdv2mlXZz4w1Q42pfgfi6YY_3Ln24bkZq14I,260
2
+ py_cq/cli.py,sha256=RRQVPVOwG-EMYndMUhBfqBqm1S1kcaObhKOdfxrrYl0,11963
3
3
  py_cq/config/__init__.py,sha256=f0wc51O_3kGDTZUnCbGv8_zWnC5yYGl4NWcf2buSImQ,670
4
- py_cq/config/config.yaml,sha256=R8CjZH8erBQSbJFKgHt-CCwD1Mgj6ZCGQ3OtWH0Ebig,2402
4
+ py_cq/config/config.yaml,sha256=TPZJogpWbyf0Ml2mHrHzTNyTik3k07KPVGsA1wT9GEc,2696
5
5
  py_cq/context_hash.py,sha256=h-i7Rhd7AUfLv9SkQvE79bjJvTsm_ZwoVwSmUKXWmfM,2977
6
- py_cq/execution_engine.py,sha256=tgNGFOO3h-EyetCEzC_RS2K-b9OkOFpOwGwrEAHIpZA,7477
6
+ py_cq/execution_engine.py,sha256=nAsCvldUfDagZnfOwXf4A37VQLxCGiv12T8ymutflC4,8697
7
7
  py_cq/language_detector.py,sha256=6av5HaimcZ54RkN69xQmGgC0mxtTvGzPV3SL8NGG8Uc,1116
8
8
  py_cq/llm_formatter.py,sha256=l74O5iqNWMR16789Xncoi93xOqgQJhW5IQK9_OyA9mE,1632
9
- py_cq/localtypes.py,sha256=jCX1ZuwTixQooEk2N1Gi1XRuDcdBc2NlOdQBKysEybk,6128
9
+ py_cq/localtypes.py,sha256=UGI2kl1xB2TedKGByth3URiqJKY56YZMNds1hnLEzvU,6228
10
10
  py_cq/main.py,sha256=VKoXI8R8rB2fEROBYoTVURfinLqyh8XTNIIWAOtH7dw,380
11
11
  py_cq/metric_aggregator.py,sha256=M2ymo62S7p7qPUqjjoiPg4IVyXQhLMuTr9-jxLiFjCY,853
12
- py_cq/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ py_cq/parsers/__init__.py,sha256=YS3wPS0cMNU80zkdSZBEZOkqDKE6Jk--0Xd_bX7VMcA,27
13
13
  py_cq/parsers/banditparser.py,sha256=Ju_CkuXtkVn1Th9aQ6mv6fTUTpb9pc1YD8Nzt_nMgFQ,2326
14
14
  py_cq/parsers/common.py,sha256=thbziNjqaf-SY3Y0W-KmPwzlcC-YCV98Z8jubUJM7_4,5203
15
15
  py_cq/parsers/compileparser.py,sha256=YsT7ePUDRjsUHTLqgRv4x6jJBCTt6cbm1mgxplzxETg,6256
@@ -17,7 +17,7 @@ py_cq/parsers/complexityparser.py,sha256=2t1-wmNjUu65fULcIm5jcgv7ZLWwjakyiY_r-Fx
17
17
  py_cq/parsers/coverageparser.py,sha256=n4h_RCxKvJoYgp356PA4laAmFcngjQ41b_GXZvKkVgc,4041
18
18
  py_cq/parsers/exitcodeparser.py,sha256=ZFL3EbPhGvFjm2qZhfLD1_5dNjR9ULYNQpKgy0Z_dGo,729
19
19
  py_cq/parsers/halsteadparser.py,sha256=ZCN7LP1iUZ91tf3tlspKAPQrTEa78XVdE9Fjn9yPvYk,9008
20
- py_cq/parsers/interrogateparser.py,sha256=G07beMaGR0Mq-pGVozAbwK0xOx9MdMaNVGq2CGrUb7w,2225
20
+ py_cq/parsers/interrogateparser.py,sha256=gk9pJ7yFXMzLjk6PP0X8fxW_gAE2DTIoWMsPqF2xIXs,2260
21
21
  py_cq/parsers/linecountparser.py,sha256=nxWvFntuR8T6FDGpafrlevvt-jR3rwkbVo_t2JX4TF0,1067
22
22
  py_cq/parsers/maintainabilityparser.py,sha256=Ax0ZFA6zzqYIWZH1hP1_GUtdVn2LIJ8SKWtqVNdszYs,3411
23
23
  py_cq/parsers/pytestparser.py,sha256=3N1X4wKhJ2h-2U1GuM3gHWHpHxU9f0LPGcPaojra9W4,6377
@@ -26,8 +26,8 @@ py_cq/parsers/ruffparser.py,sha256=ZdIya4sct2PrsOyfKfMGmXHLh3Qu7HtFqXNY9IuNFog,2
26
26
  py_cq/parsers/typarser.py,sha256=zrI0KS65MUGPxYPP74B6BRyTbjcPhKyQPjH2KZIyxN0,2495
27
27
  py_cq/parsers/vultureparser.py,sha256=U6zC7P0ATA_N4SB90BahKF5QHMITf_z-NsOgrh_Q5rA,1995
28
28
  py_cq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- py_cq/tool_registry.py,sha256=oMEkFHkU3gg5UpeGD4zHtynOYmWieRgDN5kTwZ5KsE8,1584
30
- python_code_quality-0.1.11.dist-info/WHEEL,sha256=Y4JtJkdCWKLnDS7bvHXqjUWSsYTnJMN9TTubfHSxAyo,80
31
- python_code_quality-0.1.11.dist-info/entry_points.txt,sha256=cfWbTw7eYO6Trv1-Z_odL6Zta9CqYU6Vk9lAHdfB60Q,40
32
- python_code_quality-0.1.11.dist-info/METADATA,sha256=jFwxqdRuW0FKdFkS9rhUnJ7Olv2ablFvnF20Wk_s1Eo,12091
33
- python_code_quality-0.1.11.dist-info/RECORD,,
29
+ py_cq/tool_registry.py,sha256=UfmwJH8rtmTY9kX02QaE4OJIWyboTMcXUavgb9R4fxc,1648
30
+ python_code_quality-0.1.14.dist-info/WHEEL,sha256=M4DeIjVCA49okfALADZoWX5JOGwnmHb-JOpQHtI-1c0,80
31
+ python_code_quality-0.1.14.dist-info/entry_points.txt,sha256=cfWbTw7eYO6Trv1-Z_odL6Zta9CqYU6Vk9lAHdfB60Q,40
32
+ python_code_quality-0.1.14.dist-info/METADATA,sha256=HR_8B9uca6SBAn7f74rMAnTOJ6LzODc5AF0w0Uiic28,12496
33
+ python_code_quality-0.1.14.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.11.0
2
+ Generator: uv 0.11.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any