ballpython 2.0.0__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {ballpython-2.0.0 → ballpython-2.0.2}/PKG-INFO +13 -7
  2. {ballpython-2.0.0 → ballpython-2.0.2}/README.md +12 -6
  3. ballpython-2.0.2/ballpython/py.typed +0 -0
  4. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/PKG-INFO +13 -7
  5. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/SOURCES.txt +21 -1
  6. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/__init__.py +39 -2
  7. ballpython-2.0.2/pycleaner/baseline.py +127 -0
  8. ballpython-2.0.2/pycleaner/cache.py +282 -0
  9. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/cli.py +644 -43
  10. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/complexity_analyzer.py +16 -28
  11. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/config.py +8 -1
  12. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/dead_code_detector.py +275 -38
  13. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/dependency_auditor.py +175 -75
  14. ballpython-2.0.2/pycleaner/discovery.py +206 -0
  15. ballpython-2.0.2/pycleaner/explanations.py +323 -0
  16. ballpython-2.0.2/pycleaner/frameworks/__init__.py +115 -0
  17. ballpython-2.0.2/pycleaner/frameworks/plugins.py +290 -0
  18. ballpython-2.0.2/pycleaner/modernizer.py +349 -0
  19. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/pipeline.py +208 -20
  20. ballpython-2.0.2/pycleaner/py.typed +0 -0
  21. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/security_scanner.py +59 -38
  22. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/syntax_healer.py +188 -10
  23. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/taint_engine.py +8 -8
  24. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/test_generator.py +124 -35
  25. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/type_checker.py +29 -33
  26. ballpython-2.0.2/pycleaner/verifier.py +495 -0
  27. {ballpython-2.0.0 → ballpython-2.0.2}/pyproject.toml +45 -2
  28. ballpython-2.0.2/tests/test_baseline_and_explain.py +90 -0
  29. ballpython-2.0.2/tests/test_cache.py +81 -0
  30. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_cli.py +56 -0
  31. ballpython-2.0.2/tests/test_cli_prove.py +83 -0
  32. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_config.py +34 -0
  33. ballpython-2.0.2/tests/test_dead_code_fixer.py +90 -0
  34. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_dependency_auditor.py +41 -0
  35. ballpython-2.0.2/tests/test_discovery.py +98 -0
  36. ballpython-2.0.2/tests/test_frameworks.py +133 -0
  37. ballpython-2.0.2/tests/test_modernizer.py +106 -0
  38. ballpython-2.0.2/tests/test_mutation_test_generator.py +52 -0
  39. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_syntax_healer.py +55 -0
  40. ballpython-2.0.2/tests/test_verifier.py +74 -0
  41. ballpython-2.0.2/tests/test_verifier_pipeline.py +89 -0
  42. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython/__init__.py +0 -0
  43. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython/__main__.py +0 -0
  44. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython/cli.py +0 -0
  45. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/dependency_links.txt +0 -0
  46. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/entry_points.txt +0 -0
  47. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/requires.txt +0 -0
  48. {ballpython-2.0.0 → ballpython-2.0.2}/ballpython.egg-info/top_level.txt +0 -0
  49. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/__main__.py +0 -0
  50. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/import_resolver.py +0 -0
  51. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/linter_formatter.py +0 -0
  52. {ballpython-2.0.0 → ballpython-2.0.2}/pycleaner/typeshed_resolver.py +0 -0
  53. {ballpython-2.0.0 → ballpython-2.0.2}/setup.cfg +0 -0
  54. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_complexity_analyzer.py +0 -0
  55. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_dead_code_detector.py +0 -0
  56. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_import_resolver.py +0 -0
  57. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_linter_formatter.py +0 -0
  58. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_pipeline.py +0 -0
  59. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_security_scanner.py +0 -0
  60. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_taint_engine.py +0 -0
  61. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_test_generator.py +0 -0
  62. {ballpython-2.0.0 → ballpython-2.0.2}/tests/test_type_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ballpython
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: The Ultimate Static Python Intelligence, Healing, Type Verification, and Security Suite
5
5
  Author: Developer
6
6
  License-Expression: MIT
@@ -47,6 +47,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
47
47
  - Modernizes deprecated syntax via `pyupgrade` (`UP`).
48
48
  - Applies deterministic PEP 8 formatting (`ruff format` / `black` / pure-Python formatter).
49
49
  - Wraps long `from ... import (...)` statements exceeding configured `line-length`.
50
+ - Modernizes legacy typing to PEP 585/604 syntax (`List[T]` -> `list[T]`, `Optional[T]` -> `T | None`), singleton comparisons, and `class X(object)` inheritance via `Modernizer` (disable with `--no-modernize`).
50
51
 
51
52
  4. **Dead Code Detector (`DeadCodeDetector`)**:
52
53
  - Builds a cross-file symbol definition and reference graph across a project.
@@ -54,6 +55,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
54
55
  - Respects public exports in `__all__` and framework decorators (`@app.route`, `@pytest.fixture`, `@abstractmethod`, etc.).
55
56
  - Detects unreachable code following unconditional `return`, `raise`, `break`, `continue`, or `sys.exit()`.
56
57
  - Identifies empty pass blocks with no comments.
58
+ - Auto-prunes unreachable code, redundant `pass` statements, and `if False:` branches via `DeadCodeFixer` (enable with `dead-code --fix`, disable in the pipeline with `--no-dead-code`).
57
59
 
58
60
  5. **Static Security Scanner (`SecurityScanner`)**:
59
61
  - Detects dangerous function calls: `eval()`, `exec()`, `compile()`, `pickle.loads()`, `os.system()`, and unsafe `yaml.load()` lacking `SafeLoader`.
@@ -103,12 +105,13 @@ pip install -e ".[dev,security]"
103
105
  ### Subcommands
104
106
 
105
107
  #### `fix` (Default Action)
106
- Heals syntax, resolves imports, prunes unused imports/variables, fixes lint violations, and formats code:
108
+ Heals syntax, modernizes legacy typing (PEP 585/604), prunes dead code, resolves imports, fixes lint violations, and formats code:
107
109
  ```bash
108
110
  py -m pycleaner fix src/
109
111
  py -m pycleaner fix --diff path/to/script.py
110
112
  py -m pycleaner fix --no-backup src/
111
113
  py -m pycleaner fix --parallel --workers 4 src/
114
+ py -m pycleaner fix --no-modernize --no-dead-code src/
112
115
  ```
113
116
 
114
117
  #### `check` (Dry-Run CI Verification)
@@ -139,6 +142,7 @@ Finds unused functions, unused classes, empty pass branches, and dead code:
139
142
  ```bash
140
143
  py -m pycleaner dead-code .
141
144
  py -m pycleaner dead-code --json .
145
+ py -m pycleaner dead-code --fix .
142
146
  ```
143
147
 
144
148
  #### `audit` (Project Dependency Verification)
@@ -188,7 +192,7 @@ pycleaner path/to/file.py # Equivalent to: pycleaner fix path/to/file.p
188
192
 
189
193
  ## Configuration
190
194
 
191
- `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`.
195
+ `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`. An explicit file can be forced with `pycleaner --config path/to/pyproject.toml <command> ...`, which overrides target-path auto-discovery.
192
196
 
193
197
  ### `pyproject.toml` Example
194
198
 
@@ -294,12 +298,14 @@ for func in violations:
294
298
  from pycleaner import DeadCodeDetector
295
299
 
296
300
  detector = DeadCodeDetector()
297
- report = detector.analyze_project("src/")
301
+ report = detector.scan_project("src/")
298
302
 
299
- for item in report.unused_symbols:
300
- print(f"Unused {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
303
+ print(f"{report.count} dead code item(s) across {report.files_scanned} file(s).")
301
304
 
302
- for item in report.unreachable_code:
305
+ for item in report.items:
306
+ print(f"Dead {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
307
+
308
+ for item in report.by_kind("unreachable"):
303
309
  print(f"Unreachable code at {item.filepath}:{item.lineno} ({item.reason})")
304
310
  ```
305
311
 
@@ -28,6 +28,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
28
28
  - Modernizes deprecated syntax via `pyupgrade` (`UP`).
29
29
  - Applies deterministic PEP 8 formatting (`ruff format` / `black` / pure-Python formatter).
30
30
  - Wraps long `from ... import (...)` statements exceeding configured `line-length`.
31
+ - Modernizes legacy typing to PEP 585/604 syntax (`List[T]` -> `list[T]`, `Optional[T]` -> `T | None`), singleton comparisons, and `class X(object)` inheritance via `Modernizer` (disable with `--no-modernize`).
31
32
 
32
33
  4. **Dead Code Detector (`DeadCodeDetector`)**:
33
34
  - Builds a cross-file symbol definition and reference graph across a project.
@@ -35,6 +36,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
35
36
  - Respects public exports in `__all__` and framework decorators (`@app.route`, `@pytest.fixture`, `@abstractmethod`, etc.).
36
37
  - Detects unreachable code following unconditional `return`, `raise`, `break`, `continue`, or `sys.exit()`.
37
38
  - Identifies empty pass blocks with no comments.
39
+ - Auto-prunes unreachable code, redundant `pass` statements, and `if False:` branches via `DeadCodeFixer` (enable with `dead-code --fix`, disable in the pipeline with `--no-dead-code`).
38
40
 
39
41
  5. **Static Security Scanner (`SecurityScanner`)**:
40
42
  - Detects dangerous function calls: `eval()`, `exec()`, `compile()`, `pickle.loads()`, `os.system()`, and unsafe `yaml.load()` lacking `SafeLoader`.
@@ -84,12 +86,13 @@ pip install -e ".[dev,security]"
84
86
  ### Subcommands
85
87
 
86
88
  #### `fix` (Default Action)
87
- Heals syntax, resolves imports, prunes unused imports/variables, fixes lint violations, and formats code:
89
+ Heals syntax, modernizes legacy typing (PEP 585/604), prunes dead code, resolves imports, fixes lint violations, and formats code:
88
90
  ```bash
89
91
  py -m pycleaner fix src/
90
92
  py -m pycleaner fix --diff path/to/script.py
91
93
  py -m pycleaner fix --no-backup src/
92
94
  py -m pycleaner fix --parallel --workers 4 src/
95
+ py -m pycleaner fix --no-modernize --no-dead-code src/
93
96
  ```
94
97
 
95
98
  #### `check` (Dry-Run CI Verification)
@@ -120,6 +123,7 @@ Finds unused functions, unused classes, empty pass branches, and dead code:
120
123
  ```bash
121
124
  py -m pycleaner dead-code .
122
125
  py -m pycleaner dead-code --json .
126
+ py -m pycleaner dead-code --fix .
123
127
  ```
124
128
 
125
129
  #### `audit` (Project Dependency Verification)
@@ -169,7 +173,7 @@ pycleaner path/to/file.py # Equivalent to: pycleaner fix path/to/file.p
169
173
 
170
174
  ## Configuration
171
175
 
172
- `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`.
176
+ `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`. An explicit file can be forced with `pycleaner --config path/to/pyproject.toml <command> ...`, which overrides target-path auto-discovery.
173
177
 
174
178
  ### `pyproject.toml` Example
175
179
 
@@ -275,12 +279,14 @@ for func in violations:
275
279
  from pycleaner import DeadCodeDetector
276
280
 
277
281
  detector = DeadCodeDetector()
278
- report = detector.analyze_project("src/")
282
+ report = detector.scan_project("src/")
279
283
 
280
- for item in report.unused_symbols:
281
- print(f"Unused {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
284
+ print(f"{report.count} dead code item(s) across {report.files_scanned} file(s).")
282
285
 
283
- for item in report.unreachable_code:
286
+ for item in report.items:
287
+ print(f"Dead {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
288
+
289
+ for item in report.by_kind("unreachable"):
284
290
  print(f"Unreachable code at {item.filepath}:{item.lineno} ({item.reason})")
285
291
  ```
286
292
 
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ballpython
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: The Ultimate Static Python Intelligence, Healing, Type Verification, and Security Suite
5
5
  Author: Developer
6
6
  License-Expression: MIT
@@ -47,6 +47,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
47
47
  - Modernizes deprecated syntax via `pyupgrade` (`UP`).
48
48
  - Applies deterministic PEP 8 formatting (`ruff format` / `black` / pure-Python formatter).
49
49
  - Wraps long `from ... import (...)` statements exceeding configured `line-length`.
50
+ - Modernizes legacy typing to PEP 585/604 syntax (`List[T]` -> `list[T]`, `Optional[T]` -> `T | None`), singleton comparisons, and `class X(object)` inheritance via `Modernizer` (disable with `--no-modernize`).
50
51
 
51
52
  4. **Dead Code Detector (`DeadCodeDetector`)**:
52
53
  - Builds a cross-file symbol definition and reference graph across a project.
@@ -54,6 +55,7 @@ Zero external LLM dependencies, zero mock modes, and built for deterministic dev
54
55
  - Respects public exports in `__all__` and framework decorators (`@app.route`, `@pytest.fixture`, `@abstractmethod`, etc.).
55
56
  - Detects unreachable code following unconditional `return`, `raise`, `break`, `continue`, or `sys.exit()`.
56
57
  - Identifies empty pass blocks with no comments.
58
+ - Auto-prunes unreachable code, redundant `pass` statements, and `if False:` branches via `DeadCodeFixer` (enable with `dead-code --fix`, disable in the pipeline with `--no-dead-code`).
57
59
 
58
60
  5. **Static Security Scanner (`SecurityScanner`)**:
59
61
  - Detects dangerous function calls: `eval()`, `exec()`, `compile()`, `pickle.loads()`, `os.system()`, and unsafe `yaml.load()` lacking `SafeLoader`.
@@ -103,12 +105,13 @@ pip install -e ".[dev,security]"
103
105
  ### Subcommands
104
106
 
105
107
  #### `fix` (Default Action)
106
- Heals syntax, resolves imports, prunes unused imports/variables, fixes lint violations, and formats code:
108
+ Heals syntax, modernizes legacy typing (PEP 585/604), prunes dead code, resolves imports, fixes lint violations, and formats code:
107
109
  ```bash
108
110
  py -m pycleaner fix src/
109
111
  py -m pycleaner fix --diff path/to/script.py
110
112
  py -m pycleaner fix --no-backup src/
111
113
  py -m pycleaner fix --parallel --workers 4 src/
114
+ py -m pycleaner fix --no-modernize --no-dead-code src/
112
115
  ```
113
116
 
114
117
  #### `check` (Dry-Run CI Verification)
@@ -139,6 +142,7 @@ Finds unused functions, unused classes, empty pass branches, and dead code:
139
142
  ```bash
140
143
  py -m pycleaner dead-code .
141
144
  py -m pycleaner dead-code --json .
145
+ py -m pycleaner dead-code --fix .
142
146
  ```
143
147
 
144
148
  #### `audit` (Project Dependency Verification)
@@ -188,7 +192,7 @@ pycleaner path/to/file.py # Equivalent to: pycleaner fix path/to/file.p
188
192
 
189
193
  ## Configuration
190
194
 
191
- `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`.
195
+ `pycleaner` automatically reads configuration from `pyproject.toml` under `[tool.pycleaner]` or from `.pycleaner.toml`. An explicit file can be forced with `pycleaner --config path/to/pyproject.toml <command> ...`, which overrides target-path auto-discovery.
192
196
 
193
197
  ### `pyproject.toml` Example
194
198
 
@@ -294,12 +298,14 @@ for func in violations:
294
298
  from pycleaner import DeadCodeDetector
295
299
 
296
300
  detector = DeadCodeDetector()
297
- report = detector.analyze_project("src/")
301
+ report = detector.scan_project("src/")
298
302
 
299
- for item in report.unused_symbols:
300
- print(f"Unused {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
303
+ print(f"{report.count} dead code item(s) across {report.files_scanned} file(s).")
301
304
 
302
- for item in report.unreachable_code:
305
+ for item in report.items:
306
+ print(f"Dead {item.kind} '{item.name}' at {item.filepath}:{item.lineno}")
307
+
308
+ for item in report.by_kind("unreachable"):
303
309
  print(f"Unreachable code at {item.filepath}:{item.lineno} ({item.reason})")
304
310
  ```
305
311
 
@@ -3,6 +3,7 @@ pyproject.toml
3
3
  ballpython/__init__.py
4
4
  ballpython/__main__.py
5
5
  ballpython/cli.py
6
+ ballpython/py.typed
6
7
  ballpython.egg-info/PKG-INFO
7
8
  ballpython.egg-info/SOURCES.txt
8
9
  ballpython.egg-info/dependency_links.txt
@@ -11,30 +12,49 @@ ballpython.egg-info/requires.txt
11
12
  ballpython.egg-info/top_level.txt
12
13
  pycleaner/__init__.py
13
14
  pycleaner/__main__.py
15
+ pycleaner/baseline.py
16
+ pycleaner/cache.py
14
17
  pycleaner/cli.py
15
18
  pycleaner/complexity_analyzer.py
16
19
  pycleaner/config.py
17
20
  pycleaner/dead_code_detector.py
18
21
  pycleaner/dependency_auditor.py
22
+ pycleaner/discovery.py
23
+ pycleaner/explanations.py
19
24
  pycleaner/import_resolver.py
20
25
  pycleaner/linter_formatter.py
26
+ pycleaner/modernizer.py
21
27
  pycleaner/pipeline.py
28
+ pycleaner/py.typed
22
29
  pycleaner/security_scanner.py
23
30
  pycleaner/syntax_healer.py
24
31
  pycleaner/taint_engine.py
25
32
  pycleaner/test_generator.py
26
33
  pycleaner/type_checker.py
27
34
  pycleaner/typeshed_resolver.py
35
+ pycleaner/verifier.py
36
+ pycleaner/frameworks/__init__.py
37
+ pycleaner/frameworks/plugins.py
38
+ tests/test_baseline_and_explain.py
39
+ tests/test_cache.py
28
40
  tests/test_cli.py
41
+ tests/test_cli_prove.py
29
42
  tests/test_complexity_analyzer.py
30
43
  tests/test_config.py
31
44
  tests/test_dead_code_detector.py
45
+ tests/test_dead_code_fixer.py
32
46
  tests/test_dependency_auditor.py
47
+ tests/test_discovery.py
48
+ tests/test_frameworks.py
33
49
  tests/test_import_resolver.py
34
50
  tests/test_linter_formatter.py
51
+ tests/test_modernizer.py
52
+ tests/test_mutation_test_generator.py
35
53
  tests/test_pipeline.py
36
54
  tests/test_security_scanner.py
37
55
  tests/test_syntax_healer.py
38
56
  tests/test_taint_engine.py
39
57
  tests/test_test_generator.py
40
- tests/test_type_checker.py
58
+ tests/test_type_checker.py
59
+ tests/test_verifier.py
60
+ tests/test_verifier_pipeline.py
@@ -10,19 +10,38 @@ dataflow taint vulnerabilities, and synthesizes automated behavioral test suites
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- __version__ = "2.0.0"
13
+ from pycleaner.discovery import (
14
+ DEFAULT_IGNORED_DIRS,
15
+ PROTECTED_FILE_PATTERNS,
16
+ collect_project_python_files,
17
+ is_ignored_directory,
18
+ is_protected_file,
19
+ )
20
+
21
+ __version__ = "2.0.2"
14
22
  __all__ = [
23
+ "BaselineFingerprint",
24
+ "BaselineManager",
15
25
  "CleanPipeline",
16
26
  "CleanResult",
17
27
  "ComplexityAnalyzer",
18
28
  "ComplexityReport",
29
+ "ContentAddressableCache",
30
+ "CounterExample",
19
31
  "DeadCodeDetector",
32
+ "DeadCodeFixResult",
33
+ "DeadCodeFixer",
20
34
  "DeadCodeReport",
21
35
  "DependencyAuditor",
22
36
  "GeneratedTestSuite",
23
37
  "ImportResolver",
38
+ "IsolatedDifferentialVerifier",
24
39
  "LinterFormatter",
40
+ "ModernizeResult",
41
+ "Modernizer",
42
+ "ProofReceipt",
25
43
  "PyCleanerConfig",
44
+ "RuleExplanation",
26
45
  "SecurityReport",
27
46
  "SecurityScanner",
28
47
  "SyntaxHealer",
@@ -35,15 +54,27 @@ __all__ = [
35
54
  "TypeFinding",
36
55
  "TypeReport",
37
56
  "TypeshedResolver",
57
+ "VerificationTier",
58
+ "get_explanation",
59
+ "list_rules",
38
60
  "load_config",
39
61
  ]
40
62
 
63
+ from pycleaner.baseline import BaselineFingerprint, BaselineManager
64
+ from pycleaner.cache import ContentAddressableCache
41
65
  from pycleaner.complexity_analyzer import ComplexityAnalyzer, ComplexityReport
42
66
  from pycleaner.config import PyCleanerConfig, load_config
43
- from pycleaner.dead_code_detector import DeadCodeDetector, DeadCodeReport
67
+ from pycleaner.dead_code_detector import (
68
+ DeadCodeDetector,
69
+ DeadCodeFixer,
70
+ DeadCodeFixResult,
71
+ DeadCodeReport,
72
+ )
44
73
  from pycleaner.dependency_auditor import DependencyAuditor
74
+ from pycleaner.explanations import RuleExplanation, get_explanation, list_rules
45
75
  from pycleaner.import_resolver import ImportResolver
46
76
  from pycleaner.linter_formatter import LinterFormatter
77
+ from pycleaner.modernizer import Modernizer, ModernizeResult
47
78
  from pycleaner.pipeline import CleanPipeline, CleanResult
48
79
  from pycleaner.security_scanner import SecurityReport, SecurityScanner
49
80
  from pycleaner.syntax_healer import SyntaxHealer
@@ -51,3 +82,9 @@ from pycleaner.taint_engine import TaintEngine, TaintFinding, TaintReport
51
82
  from pycleaner.test_generator import GeneratedTestSuite, TestCase, TestGenerator
52
83
  from pycleaner.type_checker import TypeChecker, TypeFinding, TypeReport
53
84
  from pycleaner.typeshed_resolver import TypeshedResolver
85
+ from pycleaner.verifier import (
86
+ CounterExample,
87
+ IsolatedDifferentialVerifier,
88
+ ProofReceipt,
89
+ VerificationTier,
90
+ )
@@ -0,0 +1,127 @@
1
+ """
2
+ pycleaner.baseline
3
+ ==================
4
+
5
+ Technical Debt Ratchet and Baseline Management Engine.
6
+
7
+ Enables incremental adoption of PyCleaner on legacy codebases by snapshotting
8
+ existing diagnostic violations into `.pycleaner/baseline.json`. Future CI/CD
9
+ checks ensure that existing technical debt can only decrease, never increase.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import hashlib
15
+ import json
16
+ import time
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class BaselineFingerprint:
24
+ rule: str
25
+ file: str
26
+ line: int
27
+ symbol: str
28
+
29
+ def to_hash(self) -> str:
30
+ raw = f"{self.rule}:{self.file}:{self.line}:{self.symbol}"
31
+ return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16]
32
+
33
+ def to_dict(self) -> dict[str, Any]:
34
+ return {
35
+ "rule": self.rule,
36
+ "file": self.file,
37
+ "line": self.line,
38
+ "symbol": self.symbol,
39
+ "hash": self.to_hash(),
40
+ }
41
+
42
+
43
+ class BaselineManager:
44
+ """Manages baseline generation, loading, and ratchet comparison."""
45
+
46
+ def __init__(self, baseline_path: Path | str = ".pycleaner/baseline.json") -> None:
47
+ self.baseline_path = Path(baseline_path)
48
+
49
+ def load_fingerprints(self) -> set[str]:
50
+ """Loads baseline issue hashes from disk."""
51
+ if not self.baseline_path.exists():
52
+ return set()
53
+ try:
54
+ data = json.loads(self.baseline_path.read_text(encoding="utf-8"))
55
+ fingerprints = data.get("fingerprints", [])
56
+ return {
57
+ fp.get("hash")
58
+ for fp in fingerprints
59
+ if isinstance(fp, dict) and fp.get("hash")
60
+ }
61
+ except json.JSONDecodeError:
62
+ return set()
63
+
64
+ def save_baseline(
65
+ self,
66
+ fingerprints: list[BaselineFingerprint],
67
+ root_dir: Path,
68
+ ) -> Path:
69
+ """Serializes fingerprints to the baseline JSON file."""
70
+ self.baseline_path.parent.mkdir(parents=True, exist_ok=True)
71
+ payload = {
72
+ "version": 1,
73
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
74
+ "root_dir": str(root_dir.resolve()),
75
+ "issue_count": len(fingerprints),
76
+ "fingerprints": [fp.to_dict() for fp in fingerprints],
77
+ }
78
+ self.baseline_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
79
+ return self.baseline_path
80
+
81
+ @staticmethod
82
+ def create_fingerprint(
83
+ rule: str,
84
+ file_path: Path | str,
85
+ line: int,
86
+ symbol: str,
87
+ root_dir: Path | None = None,
88
+ ) -> BaselineFingerprint:
89
+ """Constructs a deterministic fingerprint with normalized relative path."""
90
+ p = Path(file_path)
91
+ if root_dir is not None:
92
+ try:
93
+ rel = str(p.resolve().relative_to(root_dir.resolve())).replace(
94
+ "\\", "/"
95
+ )
96
+ except ValueError:
97
+ rel = p.name
98
+ else:
99
+ rel = p.name
100
+
101
+ return BaselineFingerprint(
102
+ rule=rule,
103
+ file=rel,
104
+ line=line,
105
+ symbol=symbol,
106
+ )
107
+
108
+ def filter_new_issues(
109
+ self,
110
+ issues: list[BaselineFingerprint],
111
+ ) -> tuple[list[BaselineFingerprint], list[BaselineFingerprint]]:
112
+ """
113
+ Separates issues into (tolerated_baseline_issues, new_debt_issues).
114
+ Returns:
115
+ (tolerated, new_debt)
116
+ """
117
+ known_hashes = self.load_fingerprints()
118
+ tolerated: list[BaselineFingerprint] = []
119
+ new_debt: list[BaselineFingerprint] = []
120
+
121
+ for issue in issues:
122
+ if issue.to_hash() in known_hashes:
123
+ tolerated.append(issue)
124
+ else:
125
+ new_debt.append(issue)
126
+
127
+ return tolerated, new_debt