pyfixer-ai 0.4.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.
Files changed (123) hide show
  1. pyfixer_ai-0.4.0/LICENSE +21 -0
  2. pyfixer_ai-0.4.0/PKG-INFO +420 -0
  3. pyfixer_ai-0.4.0/README.md +387 -0
  4. pyfixer_ai-0.4.0/pyfixer/__init__.py +4 -0
  5. pyfixer_ai-0.4.0/pyfixer/__main__.py +4 -0
  6. pyfixer_ai-0.4.0/pyfixer/_pair_probe.py +114 -0
  7. pyfixer_ai-0.4.0/pyfixer/ast_detector.py +1278 -0
  8. pyfixer_ai-0.4.0/pyfixer/ast_fixer.py +2446 -0
  9. pyfixer_ai-0.4.0/pyfixer/ast_safety.py +316 -0
  10. pyfixer_ai-0.4.0/pyfixer/ast_style_fixes.py +837 -0
  11. pyfixer_ai-0.4.0/pyfixer/byok.py +565 -0
  12. pyfixer_ai-0.4.0/pyfixer/canon.py +645 -0
  13. pyfixer_ai-0.4.0/pyfixer/cli.py +2060 -0
  14. pyfixer_ai-0.4.0/pyfixer/config.py +216 -0
  15. pyfixer_ai-0.4.0/pyfixer/contracts.py +656 -0
  16. pyfixer_ai-0.4.0/pyfixer/deterministic.py +237 -0
  17. pyfixer_ai-0.4.0/pyfixer/embedder.py +79 -0
  18. pyfixer_ai-0.4.0/pyfixer/feedback_loop.py +221 -0
  19. pyfixer_ai-0.4.0/pyfixer/fewshot.py +166 -0
  20. pyfixer_ai-0.4.0/pyfixer/fixer.py +4334 -0
  21. pyfixer_ai-0.4.0/pyfixer/guardrails.py +138 -0
  22. pyfixer_ai-0.4.0/pyfixer/kbest.py +241 -0
  23. pyfixer_ai-0.4.0/pyfixer/knowledge_base.py +1249 -0
  24. pyfixer_ai-0.4.0/pyfixer/learning.py +495 -0
  25. pyfixer_ai-0.4.0/pyfixer/llm_targeted.py +540 -0
  26. pyfixer_ai-0.4.0/pyfixer/logic_fixes.py +977 -0
  27. pyfixer_ai-0.4.0/pyfixer/main.py +866 -0
  28. pyfixer_ai-0.4.0/pyfixer/oracle_validate.py +174 -0
  29. pyfixer_ai-0.4.0/pyfixer/orchestrator.py +240 -0
  30. pyfixer_ai-0.4.0/pyfixer/postprocess.py +4298 -0
  31. pyfixer_ai-0.4.0/pyfixer/primitives.py +252 -0
  32. pyfixer_ai-0.4.0/pyfixer/py.typed +0 -0
  33. pyfixer_ai-0.4.0/pyfixer/rag_retriever.py +1761 -0
  34. pyfixer_ai-0.4.0/pyfixer/regex_patterns.py +3066 -0
  35. pyfixer_ai-0.4.0/pyfixer/report.py +163 -0
  36. pyfixer_ai-0.4.0/pyfixer/router.py +143 -0
  37. pyfixer_ai-0.4.0/pyfixer/rule_registry.py +231 -0
  38. pyfixer_ai-0.4.0/pyfixer/safety.py +474 -0
  39. pyfixer_ai-0.4.0/pyfixer/sandbox.py +102 -0
  40. pyfixer_ai-0.4.0/pyfixer/sandboxexec.py +210 -0
  41. pyfixer_ai-0.4.0/pyfixer/scanner.py +1303 -0
  42. pyfixer_ai-0.4.0/pyfixer/store.py +502 -0
  43. pyfixer_ai-0.4.0/pyfixer/style_fixes.py +389 -0
  44. pyfixer_ai-0.4.0/pyfixer/tests_gen.py +268 -0
  45. pyfixer_ai-0.4.0/pyfixer/trace.py +39 -0
  46. pyfixer_ai-0.4.0/pyfixer/type_doc_generator.py +611 -0
  47. pyfixer_ai-0.4.0/pyfixer/validators.py +316 -0
  48. pyfixer_ai-0.4.0/pyfixer/verify.py +842 -0
  49. pyfixer_ai-0.4.0/pyfixer/workers.py +420 -0
  50. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/PKG-INFO +420 -0
  51. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/SOURCES.txt +121 -0
  52. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/dependency_links.txt +1 -0
  53. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/entry_points.txt +2 -0
  54. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/requires.txt +12 -0
  55. pyfixer_ai-0.4.0/pyfixer_ai.egg-info/top_level.txt +1 -0
  56. pyfixer_ai-0.4.0/pyproject.toml +121 -0
  57. pyfixer_ai-0.4.0/setup.cfg +4 -0
  58. pyfixer_ai-0.4.0/tests/test_api.py +148 -0
  59. pyfixer_ai-0.4.0/tests/test_api_extended.py +415 -0
  60. pyfixer_ai-0.4.0/tests/test_ast_safety.py +74 -0
  61. pyfixer_ai-0.4.0/tests/test_ast_style_imports.py +281 -0
  62. pyfixer_ai-0.4.0/tests/test_byok.py +28 -0
  63. pyfixer_ai-0.4.0/tests/test_byok_extended.py +63 -0
  64. pyfixer_ai-0.4.0/tests/test_candidate_preserves_defs.py +55 -0
  65. pyfixer_ai-0.4.0/tests/test_candidate_preserves_tests.py +119 -0
  66. pyfixer_ai-0.4.0/tests/test_candidate_terminates.py +46 -0
  67. pyfixer_ai-0.4.0/tests/test_canon.py +108 -0
  68. pyfixer_ai-0.4.0/tests/test_cli.py +584 -0
  69. pyfixer_ai-0.4.0/tests/test_common_logic_imports.py +281 -0
  70. pyfixer_ai-0.4.0/tests/test_config.py +59 -0
  71. pyfixer_ai-0.4.0/tests/test_contract_gate.py +368 -0
  72. pyfixer_ai-0.4.0/tests/test_corpus_regression.py +401 -0
  73. pyfixer_ai-0.4.0/tests/test_deg_join_runtime_gate.py +116 -0
  74. pyfixer_ai-0.4.0/tests/test_detector_refinements.py +420 -0
  75. pyfixer_ai-0.4.0/tests/test_determinism_gates.py +79 -0
  76. pyfixer_ai-0.4.0/tests/test_deterministic_unified.py +202 -0
  77. pyfixer_ai-0.4.0/tests/test_differential_rules.py +458 -0
  78. pyfixer_ai-0.4.0/tests/test_docs_cli.py +55 -0
  79. pyfixer_ai-0.4.0/tests/test_ensure_typing_imports.py +46 -0
  80. pyfixer_ai-0.4.0/tests/test_fix_flow.py +576 -0
  81. pyfixer_ai-0.4.0/tests/test_folder_verify_flow.py +183 -0
  82. pyfixer_ai-0.4.0/tests/test_ghaction_fix.py +43 -0
  83. pyfixer_ai-0.4.0/tests/test_guardrails.py +87 -0
  84. pyfixer_ai-0.4.0/tests/test_idempotency.py +291 -0
  85. pyfixer_ai-0.4.0/tests/test_improvements.py +749 -0
  86. pyfixer_ai-0.4.0/tests/test_join_runtime_lane_gate.py +97 -0
  87. pyfixer_ai-0.4.0/tests/test_kbest.py +135 -0
  88. pyfixer_ai-0.4.0/tests/test_key_pool.py +77 -0
  89. pyfixer_ai-0.4.0/tests/test_learning.py +136 -0
  90. pyfixer_ai-0.4.0/tests/test_llm_targeted.py +388 -0
  91. pyfixer_ai-0.4.0/tests/test_merge_back_incompatible.py +207 -0
  92. pyfixer_ai-0.4.0/tests/test_merge_pipeline.py +425 -0
  93. pyfixer_ai-0.4.0/tests/test_mock_llm_contract.py +872 -0
  94. pyfixer_ai-0.4.0/tests/test_oracle_mutation_test.py +238 -0
  95. pyfixer_ai-0.4.0/tests/test_oracle_reflexive.py +377 -0
  96. pyfixer_ai-0.4.0/tests/test_proto_detector.py +72 -0
  97. pyfixer_ai-0.4.0/tests/test_quality_fixes.py +585 -0
  98. pyfixer_ai-0.4.0/tests/test_robustness.py +297 -0
  99. pyfixer_ai-0.4.0/tests/test_routing_fixes.py +168 -0
  100. pyfixer_ai-0.4.0/tests/test_rule_registry.py +108 -0
  101. pyfixer_ai-0.4.0/tests/test_runtime_confirm_lane.py +31 -0
  102. pyfixer_ai-0.4.0/tests/test_safe_scope.py +129 -0
  103. pyfixer_ai-0.4.0/tests/test_safety.py +693 -0
  104. pyfixer_ai-0.4.0/tests/test_sandbox.py +83 -0
  105. pyfixer_ai-0.4.0/tests/test_sandbox_extended.py +93 -0
  106. pyfixer_ai-0.4.0/tests/test_sandboxexec.py +89 -0
  107. pyfixer_ai-0.4.0/tests/test_scanner.py +406 -0
  108. pyfixer_ai-0.4.0/tests/test_scanner_recall.py +36 -0
  109. pyfixer_ai-0.4.0/tests/test_self_learning_integration.py +66 -0
  110. pyfixer_ai-0.4.0/tests/test_semantic_rules.py +169 -0
  111. pyfixer_ai-0.4.0/tests/test_single_file_lane_gate.py +86 -0
  112. pyfixer_ai-0.4.0/tests/test_store.py +98 -0
  113. pyfixer_ai-0.4.0/tests/test_store_extended.py +136 -0
  114. pyfixer_ai-0.4.0/tests/test_structure_gate.py +190 -0
  115. pyfixer_ai-0.4.0/tests/test_suppression_honesty.py +77 -0
  116. pyfixer_ai-0.4.0/tests/test_trace.py +53 -0
  117. pyfixer_ai-0.4.0/tests/test_type_doc_generator.py +268 -0
  118. pyfixer_ai-0.4.0/tests/test_validators.py +73 -0
  119. pyfixer_ai-0.4.0/tests/test_verify.py +219 -0
  120. pyfixer_ai-0.4.0/tests/test_verify_folder_batch.py +86 -0
  121. pyfixer_ai-0.4.0/tests/test_verify_gate_logic.py +140 -0
  122. pyfixer_ai-0.4.0/tests/test_verify_inconclusive.py +74 -0
  123. pyfixer_ai-0.4.0/tests/test_workers.py +158 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PyFixer contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,420 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyfixer-ai
3
+ Version: 0.4.0
4
+ Summary: The auto-fix button for Python: upload a broken file, get a fixed file.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/hix-pro/pyfixer
7
+ Project-URL: Repository, https://github.com/hix-pro/pyfixer
8
+ Keywords: lint,autofix,refactor,code-quality,security
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: fastapi>=0.110
22
+ Requires-Dist: uvicorn>=0.27
23
+ Requires-Dist: python-multipart>=0.0.9
24
+ Requires-Dist: httpx>=0.27
25
+ Requires-Dist: ruff>=0.5
26
+ Requires-Dist: bandit>=1.7
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
30
+ Requires-Dist: mypy>=1.10; extra == "dev"
31
+ Requires-Dist: hypothesis>=6.100; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # PyFixer
35
+
36
+ **The auto-fix button for Python.**
37
+
38
+ Upload a broken file. Get a fixed file. That's it.
39
+
40
+ ```
41
+ Upload → Scan → Fix → Verify → Contracts → Done
42
+ ```
43
+
44
+ ---
45
+
46
+ ## What It Does
47
+
48
+ PyFixer finds and fixes Python problems automatically.
49
+
50
+ | Problem | Fix |
51
+ |---|---|
52
+ | Security holes | Replaces eval, exec, hardcoded passwords, weak crypto |
53
+ | Unused imports | Removes them |
54
+ | Wrong types | Adds type annotations |
55
+ | Bad style | Formats code, fixes line length |
56
+ | Missing docs | Adds docstrings |
57
+ | Old Python | Modernizes to current syntax |
58
+ | Bare excepts | Changes to specific exceptions |
59
+ | Print statements | Converts to logging |
60
+ | Mutable defaults | Fixes `def foo(x=[])` |
61
+ | Builtin shadowing | Fixes `list = x` |
62
+ | Type comparison | Fixes `type(x)==type(y)` |
63
+ | Performance | Fixes O(n²) loops, string concat |
64
+ | Weak random | Replaces `random` with `secrets` |
65
+ | Weak hash | Replaces MD5 with SHA256 |
66
+ | Logic bugs | Behavioral tests catch wrong operators, crash-on-empty, stubs |
67
+
68
+ **17+ types of issues. One click to fix.**
69
+
70
+ ---
71
+
72
+ ## How It Works (v0.3)
73
+
74
+ ```
75
+ 1. Upload .py file
76
+
77
+ 2. Scan with ruff + bandit + mypy + custom/semantic checks
78
+
79
+ 3. Generate a behavioral test for EVERY function (coverage guarantee)
80
+
81
+ 4. AI proposes a fix (13 specialist workers)
82
+ - clean functions are never sent to the LLM
83
+
84
+ 5. Post-processing layer applies guaranteed fixes
85
+ - hardcoded secrets are scrubbed from the output
86
+
87
+ 6. CONTRACT GATE: probe original vs fixed behavior
88
+ - untouched functions must return identical results
89
+ - perf/logging-only fixes must be value-identical
90
+ - violations -> corrective retry -> verbatim splice
91
+
92
+ 7. Verify: syntax check + ruff + mypy + findings count
93
+
94
+ 8. You approve or reject
95
+
96
+ 9. Done. File is fixed.
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Features
102
+
103
+ ### 13 Specialist Workers
104
+
105
+ Each worker is an expert in one thing:
106
+
107
+ | Worker | What It Fixes |
108
+ |---|---|
109
+ | Syntax Surgeon | Syntax errors, indentation |
110
+ | Security Guard | SQL injection, hardcoded passwords, weak crypto |
111
+ | Type Tamer | Missing type annotations, wrong types |
112
+ | Style Formatter | Line length, imports, formatting |
113
+ | Code Cleaner | Dead code, unused variables |
114
+ | Docstring Writer | Missing docstrings, D101-D107 |
115
+ | Annotation Expert | Missing annotations, ANN001-ANN204 |
116
+ | Modernizer | Old Python syntax, PTH/SIM/RET |
117
+ | Performance Optimizer | O(n²) loops, string concatenation |
118
+ | Logger | print() → logging conversion |
119
+ | Error Handler | Bare excepts, broad exceptions |
120
+ | Generalist | Complex issues, multiple codes |
121
+ | MyPy Fixer | Type ignore comments, mypy-specific |
122
+
123
+ ### BYOK (Bring Your Own Key)
124
+
125
+ You bring your own API key. We never store it.
126
+
127
+ | Provider | Key prefix | Model |
128
+ |---|---|---|
129
+ | Google Gemini | `AQ.` | gemini-3.7-flash |
130
+ | DeepSeek | `sk-` (probed) | deepseek-chat |
131
+ | OpenRouter | `sk-or-` | deepseek/deepseek-v4-flash |
132
+ | OpenAI | `sk-` | gpt-4o-mini |
133
+
134
+ #### Server-side key pool (optional)
135
+
136
+ Tired of pasting a key into the UI? Configure keys on the server and every
137
+ client can fix without one:
138
+
139
+ ```bash
140
+ # Option A: environment variable (comma or newline separated)
141
+ export PYFIXER_KEYS="AQ.Ab8RN6...,AQ.Ab8RN7..."
142
+
143
+ # Option B: key file, one key per line
144
+ $EDITOR data/gemini.keys # gitignored — never commit this file
145
+ ```
146
+
147
+ Keys rotate automatically (N keys = N × 15 requests/min). The pool is only
148
+ used when a request carries no `X-Api-Key` header / body key; client keys
149
+ always take priority and are never stored, logged, or written to disk.
150
+ `GET /api/server-keys` reports `{ "available": true }` without exposing them.
151
+
152
+ ### LLM control
153
+
154
+ Every model call goes through a task profile (temperature / token budget /
155
+ JSON-mode) so behaviour is tunable in one place — `PROFILES` in
156
+ `pyfixer/byok.py`:
157
+
158
+ | Profile | Used for | Temperature | Max tokens |
159
+ |---|---|---|---|
160
+ | `fix` | code fixes | 0.0 | 4096 |
161
+ | `fix_escalate` | contract-violation retries | 0.0 | 4096 |
162
+ | `testgen` | behavioral test generation | 0.0 | 3072 |
163
+ | `classify` | finding classification | 0.0 | 512 + JSON mode |
164
+ | `probe` | cheap probes | 0.0 | 256 |
165
+
166
+ Model resolution order: per-call override → `PYFIXER_MODEL` env var →
167
+ provider default. The classifier requests provider-side JSON mode (Gemini
168
+ `response_mime_type`, OpenAI-compatible `response_format`).
169
+
170
+ ### Verify & Rollback
171
+
172
+ - **Verify**: Proves the fix works (syntax + ruff + mypy + findings)
173
+ - **Rollback**: Undo if you don't like it
174
+
175
+ ### Pattern Learning
176
+
177
+ PyFixer remembers what works. The more you use it, the better it gets.
178
+
179
+ ---
180
+
181
+ ## Quickstart
182
+
183
+ ```bash
184
+ # Clone
185
+ git clone <your-repo>
186
+ cd pyfixer
187
+
188
+ # Setup
189
+ python3 -m venv .venv
190
+ .venv/bin/pip install -r requirements.txt
191
+
192
+ # Run
193
+ .venv/bin/uvicorn pyfixer.main:app --host 127.0.0.1 --port 8501
194
+ ```
195
+
196
+ Open http://127.0.0.1:8501
197
+
198
+ > Note: `python-multipart` is required (pulled in by `requirements.txt`) so the
199
+ > upload endpoints that use `Form(...)` work. `ruff` and `bandit` must be
200
+ > installed and on `PATH` (the scanner shells out to `python -m ruff` /
201
+ > `python -m bandit`). `mypy` is optional — only used when you enable the mypy
202
+ > scanner.
203
+
204
+ ---
205
+
206
+ ## CLI
207
+
208
+ Fix files straight from the terminal — same pipeline, same gates, no server:
209
+
210
+ ```bash
211
+ # module form (works from a checkout)
212
+ python -m pyfixer fix app.py
213
+
214
+ # after `pip install -e .` you also get the console script
215
+ pyfixer scan app.py # findings only, no AI call (exit 1 if any)
216
+ pyfixer fix app.py # writes app.fixed.py next to it
217
+ pyfixer fix app.py --apply # overwrite in place
218
+ pyfixer fix app.py --diff # print the patch, write nothing
219
+ pyfixer fix src/ # every .py under a folder (summary at end)
220
+ pyfixer fix app.py --format json # machine-readable output
221
+ pyfixer fix app.py --worker security # force a specialist
222
+ pyfixer fix app.py --llm # + LLM fixes (needs API key)
223
+ pyfixer fix app.py --llm --fast # fewer model calls, same gates
224
+ pyfixer fix app.py --aggressive # + mechanical ruff upgrades (no key)
225
+ pyfixer fix app.py --check # preview only, write nothing
226
+ pyfixer workers # list all specialists
227
+
228
+ # key: --key beats $PYFIXER_KEYS beats data/gemini.keys (rotating pool)
229
+
230
+ # LLM-gated flags: --comprehensive and --reference work ONLY with --llm;
231
+ # --verify needs --llm (pytest gate) or --llm-targeted (mypy gate).
232
+ # Without them the CLI exits 2 instead of silently ignoring the flag.
233
+ ```
234
+
235
+ ### Deterministic Fixes (no API key needed)
236
+
237
+ Deterministic mode (the default — no API key needed) applies 50+ pattern-based fixes instantly:
238
+
239
+ | Code | Fix |
240
+ |---|---|
241
+ | C408 | `dict(name="test")` → `{'name': 'test'}` |
242
+ | C416 | `[x for x in y]` → `list(y)` |
243
+ | E711 | `x == None` → `x is None` |
244
+ | E712 | `x == True` → `x` |
245
+ | E722 | `except:` → `except Exception:` |
246
+ | F401 | Remove unused imports |
247
+ | F841 | Remove unused variables |
248
+ | LOGIC005/009 | Fix inverted comparison signs |
249
+ | LOGIC010 | Fix insertion sort off-by-one |
250
+ | LOGIC014 | `def f(x=[])` → `def f(x=None)` |
251
+ | LOGIC015 | `type(x) == int` → `isinstance(x, int)` |
252
+ | LOGIC016 | `for i in range(n)` → `for _ in range(n)` |
253
+ | PLR1714 | `x==1 or x==2` → `x in (1, 2)` |
254
+ | RET505 | Remove unnecessary `else` after `return` |
255
+ | SIM102 | `if x: if y:` → `if x and y:` |
256
+ | SIM105 | `try: pass` → `contextlib.suppress()` |
257
+ | SIM115 | `open()` → `with open()` |
258
+ | SIM116 | `if k in d: return d[k]` → `d.get(k)` |
259
+ | SIM118 | `x in d.keys()` → `x in d` |
260
+ | SIM210 | `True if x else False` → `bool(x)` |
261
+ | SIM212 | `if x: return True else: return False` → `return x` |
262
+ | UP030 | `"{}".format(x)` → `f"{x}"` |
263
+
264
+ **Example:**
265
+
266
+ ```bash
267
+ # Quick fix without API key (deterministic is the default)
268
+ pyfixer fix buggy.py
269
+
270
+ # See what would change
271
+ pyfixer fix buggy.py --diff
272
+
273
+ # Apply in place
274
+ pyfixer fix buggy.py --apply
275
+ ```
276
+
277
+ Exit codes: `0` OK · `1` findings survived the fix · `2` usage/no key.
278
+
279
+ ---
280
+
281
+ ## API
282
+
283
+ | Method | Endpoint | Description |
284
+ |---|---|---|
285
+ | GET | `/healthz` | Health check |
286
+ | POST | `/api/uploads` | Upload + auto-scan |
287
+ | GET | `/api/uploads` | List all files |
288
+ | GET | `/api/uploads/{id}` | Get file details |
289
+ | POST | `/api/uploads/{id}/fix` | Propose AI fix |
290
+ | POST | `/api/uploads/{id}/verify` | Verify fix works |
291
+ | POST | `/api/uploads/{id}/rollback` | Undo fix |
292
+ | GET | `/api/fixes/{id}/approve` | Approve fix |
293
+ | GET | `/api/fixes/{id}/reject` | Reject fix |
294
+ | GET | `/api/uploads/{id}/report` | Download report |
295
+ | GET | `/api/workers` | List all workers |
296
+ | GET | `/api/audit` | Audit trail |
297
+
298
+ ---
299
+
300
+ ## Scanner
301
+
302
+ PyFixer uses multiple scanners:
303
+
304
+ - **ruff** — Fast Python linter (16 rule sets)
305
+ - **bandit** — Security scanner
306
+ - **mypy** — Type checker
307
+ - **AST detector** — 16 LOGIC codes + 10 STYLE codes + 5+ ALGO/PERF codes
308
+ - **Custom checks** — 8 additional checks:
309
+ - ERR001: Missing error handling
310
+ - ERR002: Broad exception catching
311
+ - LOG001: print() instead of logging
312
+ - PERF001: Inefficient string concatenation
313
+ - MUT001: Mutable default arguments
314
+ - SHADOW001: Builtin name shadowing
315
+ - TYPE001: type() comparison instead of isinstance()
316
+
317
+ **Total: 50+ types of issues detected.**
318
+
319
+ ---
320
+
321
+ ## Testing
322
+
323
+ ```bash
324
+ PYTHONPATH=. .venv/bin/pytest tests/ -v
325
+ ```
326
+
327
+ **862 tests passing** (incl. 9 opt-in corpus parity suites).
328
+
329
+ ### Regression benchmarks (LLM-in-the-loop)
330
+
331
+ `benchmarks/` contains bug files with human-verified `correct.py` references
332
+ and `check.py` auto-checkers. The harness runs each through the REAL fix
333
+ pipeline and scores it — including clean **canary functions** that must keep
334
+ identical behavior (contract-regression guard).
335
+
336
+ ```bash
337
+ # all cases (uses server key pool or PYFIXER_BENCH_KEY)
338
+ .venv/bin/python benchmarks/run_bench.py
339
+
340
+ # one case
341
+ .venv/bin/python benchmarks/run_bench.py --only logic_signs
342
+ ```
343
+
344
+ Cases: `algorithms`, `strings`, `mixed`, `security_basics`, `crash_edges`,
345
+ `logic_signs`, `perf_logging`. A manual GitHub Actions workflow
346
+ (`.github/workflows/regression.yml`) runs them on demand with a
347
+ `PYFIXER_KEYS` secret.
348
+
349
+ ### Corpus regression (downstream suite parity)
350
+
351
+ `tests/test_corpus_regression.py` runs nine real projects' test suites against
352
+ both the pristine checkout and the deterministic rewrite, asserting identical
353
+ pass/fail signatures — the harness that caught the RET505 click corruption,
354
+ the ERR003 logging-import breakage, and the F401 `# noqa` side-effect import
355
+ deletion. Opt-in via `PYFIXER_CORPUS_DIR`:
356
+
357
+ ```bash
358
+ bash scripts/provision_corpus.sh /path/to/pyfixer-corpus # checkouts + venvs
359
+ PYFIXER_CORPUS_DIR=/path/to/pyfixer-corpus PYTHONPATH=. pytest tests/test_corpus_regression.py -m corpus -q
360
+ ```
361
+
362
+ Repos: click, httpie, requests, rich, flask, fastapi, attrs (2022-era),
363
+ httpx 1.0b0 (2025-era), pandas 2.2.3. Pandas is special: its compiled `_libs`
364
+ can't be PYTHONPATH-shadowed, so it runs as a dedicated **two-venv** parity
365
+ test (`venv-pandas` vs `venv-pandas-fixed`, both on numpy==1.26.4). A manual +
366
+ weekly workflow (`.github/workflows/corpus.yml`) runs the same in CI.
367
+
368
+ `tests/test_semantic_rules.py` locks the semantic tier (SEM003/SEM004/SEM006)
369
+ with a detector mutation oracle plus differential-execution coverage of the
370
+ SEM004 fixer; `tests/test_mock_llm_contract.py` locks the SEM003
371
+ detector -> per-function-LLM wiring with a stubbed model (no network).
372
+
373
+ ## Code quality
374
+
375
+ ```bash
376
+ # Lint + auto-format
377
+ .venv/bin/ruff check pyfixer
378
+ .venv/bin/ruff format pyfixer
379
+
380
+ # Static type checking
381
+ .venv/bin/mypy pyfixer
382
+ ```
383
+
384
+ A GitHub Actions workflow (`.github/workflows/ci.yml`) runs `ruff check`,
385
+ `ruff format --check`, `mypy`, and the full pytest suite on every push and
386
+ pull request, so regressions are caught before merge.
387
+
388
+ ---
389
+
390
+ ## Tech Stack
391
+
392
+ - **Backend**: FastAPI + SQLite + Python
393
+ - **Scanner**: ruff + bandit + mypy + custom AST checks
394
+ - **AI**: OpenAI, OpenRouter, Google, DeepSeek (BYOK)
395
+ - **Testing**: pytest + coverage
396
+
397
+ ## Tier Semantics
398
+
399
+ PyFixer promotes fixes through four tier levels. The goal is honest tier assignment — a function's tier reflects the evidence that validates its rewrite.
400
+
401
+ | Tier | When it's assigned | Promotion path |
402
+ |---|---|---|
403
+ | `UNVERIFIED` | Default for any fixed function that has **no oracle** (no `func_test`). LOGIC‑only fixes (ast‑detector findings) always land here, regardless of `verify_mode`. | After the real project suite passes (`--verify` in `cli.py`), tiers are promoted by `_promote_tiers_to_verified` in `cli.py:1168/1276`. |
404
+ | `AI_VERIFIED` | A generated `func_test` exists and the rewrite passes `_valid_fix`. The rewrite was validated against the AI‑generated test. | Same real‑suite promotion; this is the interim tier after propose. |
405
+ | `VERIFIED` | Earned only after the **real project suite** (`--verify`) runs successfully via `_promote_tiers_to_verified`. No automatic path mint `VERIFIED` at propose time. | Manual / CI gate after suite runs. |
406
+ | `RETRIEVED` | Deterministic fixes that match an existing reference (e.g. RETRIEVED from known‑good repo). | Stays RETRIEVED; no further promotion. |
407
+
408
+ **Honesty invariant:** `verify_mode=True` does **not** mint `VERIFIED`. It only means the user passed `--verify`; a real suite still needs to run afterwards. Promotion to `VERIFIED` belongs to the real‑suite gate in `cli.py`, never `propose()`.
409
+
410
+ ---
411
+
412
+ ## License
413
+
414
+ Copyright (c) 2026 Aziz. All rights reserved.
415
+
416
+ ---
417
+
418
+ ## Author
419
+
420
+ **Aziz** — Built PyFixer to fix Python code automatically.