auto-code-fixer 0.3.6__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- __version__ = "0.3.6"
1
+ __version__ = "0.4.0"
@@ -0,0 +1,114 @@
1
+ import difflib
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass(frozen=True)
6
+ class PlannedChange:
7
+ abs_path: str
8
+ rel_path: str
9
+ old_content: str
10
+ new_content: str
11
+
12
+
13
+ class UserAbort(Exception):
14
+ pass
15
+
16
+
17
+ def unified_diff_text(rel_path: str, old: str, new: str) -> str:
18
+ return "".join(
19
+ difflib.unified_diff(
20
+ (old or "").splitlines(keepends=True),
21
+ (new or "").splitlines(keepends=True),
22
+ fromfile=rel_path + " (before)",
23
+ tofile=rel_path + " (after)",
24
+ )
25
+ )
26
+
27
+
28
+ def guard_planned_changes(
29
+ planned: list[PlannedChange],
30
+ *,
31
+ max_file_bytes: int,
32
+ max_total_bytes: int,
33
+ max_diff_lines: int,
34
+ ) -> None:
35
+ """Safety guards against huge edits.
36
+
37
+ Raises ValueError when limits are exceeded.
38
+ """
39
+
40
+ if max_file_bytes <= 0 or max_total_bytes <= 0 or max_diff_lines <= 0:
41
+ raise ValueError("Guard limits must be positive")
42
+
43
+ total = 0
44
+
45
+ for ch in planned:
46
+ b = len((ch.new_content or "").encode("utf-8"))
47
+ total += b
48
+ if b > max_file_bytes:
49
+ raise ValueError(
50
+ f"Proposed content for {ch.rel_path} is {b} bytes, exceeding --max-file-bytes={max_file_bytes}"
51
+ )
52
+
53
+ diff = unified_diff_text(ch.rel_path, ch.old_content, ch.new_content)
54
+ diff_lines = len(diff.splitlines())
55
+ if diff_lines > max_diff_lines:
56
+ raise ValueError(
57
+ f"Diff for {ch.rel_path} is {diff_lines} lines, exceeding --max-diff-lines={max_diff_lines}"
58
+ )
59
+
60
+ if total > max_total_bytes:
61
+ raise ValueError(
62
+ f"Total proposed content is {total} bytes, exceeding --max-total-bytes={max_total_bytes}"
63
+ )
64
+
65
+
66
+ def prompt_approve_file_by_file(
67
+ planned: list[PlannedChange],
68
+ *,
69
+ input_fn=input,
70
+ print_fn=print,
71
+ ) -> list[PlannedChange]:
72
+ """Interactive approval per file.
73
+
74
+ Commands:
75
+ y = apply this change
76
+ n = skip this change
77
+ a = apply this and all remaining
78
+ q = abort
79
+ """
80
+
81
+ if not planned:
82
+ return []
83
+
84
+ out: list[PlannedChange] = []
85
+ apply_all = False
86
+
87
+ for ch in planned:
88
+ diff = unified_diff_text(ch.rel_path, ch.old_content, ch.new_content)
89
+ print_fn("\nPROPOSED CHANGE:")
90
+ print_fn(diff)
91
+
92
+ if apply_all:
93
+ out.append(ch)
94
+ continue
95
+
96
+ ans = (
97
+ input_fn(f"Apply change for {ch.rel_path}? (y/n/a/q): ")
98
+ .strip()
99
+ .lower()
100
+ )
101
+ if ans == "y":
102
+ out.append(ch)
103
+ elif ans == "n":
104
+ continue
105
+ elif ans == "a":
106
+ out.append(ch)
107
+ apply_all = True
108
+ elif ans == "q":
109
+ raise UserAbort("User aborted approvals")
110
+ else:
111
+ # default: be safe
112
+ continue
113
+
114
+ return out