python-vibe-guard 0.9.0__tar.gz → 0.11.0__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.
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/PKG-INFO +38 -2
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/README.md +37 -1
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyproject.toml +1 -1
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/python_vibe_guard.egg-info/PKG-INFO +38 -2
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/python_vibe_guard.egg-info/SOURCES.txt +4 -0
- python_vibe_guard-0.11.0/pyvibe/__init__.py +1 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/analyzer.py +9 -2
- python_vibe_guard-0.11.0/pyvibe/baseline.py +73 -0
- python_vibe_guard-0.11.0/pyvibe/cli.py +485 -0
- python_vibe_guard-0.11.0/pyvibe/diff.py +121 -0
- python_vibe_guard-0.11.0/tests/test_baseline.py +234 -0
- python_vibe_guard-0.11.0/tests/test_diff.py +304 -0
- python_vibe_guard-0.9.0/pyvibe/__init__.py +0 -1
- python_vibe_guard-0.9.0/pyvibe/cli.py +0 -198
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/python_vibe_guard.egg-info/dependency_links.txt +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/python_vibe_guard.egg-info/entry_points.txt +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/python_vibe_guard.egg-info/top_level.txt +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/__main__.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/autofix.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/explain.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rule_docs.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/__init__.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/async_requests.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/async_sleep.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/asyncio_run.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/base.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/celery_time_limit.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/contextvar_cleanup.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/create_task_orphan.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/ensure_future_orphan.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/gather_no_return_exceptions.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/httpx_client_sync.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/httpx_sync.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/loop_run_until_complete.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/open_async.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/os_blocking.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/queue_put_nowait.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/retry_no_backoff.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/silent_except.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/sqlite_async.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/subprocess_async.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/threading_lock.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/rules/while_true_no_await.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/pyvibe/sarif.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/setup.cfg +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/tests/test_exclude.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/tests/test_explain.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/tests/test_rules.py +0 -0
- {python_vibe_guard-0.9.0 → python_vibe_guard-0.11.0}/tests/test_sarif.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-vibe-guard
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Runtime anti-pattern scanner for async Python — detects what AI-generated code gets wrong
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: async,linter,fastapi,asyncio,static-analysis
|
|
@@ -173,6 +173,10 @@ python -m pyvibe src/ --exclude tests
|
|
|
173
173
|
# Show the research evidence behind a rule (accuracy, false positives, sources)
|
|
174
174
|
python -m pyvibe explain PYVIBE-002
|
|
175
175
|
|
|
176
|
+
# Baseline mode: suppress pre-existing findings, only fail on new ones
|
|
177
|
+
python -m pyvibe baseline create src/ # snapshot current findings
|
|
178
|
+
python -m pyvibe src/ --baseline # only reports findings NOT in the baseline
|
|
179
|
+
|
|
176
180
|
# Exit code: 0 = clean, 1 = violations found, 2 = path error
|
|
177
181
|
```
|
|
178
182
|
|
|
@@ -255,6 +259,38 @@ python -m pyvibe explain PYVIBE-002
|
|
|
255
259
|
If a rule has no evidence file, it prints a clear `No evidence file found for PYVIBE-XXX`
|
|
256
260
|
and exits non-zero — it never invents data.
|
|
257
261
|
|
|
262
|
+
### Baseline mode
|
|
263
|
+
|
|
264
|
+
Adopting python-vibe-guard on an existing codebase usually means hundreds of pre-existing
|
|
265
|
+
findings you can't fix before CI needs to go green. Baseline mode snapshots the current
|
|
266
|
+
findings once, then only fails on genuinely **new** ones:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Snapshot every current finding into .pyvibe-baseline.json
|
|
270
|
+
python -m pyvibe baseline create src/
|
|
271
|
+
|
|
272
|
+
# Refuses to run if a baseline already exists — use `update` to overwrite it
|
|
273
|
+
python -m pyvibe baseline update src/
|
|
274
|
+
|
|
275
|
+
# Full scan, but only reports findings NOT already in the baseline
|
|
276
|
+
python -m pyvibe src/ --baseline
|
|
277
|
+
python -m pyvibe src/ --baseline --json
|
|
278
|
+
python -m pyvibe src/ --baseline --sarif
|
|
279
|
+
|
|
280
|
+
# `pyvibe scan` is an equivalent, explicit subcommand form of the same flags
|
|
281
|
+
python -m pyvibe scan src/ --baseline
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
A finding is considered "already known" on an exact match of `(file path, rule ID, line
|
|
285
|
+
number)`. Anything that doesn't match — a new violation, or an existing one that moved to
|
|
286
|
+
a different line — is reported and fails the scan (exit code `1`); an unchanged baseline
|
|
287
|
+
produces exit code `0`.
|
|
288
|
+
|
|
289
|
+
`.pyvibe-baseline.json` is a plain JSON file — whether to commit it or add it to
|
|
290
|
+
`.gitignore` is up to your team. Commit it if you want the accepted-debt snapshot shared
|
|
291
|
+
and reviewed like any other file; gitignore it if each contributor/CI run should
|
|
292
|
+
regenerate its own baseline instead.
|
|
293
|
+
|
|
258
294
|
---
|
|
259
295
|
|
|
260
296
|
## CI/CD integration
|
|
@@ -352,7 +388,7 @@ python -m pytest tests/ -v
|
|
|
352
388
|
python tests/test_rules.py
|
|
353
389
|
```
|
|
354
390
|
|
|
355
|
-
|
|
391
|
+
268 tests: true positives + false-positive guards for every rule, plus SARIF output, `pyvibe explain`, `pyvibe review`, and baseline mode coverage.
|
|
356
392
|
|
|
357
393
|
---
|
|
358
394
|
|
|
@@ -158,6 +158,10 @@ python -m pyvibe src/ --exclude tests
|
|
|
158
158
|
# Show the research evidence behind a rule (accuracy, false positives, sources)
|
|
159
159
|
python -m pyvibe explain PYVIBE-002
|
|
160
160
|
|
|
161
|
+
# Baseline mode: suppress pre-existing findings, only fail on new ones
|
|
162
|
+
python -m pyvibe baseline create src/ # snapshot current findings
|
|
163
|
+
python -m pyvibe src/ --baseline # only reports findings NOT in the baseline
|
|
164
|
+
|
|
161
165
|
# Exit code: 0 = clean, 1 = violations found, 2 = path error
|
|
162
166
|
```
|
|
163
167
|
|
|
@@ -240,6 +244,38 @@ python -m pyvibe explain PYVIBE-002
|
|
|
240
244
|
If a rule has no evidence file, it prints a clear `No evidence file found for PYVIBE-XXX`
|
|
241
245
|
and exits non-zero — it never invents data.
|
|
242
246
|
|
|
247
|
+
### Baseline mode
|
|
248
|
+
|
|
249
|
+
Adopting python-vibe-guard on an existing codebase usually means hundreds of pre-existing
|
|
250
|
+
findings you can't fix before CI needs to go green. Baseline mode snapshots the current
|
|
251
|
+
findings once, then only fails on genuinely **new** ones:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Snapshot every current finding into .pyvibe-baseline.json
|
|
255
|
+
python -m pyvibe baseline create src/
|
|
256
|
+
|
|
257
|
+
# Refuses to run if a baseline already exists — use `update` to overwrite it
|
|
258
|
+
python -m pyvibe baseline update src/
|
|
259
|
+
|
|
260
|
+
# Full scan, but only reports findings NOT already in the baseline
|
|
261
|
+
python -m pyvibe src/ --baseline
|
|
262
|
+
python -m pyvibe src/ --baseline --json
|
|
263
|
+
python -m pyvibe src/ --baseline --sarif
|
|
264
|
+
|
|
265
|
+
# `pyvibe scan` is an equivalent, explicit subcommand form of the same flags
|
|
266
|
+
python -m pyvibe scan src/ --baseline
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
A finding is considered "already known" on an exact match of `(file path, rule ID, line
|
|
270
|
+
number)`. Anything that doesn't match — a new violation, or an existing one that moved to
|
|
271
|
+
a different line — is reported and fails the scan (exit code `1`); an unchanged baseline
|
|
272
|
+
produces exit code `0`.
|
|
273
|
+
|
|
274
|
+
`.pyvibe-baseline.json` is a plain JSON file — whether to commit it or add it to
|
|
275
|
+
`.gitignore` is up to your team. Commit it if you want the accepted-debt snapshot shared
|
|
276
|
+
and reviewed like any other file; gitignore it if each contributor/CI run should
|
|
277
|
+
regenerate its own baseline instead.
|
|
278
|
+
|
|
243
279
|
---
|
|
244
280
|
|
|
245
281
|
## CI/CD integration
|
|
@@ -337,7 +373,7 @@ python -m pytest tests/ -v
|
|
|
337
373
|
python tests/test_rules.py
|
|
338
374
|
```
|
|
339
375
|
|
|
340
|
-
|
|
376
|
+
268 tests: true positives + false-positive guards for every rule, plus SARIF output, `pyvibe explain`, `pyvibe review`, and baseline mode coverage.
|
|
341
377
|
|
|
342
378
|
---
|
|
343
379
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-vibe-guard"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
8
8
|
description = "Runtime anti-pattern scanner for async Python — detects what AI-generated code gets wrong"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-vibe-guard
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Runtime anti-pattern scanner for async Python — detects what AI-generated code gets wrong
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: async,linter,fastapi,asyncio,static-analysis
|
|
@@ -173,6 +173,10 @@ python -m pyvibe src/ --exclude tests
|
|
|
173
173
|
# Show the research evidence behind a rule (accuracy, false positives, sources)
|
|
174
174
|
python -m pyvibe explain PYVIBE-002
|
|
175
175
|
|
|
176
|
+
# Baseline mode: suppress pre-existing findings, only fail on new ones
|
|
177
|
+
python -m pyvibe baseline create src/ # snapshot current findings
|
|
178
|
+
python -m pyvibe src/ --baseline # only reports findings NOT in the baseline
|
|
179
|
+
|
|
176
180
|
# Exit code: 0 = clean, 1 = violations found, 2 = path error
|
|
177
181
|
```
|
|
178
182
|
|
|
@@ -255,6 +259,38 @@ python -m pyvibe explain PYVIBE-002
|
|
|
255
259
|
If a rule has no evidence file, it prints a clear `No evidence file found for PYVIBE-XXX`
|
|
256
260
|
and exits non-zero — it never invents data.
|
|
257
261
|
|
|
262
|
+
### Baseline mode
|
|
263
|
+
|
|
264
|
+
Adopting python-vibe-guard on an existing codebase usually means hundreds of pre-existing
|
|
265
|
+
findings you can't fix before CI needs to go green. Baseline mode snapshots the current
|
|
266
|
+
findings once, then only fails on genuinely **new** ones:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Snapshot every current finding into .pyvibe-baseline.json
|
|
270
|
+
python -m pyvibe baseline create src/
|
|
271
|
+
|
|
272
|
+
# Refuses to run if a baseline already exists — use `update` to overwrite it
|
|
273
|
+
python -m pyvibe baseline update src/
|
|
274
|
+
|
|
275
|
+
# Full scan, but only reports findings NOT already in the baseline
|
|
276
|
+
python -m pyvibe src/ --baseline
|
|
277
|
+
python -m pyvibe src/ --baseline --json
|
|
278
|
+
python -m pyvibe src/ --baseline --sarif
|
|
279
|
+
|
|
280
|
+
# `pyvibe scan` is an equivalent, explicit subcommand form of the same flags
|
|
281
|
+
python -m pyvibe scan src/ --baseline
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
A finding is considered "already known" on an exact match of `(file path, rule ID, line
|
|
285
|
+
number)`. Anything that doesn't match — a new violation, or an existing one that moved to
|
|
286
|
+
a different line — is reported and fails the scan (exit code `1`); an unchanged baseline
|
|
287
|
+
produces exit code `0`.
|
|
288
|
+
|
|
289
|
+
`.pyvibe-baseline.json` is a plain JSON file — whether to commit it or add it to
|
|
290
|
+
`.gitignore` is up to your team. Commit it if you want the accepted-debt snapshot shared
|
|
291
|
+
and reviewed like any other file; gitignore it if each contributor/CI run should
|
|
292
|
+
regenerate its own baseline instead.
|
|
293
|
+
|
|
258
294
|
---
|
|
259
295
|
|
|
260
296
|
## CI/CD integration
|
|
@@ -352,7 +388,7 @@ python -m pytest tests/ -v
|
|
|
352
388
|
python tests/test_rules.py
|
|
353
389
|
```
|
|
354
390
|
|
|
355
|
-
|
|
391
|
+
268 tests: true positives + false-positive guards for every rule, plus SARIF output, `pyvibe explain`, `pyvibe review`, and baseline mode coverage.
|
|
356
392
|
|
|
357
393
|
---
|
|
358
394
|
|
|
@@ -9,7 +9,9 @@ pyvibe/__init__.py
|
|
|
9
9
|
pyvibe/__main__.py
|
|
10
10
|
pyvibe/analyzer.py
|
|
11
11
|
pyvibe/autofix.py
|
|
12
|
+
pyvibe/baseline.py
|
|
12
13
|
pyvibe/cli.py
|
|
14
|
+
pyvibe/diff.py
|
|
13
15
|
pyvibe/explain.py
|
|
14
16
|
pyvibe/rule_docs.py
|
|
15
17
|
pyvibe/sarif.py
|
|
@@ -35,6 +37,8 @@ pyvibe/rules/sqlite_async.py
|
|
|
35
37
|
pyvibe/rules/subprocess_async.py
|
|
36
38
|
pyvibe/rules/threading_lock.py
|
|
37
39
|
pyvibe/rules/while_true_no_await.py
|
|
40
|
+
tests/test_baseline.py
|
|
41
|
+
tests/test_diff.py
|
|
38
42
|
tests/test_exclude.py
|
|
39
43
|
tests/test_explain.py
|
|
40
44
|
tests/test_rules.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.11.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import FrozenSet, List
|
|
3
|
+
from typing import FrozenSet, List, Optional
|
|
4
4
|
|
|
5
5
|
from pyvibe.rules.base import Violation
|
|
6
6
|
from pyvibe.rules.async_sleep import AsyncSleepRule
|
|
@@ -132,9 +132,16 @@ def analyze_file(
|
|
|
132
132
|
path: Path,
|
|
133
133
|
*,
|
|
134
134
|
downgrade_in_tests: FrozenSet[str] = TEST_FILE_DOWNGRADE,
|
|
135
|
+
line_filter: Optional[FrozenSet[int]] = None,
|
|
135
136
|
) -> List[Violation]:
|
|
137
|
+
"""line_filter: if given, only violations whose `line` is in this set
|
|
138
|
+
are returned (used by `pyvibe review` to report only diff-touched lines).
|
|
139
|
+
"""
|
|
136
140
|
source = path.read_text(encoding="utf-8", errors="ignore")
|
|
137
|
-
|
|
141
|
+
violations = analyze_source(source, filepath=str(path), downgrade_in_tests=downgrade_in_tests)
|
|
142
|
+
if line_filter is not None:
|
|
143
|
+
violations = [v for v in violations if v.line in line_filter]
|
|
144
|
+
return violations
|
|
138
145
|
|
|
139
146
|
|
|
140
147
|
DEFAULT_EXCLUDES = frozenset({
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Baseline support — `pyvibe baseline create/update` and `pyvibe scan --baseline`.
|
|
2
|
+
|
|
3
|
+
A baseline is a snapshot of existing findings ({filepath: [{rule_id, line,
|
|
4
|
+
message}, ...]}) that a team can choose to commit or gitignore (see README).
|
|
5
|
+
Once created, `pyvibe scan --baseline` only reports genuinely NEW
|
|
6
|
+
violations — anything already in the baseline is suppressed — so a
|
|
7
|
+
codebase can adopt python-vibe-guard incrementally instead of having to
|
|
8
|
+
fix every existing hit before CI goes green.
|
|
9
|
+
"""
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Dict, Set, Tuple
|
|
13
|
+
|
|
14
|
+
DEFAULT_BASELINE_PATH = ".pyvibe-baseline.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaselineNotFoundError(Exception):
|
|
18
|
+
"""Raised when `pyvibe scan --baseline` can't find/parse the baseline file."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_baseline(file_results: Dict) -> dict:
|
|
22
|
+
"""file_results: {path: [Violation, ...]} as produced by analyze_file /
|
|
23
|
+
analyze_directory. Returns the JSON-serializable baseline:
|
|
24
|
+
{filepath: [{rule_id, line, message}, ...]}.
|
|
25
|
+
"""
|
|
26
|
+
return {
|
|
27
|
+
str(path): [
|
|
28
|
+
{"rule_id": v.rule_id, "line": v.line, "message": v.message}
|
|
29
|
+
for v in violations
|
|
30
|
+
]
|
|
31
|
+
for path, violations in file_results.items()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def write_baseline(file_results: Dict, output_path=DEFAULT_BASELINE_PATH) -> None:
|
|
36
|
+
baseline = build_baseline(file_results)
|
|
37
|
+
Path(output_path).write_text(json.dumps(baseline, indent=2), encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_baseline(path=DEFAULT_BASELINE_PATH) -> dict:
|
|
41
|
+
baseline_path = Path(path)
|
|
42
|
+
if not baseline_path.exists():
|
|
43
|
+
raise BaselineNotFoundError(
|
|
44
|
+
f"No baseline found at {path}. Run `pyvibe baseline create` first."
|
|
45
|
+
)
|
|
46
|
+
try:
|
|
47
|
+
return json.loads(baseline_path.read_text(encoding="utf-8"))
|
|
48
|
+
except json.JSONDecodeError as e:
|
|
49
|
+
raise BaselineNotFoundError(f"Baseline at {path} is not valid JSON: {e}") from e
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _baseline_keys(baseline: dict) -> Set[Tuple[str, str, int]]:
|
|
53
|
+
"""Flatten the baseline into (filepath, rule_id, line) triples for exact,
|
|
54
|
+
O(1) membership checks.
|
|
55
|
+
"""
|
|
56
|
+
return {
|
|
57
|
+
(filepath, entry["rule_id"], entry["line"])
|
|
58
|
+
for filepath, entries in baseline.items()
|
|
59
|
+
for entry in entries
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def filter_new_violations(file_results: Dict, baseline: dict) -> Dict:
|
|
64
|
+
"""Return only the violations in file_results NOT present in baseline,
|
|
65
|
+
matched exactly on (filepath, rule_id, line).
|
|
66
|
+
"""
|
|
67
|
+
known = _baseline_keys(baseline)
|
|
68
|
+
new_results = {}
|
|
69
|
+
for path, violations in file_results.items():
|
|
70
|
+
new = [v for v in violations if (str(path), v.rule_id, v.line) not in known]
|
|
71
|
+
if new:
|
|
72
|
+
new_results[path] = new
|
|
73
|
+
return new_results
|