code-towel 1.0.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.
- code_towel-1.0.0.dist-info/METADATA +722 -0
- code_towel-1.0.0.dist-info/RECORD +27 -0
- code_towel-1.0.0.dist-info/WHEEL +5 -0
- code_towel-1.0.0.dist-info/entry_points.txt +4 -0
- code_towel-1.0.0.dist-info/licenses/LICENSE +201 -0
- code_towel-1.0.0.dist-info/top_level.txt +1 -0
- towel/__init__.py +20 -0
- towel/cli.py +813 -0
- towel/unification/__init__.py +24 -0
- towel/unification/assignment_analyzer.py +403 -0
- towel/unification/ast_normalizer.py +223 -0
- towel/unification/ast_pretty_printer.py +133 -0
- towel/unification/binding_detector.py +381 -0
- towel/unification/block_signature.py +123 -0
- towel/unification/builtins.py +209 -0
- towel/unification/exceptions.py +113 -0
- towel/unification/extractor.py +884 -0
- towel/unification/models.py +190 -0
- towel/unification/nominal_unifier.py +375 -0
- towel/unification/orphan_detector.py +187 -0
- towel/unification/pipeline.py +524 -0
- towel/unification/project_layout.py +201 -0
- towel/unification/refactor_engine.py +3381 -0
- towel/unification/scope_analyzer.py +597 -0
- towel/unification/unifier.py +2128 -0
- towel/unification/visitor_utils.py +83 -0
- towel/unification/visitors.py +377 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Copyright 2025 Eric Allen
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Python builtins tracking.
|
|
17
|
+
|
|
18
|
+
Tracks Python builtin functions and names that should never be treated
|
|
19
|
+
as free variables or parameterized.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import Set
|
|
23
|
+
|
|
24
|
+
# Python builtin functions and constants
|
|
25
|
+
# These should never be parameterized or treated as free variables
|
|
26
|
+
PYTHON_BUILTINS = {
|
|
27
|
+
# Common builtin functions
|
|
28
|
+
"abs",
|
|
29
|
+
"all",
|
|
30
|
+
"any",
|
|
31
|
+
"ascii",
|
|
32
|
+
"bin",
|
|
33
|
+
"bool",
|
|
34
|
+
"bytearray",
|
|
35
|
+
"bytes",
|
|
36
|
+
"callable",
|
|
37
|
+
"chr",
|
|
38
|
+
"classmethod",
|
|
39
|
+
"compile",
|
|
40
|
+
"complex",
|
|
41
|
+
"delattr",
|
|
42
|
+
"dict",
|
|
43
|
+
"dir",
|
|
44
|
+
"divmod",
|
|
45
|
+
"enumerate",
|
|
46
|
+
"eval",
|
|
47
|
+
"exec",
|
|
48
|
+
"filter",
|
|
49
|
+
"float",
|
|
50
|
+
"format",
|
|
51
|
+
"frozenset",
|
|
52
|
+
"getattr",
|
|
53
|
+
"globals",
|
|
54
|
+
"hasattr",
|
|
55
|
+
"hash",
|
|
56
|
+
"help",
|
|
57
|
+
"hex",
|
|
58
|
+
"id",
|
|
59
|
+
"input",
|
|
60
|
+
"int",
|
|
61
|
+
"isinstance",
|
|
62
|
+
"issubclass",
|
|
63
|
+
"iter",
|
|
64
|
+
"len",
|
|
65
|
+
"list",
|
|
66
|
+
"locals",
|
|
67
|
+
"map",
|
|
68
|
+
"max",
|
|
69
|
+
"memoryview",
|
|
70
|
+
"min",
|
|
71
|
+
"next",
|
|
72
|
+
"object",
|
|
73
|
+
"oct",
|
|
74
|
+
"open",
|
|
75
|
+
"ord",
|
|
76
|
+
"pow",
|
|
77
|
+
"print",
|
|
78
|
+
"property",
|
|
79
|
+
"range",
|
|
80
|
+
"repr",
|
|
81
|
+
"reversed",
|
|
82
|
+
"round",
|
|
83
|
+
"set",
|
|
84
|
+
"setattr",
|
|
85
|
+
"slice",
|
|
86
|
+
"sorted",
|
|
87
|
+
"staticmethod",
|
|
88
|
+
"str",
|
|
89
|
+
"sum",
|
|
90
|
+
"super",
|
|
91
|
+
"tuple",
|
|
92
|
+
"type",
|
|
93
|
+
"vars",
|
|
94
|
+
"zip",
|
|
95
|
+
# Async iteration (Python 3.10+)
|
|
96
|
+
"aiter",
|
|
97
|
+
"anext",
|
|
98
|
+
# Debugging
|
|
99
|
+
"breakpoint",
|
|
100
|
+
# Builtin constants
|
|
101
|
+
"True",
|
|
102
|
+
"False",
|
|
103
|
+
"None",
|
|
104
|
+
"Ellipsis",
|
|
105
|
+
"NotImplemented",
|
|
106
|
+
# Interactive helpers (in REPL)
|
|
107
|
+
"copyright",
|
|
108
|
+
"credits",
|
|
109
|
+
"exit",
|
|
110
|
+
"license",
|
|
111
|
+
"quit",
|
|
112
|
+
# All builtin exceptions
|
|
113
|
+
"ArithmeticError",
|
|
114
|
+
"AssertionError",
|
|
115
|
+
"AttributeError",
|
|
116
|
+
"BaseException",
|
|
117
|
+
"BaseExceptionGroup",
|
|
118
|
+
"BlockingIOError",
|
|
119
|
+
"BrokenPipeError",
|
|
120
|
+
"BufferError",
|
|
121
|
+
"BytesWarning",
|
|
122
|
+
"ChildProcessError",
|
|
123
|
+
"ConnectionAbortedError",
|
|
124
|
+
"ConnectionError",
|
|
125
|
+
"ConnectionRefusedError",
|
|
126
|
+
"ConnectionResetError",
|
|
127
|
+
"DeprecationWarning",
|
|
128
|
+
"EOFError",
|
|
129
|
+
"EncodingWarning",
|
|
130
|
+
"EnvironmentError",
|
|
131
|
+
"Exception",
|
|
132
|
+
"ExceptionGroup",
|
|
133
|
+
"FileExistsError",
|
|
134
|
+
"FileNotFoundError",
|
|
135
|
+
"FloatingPointError",
|
|
136
|
+
"FutureWarning",
|
|
137
|
+
"GeneratorExit",
|
|
138
|
+
"IOError",
|
|
139
|
+
"ImportError",
|
|
140
|
+
"ImportWarning",
|
|
141
|
+
"IndentationError",
|
|
142
|
+
"IndexError",
|
|
143
|
+
"InterruptedError",
|
|
144
|
+
"IsADirectoryError",
|
|
145
|
+
"KeyError",
|
|
146
|
+
"KeyboardInterrupt",
|
|
147
|
+
"LookupError",
|
|
148
|
+
"MemoryError",
|
|
149
|
+
"ModuleNotFoundError",
|
|
150
|
+
"NameError",
|
|
151
|
+
"NotADirectoryError",
|
|
152
|
+
"NotImplementedError",
|
|
153
|
+
"OSError",
|
|
154
|
+
"OverflowError",
|
|
155
|
+
"PendingDeprecationWarning",
|
|
156
|
+
"PermissionError",
|
|
157
|
+
"ProcessLookupError",
|
|
158
|
+
"PythonFinalizationError",
|
|
159
|
+
"RecursionError",
|
|
160
|
+
"ReferenceError",
|
|
161
|
+
"ResourceWarning",
|
|
162
|
+
"RuntimeError",
|
|
163
|
+
"RuntimeWarning",
|
|
164
|
+
"StopAsyncIteration",
|
|
165
|
+
"StopIteration",
|
|
166
|
+
"SyntaxError",
|
|
167
|
+
"SyntaxWarning",
|
|
168
|
+
"SystemError",
|
|
169
|
+
"SystemExit",
|
|
170
|
+
"TabError",
|
|
171
|
+
"TimeoutError",
|
|
172
|
+
"TypeError",
|
|
173
|
+
"UnboundLocalError",
|
|
174
|
+
"UnicodeDecodeError",
|
|
175
|
+
"UnicodeEncodeError",
|
|
176
|
+
"UnicodeError",
|
|
177
|
+
"UnicodeTranslateError",
|
|
178
|
+
"UnicodeWarning",
|
|
179
|
+
"UserWarning",
|
|
180
|
+
"ValueError",
|
|
181
|
+
"Warning",
|
|
182
|
+
"ZeroDivisionError",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def is_builtin(name: str) -> bool:
|
|
187
|
+
"""
|
|
188
|
+
Check if a name is a Python builtin.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
name: Variable/function name
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
True if it's a builtin
|
|
195
|
+
"""
|
|
196
|
+
return name in PYTHON_BUILTINS
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def filter_builtins(names: Set[str]) -> Set[str]:
|
|
200
|
+
"""
|
|
201
|
+
Filter out builtin names from a set of names.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
names: Set of names
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Set with builtins removed
|
|
208
|
+
"""
|
|
209
|
+
return {name for name in names if not is_builtin(name)}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Copyright 2025 Eric Allen
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Custom exceptions for the Towel refactoring system.
|
|
16
|
+
|
|
17
|
+
This module defines domain-specific exceptions that provide clearer error
|
|
18
|
+
semantics than generic Python exceptions.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TowelError(Exception):
|
|
23
|
+
"""Base exception for all Towel-related errors."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class UnificationError(TowelError):
|
|
29
|
+
"""Failed to unify two code blocks.
|
|
30
|
+
|
|
31
|
+
Raised when the unification algorithm cannot find a valid substitution
|
|
32
|
+
that makes two AST structures equivalent.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ExtractionError(TowelError):
|
|
39
|
+
"""Failed to extract a function from a code block.
|
|
40
|
+
|
|
41
|
+
Raised when function extraction fails due to invalid AST structure,
|
|
42
|
+
scope issues, or other extraction constraints.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class OrphanVariableError(ExtractionError):
|
|
49
|
+
"""Code block contains orphaned variables.
|
|
50
|
+
|
|
51
|
+
Raised when attempting to extract code that would leave variables
|
|
52
|
+
in an undefined state. For example, extracting code that binds a
|
|
53
|
+
variable but leaving usage of that variable in the original scope.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> # This would raise OrphanVariableError:
|
|
57
|
+
>>> # Extract lines 1-2, leaving line 3:
|
|
58
|
+
>>> x = 10
|
|
59
|
+
>>> y = 20
|
|
60
|
+
>>> total = x + y # Would be orphaned
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ScopeAnalysisError(TowelError):
|
|
67
|
+
"""Error during scope analysis.
|
|
68
|
+
|
|
69
|
+
Raised when scope analysis encounters unexpected AST structures
|
|
70
|
+
or fails to determine variable binding relationships.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RefactoringError(TowelError):
|
|
77
|
+
"""Failed to apply a refactoring transformation.
|
|
78
|
+
|
|
79
|
+
Raised when applying a refactoring proposal fails, such as when
|
|
80
|
+
generating replacement code or writing modified files.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ParseError(TowelError):
|
|
87
|
+
"""Failed to parse Python source code.
|
|
88
|
+
|
|
89
|
+
Raised when AST parsing fails due to syntax errors or unsupported
|
|
90
|
+
Python constructs.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ImportResolutionError(TowelError):
|
|
97
|
+
"""Failed to resolve an import path.
|
|
98
|
+
|
|
99
|
+
Raised during cross-file refactoring when import paths cannot be
|
|
100
|
+
determined or when module structure is ambiguous.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ClassHierarchyError(TowelError):
|
|
107
|
+
"""Error in class hierarchy analysis.
|
|
108
|
+
|
|
109
|
+
Raised when analyzing class inheritance relationships for method
|
|
110
|
+
promotion into shared base classes.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
pass
|