audit-test 0.2.2__tar.gz → 0.2.4__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.
- {audit_test-0.2.2/src/audit_test.egg-info → audit_test-0.2.4}/PKG-INFO +17 -17
- {audit_test-0.2.2 → audit_test-0.2.4}/README.md +16 -16
- {audit_test-0.2.2 → audit_test-0.2.4}/pyproject.toml +1 -1
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/__init__.py +1 -1
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_gate.py +2 -5
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_quality.py +2 -5
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_shared.py +77 -8
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_suite.py +2 -4
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/cli.py +70 -13
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/deps.py +2 -0
- audit_test-0.2.4/src/audit_code/encoding_check.py +86 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/gate.py +3 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/phd.py +2 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/quality.py +4 -8
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/run_all_audits.py +3 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/runner.py +4 -8
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/runtime.py +2 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/suite.py +2 -4
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/wiring.py +2 -0
- {audit_test-0.2.2 → audit_test-0.2.4/src/audit_test.egg-info}/PKG-INFO +17 -17
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_test.egg-info/SOURCES.txt +4 -0
- audit_test-0.2.4/tests/test_cli_focus_ignore.py +217 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_focus.py +23 -0
- audit_test-0.2.4/tests/test_quality_helpers.py +44 -0
- audit_test-0.2.4/tests/test_utf8_output.py +69 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/LICENSE +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/LICENSES.md +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/setup.cfg +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/__main__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/base.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/cpp/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/cpp/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/csharp/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/csharp/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/go/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/go/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/html/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/html/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/java/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/java/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/javascript/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/javascript/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/python/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/python/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/rust/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/rust/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/sql/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/adapters/sql/adapter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_config.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_deps.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_phd.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_runtime.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/audit_wiring.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/config.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/_tool_runner.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/bandit.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/checkstyle.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/clang_tidy.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/clippy.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/codeql.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/cppcheck.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/dependency_scan.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/dotnet_format.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/eslint.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/go_vet.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/golangci_lint.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/htmlhint.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/megalinter.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/pmd.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/prettier.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/rustfmt.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/secret_scan.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/semgrep.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/integrations/stylelint.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/models.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/config_checks.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/profile.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/prompt_checks.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/stdout_checks.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/profiles/agent_engine/tool_registry_checks.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/project.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/reporting/__init__.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/reporting/json_report.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/reporting/junit.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_code/reporting/sarif.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_test.egg-info/dependency_links.txt +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_test.egg-info/entry_points.txt +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/src/audit_test.egg-info/top_level.txt +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_adapters.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_audit_misc.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_audit_phd.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_audit_quality.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_audit_runtime.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_audit_wiring.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_base.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_cli.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_cli_combinatorics.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_config.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_coverage.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_gate.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_integrations.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_reporting.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_runner.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_smoke.py +0 -0
- {audit_test-0.2.2 → audit_test-0.2.4}/tests/test_wrappers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: audit-test
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Code and test verification orchestrator — interrogates code quality AND whether tests genuinely prove correctness
|
|
5
5
|
License: CC BY-NC-ND 4.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -31,14 +31,6 @@ All in one shot. Zero config needed.
|
|
|
31
31
|
pip install audit-test
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Three commands — all identical:
|
|
35
|
-
|
|
36
|
-
```powershell
|
|
37
|
-
audit-test high
|
|
38
|
-
audit-code high
|
|
39
|
-
audit-tests high
|
|
40
|
-
```
|
|
41
|
-
|
|
42
34
|
Or from source:
|
|
43
35
|
|
|
44
36
|
```powershell
|
|
@@ -49,10 +41,17 @@ pip install -e .
|
|
|
49
41
|
|
|
50
42
|
## Usage
|
|
51
43
|
|
|
44
|
+
Three commands — all identical:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
audit-test
|
|
48
|
+
audit-code
|
|
49
|
+
audit-tests
|
|
50
|
+
```
|
|
52
51
|
Bare words or flags — both work:
|
|
53
52
|
|
|
54
53
|
```powershell
|
|
55
|
-
audit-test # full audit
|
|
54
|
+
audit-test # full audit
|
|
56
55
|
audit-test v # full audit, verbose
|
|
57
56
|
audit-test f # fix: auto-format (lint+black)
|
|
58
57
|
audit-test min # min: fast wiring + phd + quality
|
|
@@ -189,13 +188,14 @@ python run_all_audits.py # orchestrate all five into one report
|
|
|
189
188
|
|
|
190
189
|
## The stack
|
|
191
190
|
|
|
192
|
-
| Audit | Question it answers |
|
|
193
|
-
|
|
194
|
-
| wiring | **Is it connected?** Dead symbols, test-only code, config key drift |
|
|
195
|
-
| phd | **Does it meet the bar?** Exception discipline, security patterns, state bugs |
|
|
196
|
-
| runtime | **Will it hang or crash?** Unbounded loops, missing timeouts, secrets in logs |
|
|
197
|
-
| suite | **Is the test suite healthy?** Runs pytest, classifies real vs pollution failures |
|
|
198
|
-
| quality | **External gates + execution truth.** Black, ruff, mypy, CVE scan, coverage |
|
|
191
|
+
| Audit | Question it answers | Full rules |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| wiring | **Is it connected?** Dead symbols, test-only code, config key drift | [docs/wiring.md](docs/wiring.md) |
|
|
194
|
+
| phd | **Does it meet the bar?** Exception discipline, security patterns, state bugs | [docs/phd.md](docs/phd.md) — 37 rules |
|
|
195
|
+
| runtime | **Will it hang or crash?** Unbounded loops, missing timeouts, secrets in logs | [docs/runtime.md](docs/runtime.md) — 13 checks |
|
|
196
|
+
| suite | **Is the test suite healthy?** Runs pytest, classifies real vs pollution failures | [docs/suite.md](docs/suite.md) |
|
|
197
|
+
| quality | **External gates + execution truth.** Black, ruff, mypy, CVE scan, coverage | [docs/quality.md](docs/quality.md) — Q0-Q8 |
|
|
198
|
+
| integrations | **External tools.** semgrep, bandit, +14 native linters across 9 languages | [docs/integrations.md](docs/integrations.md) |
|
|
199
199
|
|
|
200
200
|
## Languages
|
|
201
201
|
|
|
@@ -20,14 +20,6 @@ All in one shot. Zero config needed.
|
|
|
20
20
|
pip install audit-test
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Three commands — all identical:
|
|
24
|
-
|
|
25
|
-
```powershell
|
|
26
|
-
audit-test high
|
|
27
|
-
audit-code high
|
|
28
|
-
audit-tests high
|
|
29
|
-
```
|
|
30
|
-
|
|
31
23
|
Or from source:
|
|
32
24
|
|
|
33
25
|
```powershell
|
|
@@ -38,10 +30,17 @@ pip install -e .
|
|
|
38
30
|
|
|
39
31
|
## Usage
|
|
40
32
|
|
|
33
|
+
Three commands — all identical:
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
audit-test
|
|
37
|
+
audit-code
|
|
38
|
+
audit-tests
|
|
39
|
+
```
|
|
41
40
|
Bare words or flags — both work:
|
|
42
41
|
|
|
43
42
|
```powershell
|
|
44
|
-
audit-test # full audit
|
|
43
|
+
audit-test # full audit
|
|
45
44
|
audit-test v # full audit, verbose
|
|
46
45
|
audit-test f # fix: auto-format (lint+black)
|
|
47
46
|
audit-test min # min: fast wiring + phd + quality
|
|
@@ -178,13 +177,14 @@ python run_all_audits.py # orchestrate all five into one report
|
|
|
178
177
|
|
|
179
178
|
## The stack
|
|
180
179
|
|
|
181
|
-
| Audit | Question it answers |
|
|
182
|
-
|
|
183
|
-
| wiring | **Is it connected?** Dead symbols, test-only code, config key drift |
|
|
184
|
-
| phd | **Does it meet the bar?** Exception discipline, security patterns, state bugs |
|
|
185
|
-
| runtime | **Will it hang or crash?** Unbounded loops, missing timeouts, secrets in logs |
|
|
186
|
-
| suite | **Is the test suite healthy?** Runs pytest, classifies real vs pollution failures |
|
|
187
|
-
| quality | **External gates + execution truth.** Black, ruff, mypy, CVE scan, coverage |
|
|
180
|
+
| Audit | Question it answers | Full rules |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| wiring | **Is it connected?** Dead symbols, test-only code, config key drift | [docs/wiring.md](docs/wiring.md) |
|
|
183
|
+
| phd | **Does it meet the bar?** Exception discipline, security patterns, state bugs | [docs/phd.md](docs/phd.md) — 37 rules |
|
|
184
|
+
| runtime | **Will it hang or crash?** Unbounded loops, missing timeouts, secrets in logs | [docs/runtime.md](docs/runtime.md) — 13 checks |
|
|
185
|
+
| suite | **Is the test suite healthy?** Runs pytest, classifies real vs pollution failures | [docs/suite.md](docs/suite.md) |
|
|
186
|
+
| quality | **External gates + execution truth.** Black, ruff, mypy, CVE scan, coverage | [docs/quality.md](docs/quality.md) — Q0-Q8 |
|
|
187
|
+
| integrations | **External tools.** semgrep, bandit, +14 native linters across 9 languages | [docs/integrations.md](docs/integrations.md) |
|
|
188
188
|
|
|
189
189
|
## Languages
|
|
190
190
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "audit-test"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "Code and test verification orchestrator — interrogates code quality AND whether tests genuinely prove correctness"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
license = {text = "CC BY-NC-ND 4.0"}
|
|
@@ -54,7 +54,7 @@ from audit_code.audit_config import (
|
|
|
54
54
|
SUITE_TIMEOUT,
|
|
55
55
|
)
|
|
56
56
|
from audit_code.audit_quality import _def_spans # noqa: E402
|
|
57
|
-
from audit_code.audit_shared import EXCLUDE_DIRS
|
|
57
|
+
from audit_code.audit_shared import EXCLUDE_DIRS, force_utf8_streams
|
|
58
58
|
|
|
59
59
|
ROOT = Path(__file__).resolve().parent.parent.parent
|
|
60
60
|
# Allow --path override for audit-code wrapper
|
|
@@ -521,10 +521,7 @@ def _run_gates(
|
|
|
521
521
|
|
|
522
522
|
|
|
523
523
|
def main():
|
|
524
|
-
|
|
525
|
-
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
526
|
-
except (AttributeError, OSError):
|
|
527
|
-
pass
|
|
524
|
+
force_utf8_streams()
|
|
528
525
|
ap = argparse.ArgumentParser()
|
|
529
526
|
ap.add_argument(
|
|
530
527
|
"--path", default=None, help="project root (for audit-code wrapper)"
|
|
@@ -60,7 +60,7 @@ import tempfile
|
|
|
60
60
|
from pathlib import Path
|
|
61
61
|
|
|
62
62
|
from audit_code.audit_config import DOC_THRESHOLD_PCT, MIN_FLAG_BODY_LINES, TOOL_TIMEOUT
|
|
63
|
-
from audit_code.audit_shared import EXCLUDE_DIRS
|
|
63
|
+
from audit_code.audit_shared import EXCLUDE_DIRS, force_utf8_streams
|
|
64
64
|
|
|
65
65
|
ROOT = Path(__file__).resolve().parent.parent.parent
|
|
66
66
|
|
|
@@ -523,10 +523,7 @@ def q_mutation(root: Path, counts: dict, enabled: bool):
|
|
|
523
523
|
|
|
524
524
|
|
|
525
525
|
def main():
|
|
526
|
-
|
|
527
|
-
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
528
|
-
except (AttributeError, OSError):
|
|
529
|
-
pass
|
|
526
|
+
force_utf8_streams()
|
|
530
527
|
ap = argparse.ArgumentParser()
|
|
531
528
|
ap.add_argument("--path", default=str(ROOT), help="repo root to audit")
|
|
532
529
|
ap.add_argument("--tests", default="tests", help="tests dir relative to root")
|
|
@@ -6,14 +6,85 @@ Reads .audit-test-ignore from the project root. Supports named #only groups:
|
|
|
6
6
|
slow=[src/quality.py] /mnt/c/other | full quality sweep
|
|
7
7
|
#only
|
|
8
8
|
Format per line: name=[file,...] [/path] [| description]
|
|
9
|
+
|
|
10
|
+
Also supports a project-level source-encoding declaration:
|
|
11
|
+
#encoding utf-8
|
|
12
|
+
used by the `check` command to verify every file decodes under that codec.
|
|
9
13
|
"""
|
|
10
14
|
|
|
15
|
+
import codecs
|
|
11
16
|
import os
|
|
17
|
+
import sys
|
|
12
18
|
from collections import namedtuple
|
|
13
19
|
from pathlib import Path
|
|
14
20
|
|
|
15
21
|
Group = namedtuple("Group", "files path description")
|
|
16
22
|
|
|
23
|
+
# Encoding a scanned project's source is expected to be in. Overridable per
|
|
24
|
+
# target via a `#encoding <name>` line in that project's .audit-test-ignore.
|
|
25
|
+
DEFAULT_ENCODING = "utf-8"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def normalize_encoding(raw: str) -> str:
|
|
29
|
+
"""Validate and canonicalize an encoding name (e.g. 'UTF-16' -> 'utf-16').
|
|
30
|
+
|
|
31
|
+
Accepts spaces so multi-word CLI args like 'GB 18030' resolve to 'gb18030'.
|
|
32
|
+
Raises LookupError if the name is not a known codec.
|
|
33
|
+
"""
|
|
34
|
+
candidate = raw.strip()
|
|
35
|
+
try:
|
|
36
|
+
return codecs.lookup(candidate).name
|
|
37
|
+
except LookupError:
|
|
38
|
+
return codecs.lookup(candidate.replace(" ", "")).name
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def configured_encoding(root: Path | None = None) -> str:
|
|
42
|
+
"""Return the `#encoding` declared in <root>/.audit-test-ignore, else utf-8.
|
|
43
|
+
|
|
44
|
+
Read from the *target* project's ignore file (default cwd) so scanning an
|
|
45
|
+
external repo honours that repo's declared source encoding.
|
|
46
|
+
"""
|
|
47
|
+
base = Path.cwd() if root is None else root
|
|
48
|
+
try:
|
|
49
|
+
for line in (base / ".audit-test-ignore").read_text(
|
|
50
|
+
encoding="utf-8"
|
|
51
|
+
).splitlines():
|
|
52
|
+
s = line.strip()
|
|
53
|
+
if s.lower().startswith("#encoding"):
|
|
54
|
+
parts = s.split(None, 1)
|
|
55
|
+
if len(parts) > 1 and parts[1].strip():
|
|
56
|
+
return parts[1].strip()
|
|
57
|
+
except OSError:
|
|
58
|
+
pass
|
|
59
|
+
return DEFAULT_ENCODING
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def force_utf8_streams() -> None:
|
|
63
|
+
"""Make stdout AND stderr emit UTF-8 (replace on error), on every platform.
|
|
64
|
+
|
|
65
|
+
Windows consoles and pipes default to cp1252/cp437, which raise
|
|
66
|
+
UnicodeEncodeError on the audit's glyphs (🐇, ═, ✓, ✨). macOS/Linux are
|
|
67
|
+
almost always UTF-8 already, so this is a harmless no-op there. Guarded
|
|
68
|
+
because captured/replaced streams (pytest, StringIO) lack reconfigure().
|
|
69
|
+
"""
|
|
70
|
+
for stream in (sys.stdout, sys.stderr):
|
|
71
|
+
try:
|
|
72
|
+
stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
73
|
+
except (AttributeError, OSError):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Environment additions that force a spawned Python worker to start with UTF-8
|
|
78
|
+
# stdio regardless of the host locale — the child sets its pipe encoding at
|
|
79
|
+
# interpreter startup, before any reconfigure() call can run.
|
|
80
|
+
UTF8_ENV = {"PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def utf8_subprocess_env(base: dict | None = None) -> dict:
|
|
84
|
+
"""Return a copy of *base* (default os.environ) with UTF-8 stdio forced."""
|
|
85
|
+
return {**(os.environ if base is None else base), **UTF8_ENV}
|
|
86
|
+
|
|
87
|
+
|
|
17
88
|
# ── Built-in defaults ────────────────────────────────────────────────────────
|
|
18
89
|
|
|
19
90
|
_SKIP_DEFAULTS = {
|
|
@@ -53,25 +124,23 @@ def _parse_group(
|
|
|
53
124
|
) -> tuple[str, Group] | None: # audit: ok (parse helper)
|
|
54
125
|
"""Parse 'fast=[main.py,cli.py] /x | desc' into (name, Group)."""
|
|
55
126
|
line = line.strip()
|
|
56
|
-
# description
|
|
127
|
+
# description (strip first so it can't corrupt the file-list terminator)
|
|
57
128
|
desc = ""
|
|
58
129
|
if "|" in line:
|
|
59
130
|
line, desc = line.split("|", 1)
|
|
60
131
|
desc = desc.strip()
|
|
61
|
-
# path override
|
|
62
|
-
path = default_path
|
|
63
|
-
if " /" in line:
|
|
64
|
-
line, path_part = line.rsplit(" /", 1)
|
|
65
|
-
path = path_part.strip()
|
|
66
132
|
# name=[...]
|
|
67
133
|
if "=[" not in line:
|
|
68
134
|
return None
|
|
69
135
|
name, rest = line.split("=[", 1)
|
|
70
136
|
name = name.strip()
|
|
71
|
-
|
|
137
|
+
# Everything up to the closing ']' is the file list; whatever follows is the
|
|
138
|
+
# optional path, kept verbatim so a leading '/' or Windows 'C:' survives.
|
|
139
|
+
if "]" not in rest:
|
|
72
140
|
return None
|
|
73
|
-
files_str = rest
|
|
141
|
+
files_str, after = rest.split("]", 1)
|
|
74
142
|
files = [f.strip() for f in files_str.split(",") if f.strip()]
|
|
143
|
+
path = after.strip() or default_path
|
|
75
144
|
return name, Group(tuple(files), path, desc)
|
|
76
145
|
|
|
77
146
|
|
|
@@ -51,6 +51,7 @@ from collections import Counter
|
|
|
51
51
|
from pathlib import Path
|
|
52
52
|
|
|
53
53
|
from audit_code.audit_config import FULL_SUITE_TIMEOUT, MAX_SOLO_RERUNS, SOLO_TIMEOUT
|
|
54
|
+
from audit_code.audit_shared import force_utf8_streams
|
|
54
55
|
|
|
55
56
|
ROOT = Path(__file__).resolve().parent.parent.parent
|
|
56
57
|
TESTS_DIR = "tests"
|
|
@@ -221,10 +222,7 @@ def _s3_s5_report(out: str, r: dict, med: int, info: int) -> tuple[int, int]:
|
|
|
221
222
|
|
|
222
223
|
|
|
223
224
|
def main():
|
|
224
|
-
|
|
225
|
-
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
226
|
-
except (AttributeError, OSError):
|
|
227
|
-
pass
|
|
225
|
+
force_utf8_streams()
|
|
228
226
|
strict = "--strict" in sys.argv
|
|
229
227
|
fast = "--fast" in sys.argv
|
|
230
228
|
baseline = "--baseline" in sys.argv
|
|
@@ -26,6 +26,8 @@ import sys
|
|
|
26
26
|
from collections import namedtuple as _nt # audit: ok (focus helpers)
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
|
+
from audit_code import encoding_check
|
|
30
|
+
from audit_code.audit_shared import force_utf8_streams, normalize_encoding
|
|
29
31
|
from audit_code.config import load_project_config
|
|
30
32
|
from audit_code.gate import run_gate as gate_main
|
|
31
33
|
from audit_code.models import EXIT_FAIL, EXIT_PASS
|
|
@@ -351,11 +353,7 @@ def run_gate_cmd(args: argparse.Namespace) -> int:
|
|
|
351
353
|
|
|
352
354
|
|
|
353
355
|
def _force_utf8_output() -> None:
|
|
354
|
-
|
|
355
|
-
try:
|
|
356
|
-
stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
357
|
-
except (AttributeError, OSError):
|
|
358
|
-
pass
|
|
356
|
+
force_utf8_streams()
|
|
359
357
|
|
|
360
358
|
|
|
361
359
|
# ── focus / ignore commands ──────────────────────────────────────────────────
|
|
@@ -409,12 +407,15 @@ def _parse_groups(lines: list[str]) -> tuple[dict[str, _Grp], str]:
|
|
|
409
407
|
if "|" in s:
|
|
410
408
|
s, desc = s.split("|", 1)
|
|
411
409
|
desc = desc.strip()
|
|
412
|
-
if "
|
|
413
|
-
s, path = s.rsplit(" /", 1)
|
|
414
|
-
path = path.strip()
|
|
415
|
-
if "=[" in s and s.endswith("]"):
|
|
410
|
+
if "=[" in s:
|
|
416
411
|
name, rest = s.split("=[", 1)
|
|
417
|
-
|
|
412
|
+
# file list ends at the first ']'; the remainder is the path, kept
|
|
413
|
+
# verbatim so a leading '/' or Windows 'C:' survives.
|
|
414
|
+
if "]" not in rest:
|
|
415
|
+
continue
|
|
416
|
+
files_str, after = rest.split("]", 1)
|
|
417
|
+
files = tuple(f.strip() for f in files_str.split(",") if f.strip())
|
|
418
|
+
path = after.strip() or default
|
|
418
419
|
groups[name.strip()] = _Grp(files, path, desc)
|
|
419
420
|
return groups, default
|
|
420
421
|
|
|
@@ -439,10 +440,11 @@ def _rebuild_file(
|
|
|
439
440
|
for name, g in sorted(groups.items()):
|
|
440
441
|
line = f"{name}=[{', '.join(g.files)}]"
|
|
441
442
|
if g.path and g.path != default_path:
|
|
442
|
-
|
|
443
|
-
|
|
443
|
+
# plain space separator; the parser takes everything after ']'
|
|
444
|
+
# as the path, so the value round-trips exactly as written.
|
|
445
|
+
line += f" {g.path}"
|
|
444
446
|
if g.desc:
|
|
445
|
-
line += f"
|
|
447
|
+
line += f" | {g.desc}"
|
|
446
448
|
out.append(line)
|
|
447
449
|
out.append("#only")
|
|
448
450
|
return out
|
|
@@ -714,6 +716,59 @@ def _is_ignore_mode() -> bool:
|
|
|
714
716
|
return "ignore" in sys.argv
|
|
715
717
|
|
|
716
718
|
|
|
719
|
+
def _is_check_mode() -> bool:
|
|
720
|
+
for a in sys.argv[1:]:
|
|
721
|
+
if not a.startswith("-"):
|
|
722
|
+
return a == "check"
|
|
723
|
+
return False
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def _handle_check() -> None:
|
|
727
|
+
"""`check [encoding...] [--path DIR]` — verify every file decodes as encoding.
|
|
728
|
+
|
|
729
|
+
Encoding may be several tokens ('GB 18030'); if omitted, the target's
|
|
730
|
+
#encoding (or utf-8) is used. Path-like args or --path pick the project.
|
|
731
|
+
"""
|
|
732
|
+
idx = sys.argv.index("check")
|
|
733
|
+
args = sys.argv[idx + 1 :]
|
|
734
|
+
if args and args[0] in ("help", "-h", "--help"):
|
|
735
|
+
print(
|
|
736
|
+
"check — verify every text file decodes under an encoding\n\n"
|
|
737
|
+
" check use the project's #encoding (or utf-8)\n"
|
|
738
|
+
" check <encoding> e.g. utf-8 | ascii | UTF-16 | gb18030\n"
|
|
739
|
+
" check <encoding> --path <dir> check another project\n\n"
|
|
740
|
+
"Configure a project's expected encoding with a line in "
|
|
741
|
+
".audit-test-ignore:\n #encoding utf-8"
|
|
742
|
+
)
|
|
743
|
+
sys.exit(0)
|
|
744
|
+
|
|
745
|
+
root: str | None = None
|
|
746
|
+
enc_tokens: list[str] = []
|
|
747
|
+
i = 0
|
|
748
|
+
while i < len(args):
|
|
749
|
+
a = args[i]
|
|
750
|
+
if a in ("--path", "-p"):
|
|
751
|
+
root = args[i + 1] if i + 1 < len(args) else None
|
|
752
|
+
i += 2
|
|
753
|
+
continue
|
|
754
|
+
if a.startswith("/") or a.startswith("C:") or a.startswith("D:"):
|
|
755
|
+
root = a
|
|
756
|
+
i += 1
|
|
757
|
+
continue
|
|
758
|
+
enc_tokens.append(a)
|
|
759
|
+
i += 1
|
|
760
|
+
|
|
761
|
+
encoding = " ".join(enc_tokens) if enc_tokens else None
|
|
762
|
+
if encoding is not None:
|
|
763
|
+
try:
|
|
764
|
+
encoding = normalize_encoding(encoding)
|
|
765
|
+
except LookupError:
|
|
766
|
+
print(f"unknown encoding: {' '.join(enc_tokens)!r} — try utf-8, ascii, gb18030")
|
|
767
|
+
sys.exit(2)
|
|
768
|
+
target = find_target_root(root)
|
|
769
|
+
sys.exit(encoding_check.run(target, encoding))
|
|
770
|
+
|
|
771
|
+
|
|
717
772
|
def main():
|
|
718
773
|
_force_utf8_output()
|
|
719
774
|
if _is_gate_mode():
|
|
@@ -722,6 +777,8 @@ def main():
|
|
|
722
777
|
parser = build_gate_parser()
|
|
723
778
|
args = parser.parse_args()
|
|
724
779
|
sys.exit(run_gate_cmd(args))
|
|
780
|
+
elif _is_check_mode():
|
|
781
|
+
_handle_check()
|
|
725
782
|
elif _is_focus_mode():
|
|
726
783
|
_handle_focus()
|
|
727
784
|
elif _is_ignore_mode():
|
|
@@ -10,6 +10,7 @@ import subprocess
|
|
|
10
10
|
import sys
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
from audit_code.audit_shared import utf8_subprocess_env
|
|
13
14
|
from audit_code.models import (
|
|
14
15
|
AuditResult,
|
|
15
16
|
AuditStatus,
|
|
@@ -33,6 +34,7 @@ def run(target_root: Path, print_only: bool = False) -> AuditResult:
|
|
|
33
34
|
errors="replace",
|
|
34
35
|
timeout=60,
|
|
35
36
|
cwd=str(target_root),
|
|
37
|
+
env=utf8_subprocess_env(),
|
|
36
38
|
)
|
|
37
39
|
except subprocess.TimeoutExpired:
|
|
38
40
|
return AuditResult(
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""encoding_check.py — universal source-encoding gate.
|
|
2
|
+
|
|
3
|
+
Verifies that every text file under a project decodes cleanly under a chosen
|
|
4
|
+
codec (strict, no error replacement). The codec is either passed explicitly
|
|
5
|
+
(`audit-test check gb18030`) or read from the target project's
|
|
6
|
+
`.audit-test-ignore` (`#encoding <name>`), defaulting to UTF-8.
|
|
7
|
+
|
|
8
|
+
check utf-8 -> every file must be valid UTF-8
|
|
9
|
+
check ascii -> every file must be pure ASCII
|
|
10
|
+
check gb18030 -> every file must be valid GB 18030
|
|
11
|
+
|
|
12
|
+
Binary files (detected by a NUL byte) are skipped so images/archives do not
|
|
13
|
+
false-fail. Skip/exclude and focus-group rules are honoured via should_audit.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from audit_code.audit_shared import (
|
|
20
|
+
EXCLUDE_DIRS,
|
|
21
|
+
configured_encoding,
|
|
22
|
+
force_utf8_streams,
|
|
23
|
+
normalize_encoding,
|
|
24
|
+
should_audit,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Only the first slice is sniffed for NUL bytes when classifying binary files.
|
|
28
|
+
_BINARY_SNIFF_BYTES = 4096
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _looks_binary(raw: bytes) -> bool:
|
|
32
|
+
return b"\x00" in raw[:_BINARY_SNIFF_BYTES]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def scan(root: Path, encoding: str) -> tuple[list[tuple[Path, int, str]], int]:
|
|
36
|
+
"""Return (failures, files_checked).
|
|
37
|
+
|
|
38
|
+
Each failure is (path, byte_offset, reason) for a file that does NOT decode
|
|
39
|
+
under *encoding*.
|
|
40
|
+
"""
|
|
41
|
+
failures: list[tuple[Path, int, str]] = []
|
|
42
|
+
checked = 0
|
|
43
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
44
|
+
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
|
|
45
|
+
for fn in filenames:
|
|
46
|
+
p = Path(dirpath) / fn
|
|
47
|
+
if not should_audit(p):
|
|
48
|
+
continue
|
|
49
|
+
try:
|
|
50
|
+
raw = p.read_bytes()
|
|
51
|
+
except OSError:
|
|
52
|
+
continue
|
|
53
|
+
if _looks_binary(raw):
|
|
54
|
+
continue
|
|
55
|
+
checked += 1
|
|
56
|
+
try:
|
|
57
|
+
raw.decode(encoding)
|
|
58
|
+
except UnicodeDecodeError as e:
|
|
59
|
+
failures.append((p, e.start, e.reason))
|
|
60
|
+
return failures, checked
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def run(target_root: Path, encoding: str | None = None) -> int:
|
|
64
|
+
"""Run the encoding check and print a report. Return an exit code (0 = clean)."""
|
|
65
|
+
force_utf8_streams()
|
|
66
|
+
root = target_root.resolve()
|
|
67
|
+
enc = normalize_encoding(encoding or configured_encoding(root))
|
|
68
|
+
|
|
69
|
+
failures, checked = scan(root, enc)
|
|
70
|
+
|
|
71
|
+
bar = "=" * 74
|
|
72
|
+
print(bar)
|
|
73
|
+
print(f"ENCODING [{enc}] — {len(failures)} file(s) not decodable / {checked} checked")
|
|
74
|
+
print(bar)
|
|
75
|
+
if not failures:
|
|
76
|
+
print(f" all {checked} text file(s) are valid {enc}")
|
|
77
|
+
return 0
|
|
78
|
+
for path, offset, reason in failures[:50]:
|
|
79
|
+
try:
|
|
80
|
+
rel = path.relative_to(root)
|
|
81
|
+
except ValueError:
|
|
82
|
+
rel = path
|
|
83
|
+
print(f" {rel} byte {offset}: {reason}")
|
|
84
|
+
if len(failures) > 50:
|
|
85
|
+
print(f" ... and {len(failures) - 50} more")
|
|
86
|
+
return 1
|
|
@@ -12,6 +12,8 @@ import subprocess
|
|
|
12
12
|
import sys
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
+
from audit_code.audit_shared import utf8_subprocess_env
|
|
16
|
+
|
|
15
17
|
_SCRIPT = Path(__file__).resolve().parent / "audit_gate.py"
|
|
16
18
|
|
|
17
19
|
|
|
@@ -48,5 +50,6 @@ def run_gate(
|
|
|
48
50
|
capture_output=False, # stream to terminal so user sees gate progress
|
|
49
51
|
cwd=str(target_root),
|
|
50
52
|
timeout=3600,
|
|
53
|
+
env=utf8_subprocess_env(),
|
|
51
54
|
)
|
|
52
55
|
return proc.returncode
|
|
@@ -9,6 +9,7 @@ import subprocess
|
|
|
9
9
|
import sys
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
+
from audit_code.audit_shared import utf8_subprocess_env
|
|
12
13
|
from audit_code.models import (
|
|
13
14
|
AuditResult,
|
|
14
15
|
AuditStatus,
|
|
@@ -41,6 +42,7 @@ def run(target_root: Path, severity: str | None = "HIGH") -> AuditResult:
|
|
|
41
42
|
errors="replace",
|
|
42
43
|
timeout=300,
|
|
43
44
|
cwd=str(target_root),
|
|
45
|
+
env=utf8_subprocess_env(),
|
|
44
46
|
)
|
|
45
47
|
except subprocess.TimeoutExpired:
|
|
46
48
|
return AuditResult(
|
|
@@ -22,7 +22,7 @@ import sys
|
|
|
22
22
|
import tempfile
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
|
|
25
|
-
from audit_code.audit_shared import should_audit
|
|
25
|
+
from audit_code.audit_shared import force_utf8_streams, should_audit
|
|
26
26
|
from audit_code.config import (
|
|
27
27
|
DOC_THRESHOLD_PCT,
|
|
28
28
|
MIN_FLAG_BODY_LINES,
|
|
@@ -168,10 +168,7 @@ def run( # needs fix (god function 550+ lines — decompose into sub-audits)
|
|
|
168
168
|
if fix:
|
|
169
169
|
fast = True
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
173
|
-
except (AttributeError, OSError):
|
|
174
|
-
pass
|
|
171
|
+
force_utf8_streams()
|
|
175
172
|
|
|
176
173
|
root = target_root.resolve()
|
|
177
174
|
tests_dir = (root / tests).resolve()
|
|
@@ -471,12 +468,11 @@ def run( # needs fix (god function 550+ lines — decompose into sub-audits)
|
|
|
471
468
|
else:
|
|
472
469
|
tmp = Path(tempfile.mkdtemp(prefix="audit_q5_"))
|
|
473
470
|
json_file = tmp / "cov.json"
|
|
474
|
-
|
|
471
|
+
if (
|
|
475
472
|
shared_cov is not None
|
|
476
473
|
and shared_cov.exists()
|
|
477
474
|
and shared_cov.stat().st_size > 0
|
|
478
|
-
)
|
|
479
|
-
if reuse:
|
|
475
|
+
):
|
|
480
476
|
# The suite audit already ran the whole suite under coverage;
|
|
481
477
|
# reuse its data instead of a second full test run.
|
|
482
478
|
data_file = shared_cov
|
|
@@ -57,6 +57,9 @@ def run_one(script: str):
|
|
|
57
57
|
encoding="utf-8",
|
|
58
58
|
errors="replace",
|
|
59
59
|
timeout=timeout,
|
|
60
|
+
# force the child interpreter to UTF-8 stdio on every OS (Windows pipes
|
|
61
|
+
# default to cp1252, which crashes on the audit's Unicode glyphs)
|
|
62
|
+
env={**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"},
|
|
60
63
|
)
|
|
61
64
|
out = proc.stdout or ""
|
|
62
65
|
high = med = info = None
|
|
@@ -11,7 +11,6 @@ Order:
|
|
|
11
11
|
|
|
12
12
|
import shutil
|
|
13
13
|
import subprocess
|
|
14
|
-
import sys
|
|
15
14
|
import tempfile
|
|
16
15
|
import time
|
|
17
16
|
from pathlib import Path
|
|
@@ -19,6 +18,7 @@ from pathlib import Path
|
|
|
19
18
|
from audit_code import phd, quality, runtime, suite, wiring
|
|
20
19
|
from audit_code.adapters import discover
|
|
21
20
|
from audit_code.adapters.base import run_tool, which
|
|
21
|
+
from audit_code.audit_shared import force_utf8_streams
|
|
22
22
|
from audit_code.config import FULL_SUITE_TIMEOUT
|
|
23
23
|
from audit_code.models import AuditResult, AuditStatus
|
|
24
24
|
from audit_code.profiles import load as profile_load
|
|
@@ -56,11 +56,7 @@ def run_suite(
|
|
|
56
56
|
# Status glyphs (✓ ✗ ☠) below crash a cp1252 stdout with UnicodeEncodeError.
|
|
57
57
|
# The CLI forces UTF-8, but a direct/library caller may not — do it here too
|
|
58
58
|
# so any entry into the runner is safe. Idempotent; guarded for capture bufs.
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
_stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
62
|
-
except (AttributeError, OSError):
|
|
63
|
-
pass
|
|
59
|
+
force_utf8_streams()
|
|
64
60
|
|
|
65
61
|
results: list[AuditResult] = []
|
|
66
62
|
start = time.monotonic()
|
|
@@ -322,7 +318,7 @@ def _run_one_module(
|
|
|
322
318
|
stderr=f"Module {module_name} has no run() function",
|
|
323
319
|
)
|
|
324
320
|
|
|
325
|
-
kwargs = {}
|
|
321
|
+
kwargs: dict[str, object] = {}
|
|
326
322
|
if module_name == "quality" and (mode == "min" or fast):
|
|
327
323
|
kwargs["fast"] = True # skip coverage
|
|
328
324
|
if module_name == "quality" and fix:
|
|
@@ -332,7 +328,7 @@ def _run_one_module(
|
|
|
332
328
|
if module_name == "suite" and shared_cov is not None:
|
|
333
329
|
kwargs["cov_file"] = shared_cov # instrument the one shared run
|
|
334
330
|
if module_name == "phd":
|
|
335
|
-
kwargs["severity"] = severity
|
|
331
|
+
kwargs["severity"] = severity
|
|
336
332
|
|
|
337
333
|
return run_fn(target_root, **kwargs)
|
|
338
334
|
|