mapFolding 0.5.1__py3-none-any.whl → 0.7.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.
- mapFolding/__init__.py +6 -101
- mapFolding/basecamp.py +12 -10
- mapFolding/beDRY.py +96 -316
- mapFolding/filesystem.py +87 -0
- mapFolding/noHomeYet.py +20 -0
- mapFolding/oeis.py +39 -36
- mapFolding/reference/flattened.py +377 -0
- mapFolding/reference/hunterNumba.py +132 -0
- mapFolding/reference/irvineJavaPort.py +120 -0
- mapFolding/reference/jax.py +208 -0
- mapFolding/reference/lunnan.py +153 -0
- mapFolding/reference/lunnanNumpy.py +123 -0
- mapFolding/reference/lunnanWhile.py +121 -0
- mapFolding/reference/rotatedEntryPoint.py +240 -0
- mapFolding/reference/total_countPlus1vsPlusN.py +211 -0
- mapFolding/someAssemblyRequired/Z0Z_workbench.py +34 -0
- mapFolding/someAssemblyRequired/__init__.py +16 -0
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +21 -0
- mapFolding/someAssemblyRequired/ingredientsNumba.py +100 -0
- mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +7 -0
- mapFolding/someAssemblyRequired/synthesizeDataConverters.py +135 -0
- mapFolding/someAssemblyRequired/synthesizeNumba.py +91 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +417 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +91 -0
- mapFolding/someAssemblyRequired/transformationTools.py +425 -0
- mapFolding/someAssemblyRequired/whatWillBe.py +311 -0
- mapFolding/syntheticModules/__init__.py +0 -0
- mapFolding/syntheticModules/dataNamespaceFlattened.py +30 -0
- mapFolding/syntheticModules/numbaCount.py +90 -0
- mapFolding/syntheticModules/numbaCountExample.py +158 -0
- mapFolding/syntheticModules/numbaCountSequential.py +110 -0
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py +13 -0
- mapFolding/syntheticModules/numba_doTheNeedful.py +12 -0
- mapFolding/syntheticModules/numba_doTheNeedfulExample.py +13 -0
- mapFolding/theDao.py +203 -227
- mapFolding/theSSOT.py +254 -123
- {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/METADATA +10 -8
- mapfolding-0.7.0.dist-info/RECORD +50 -0
- {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/WHEEL +1 -1
- {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/conftest.py +278 -0
- tests/test_computations.py +49 -0
- tests/test_filesystem.py +52 -0
- tests/test_oeis.py +128 -0
- tests/test_other.py +84 -0
- tests/test_tasks.py +50 -0
- mapFolding/theSSOTdatatypes.py +0 -156
- mapFolding-0.5.1.dist-info/RECORD +0 -14
- {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/LICENSE +0 -0
- {mapFolding-0.5.1.dist-info → mapfolding-0.7.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
from collections.abc import Callable, Container, Sequence
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, cast, NamedTuple, TypeAlias, TYPE_CHECKING, TypeGuard, TypeVar
|
|
4
|
+
import ast
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from mapFolding.someAssemblyRequired.whatWillBe import LedgerOfImports
|
|
7
|
+
"""
|
|
8
|
+
Semiotic notes:
|
|
9
|
+
In the `ast` package, some things that look and feel like a "name" are not `ast.Name` type. The following semiotics are a balance between technical precision and practical usage.
|
|
10
|
+
|
|
11
|
+
astName: always means `ast.Name`.
|
|
12
|
+
Name: uppercase, _should_ be interchangeable with astName, even in camelCase.
|
|
13
|
+
name: lowercase, never means `ast.Name`. In camelCase, I _should_ avoid using it in such a way that it could be confused with "Name", uppercase.
|
|
14
|
+
_Identifier: very strongly correlates with the private `ast._Identifier`, which is a TypeAlias for `str`.
|
|
15
|
+
identifier: lowercase, a general term that includes the above and other Python identifiers.
|
|
16
|
+
Identifier: uppercase, without the leading underscore should only appear in camelCase and means "identifier", lowercase.
|
|
17
|
+
namespace: lowercase, in dotted-names, such as `pathlib.Path` or `collections.abc`, "namespace" is the part before the dot.
|
|
18
|
+
Namespace: uppercase, should only appear in camelCase and means "namespace", lowercase.
|
|
19
|
+
"""
|
|
20
|
+
# TODO consider semiotic usefulness of "namespace" or variations such as "namespaceName", "namespacePath", and "namespace_Identifier"
|
|
21
|
+
|
|
22
|
+
# TODO learn whether libcst can help
|
|
23
|
+
|
|
24
|
+
astParameter = TypeVar('astParameter', bound=Any)
|
|
25
|
+
ast_Identifier: TypeAlias = str
|
|
26
|
+
strDotStrCuzPyStoopid: TypeAlias = str
|
|
27
|
+
strORlist_ast_type_paramORintORNone: TypeAlias = Any
|
|
28
|
+
list_ast_type_paramORintORNone: TypeAlias = Any
|
|
29
|
+
strORintORNone: TypeAlias = Any
|
|
30
|
+
Z0Z_thisCannotBeTheBestWay: TypeAlias = list[ast.Name] | list[ast.Attribute] | list[ast.Subscript] | list[ast.Name | ast.Attribute] | list[ast.Name | ast.Subscript] | list[ast.Attribute | ast.Subscript] | list[ast.Name | ast.Attribute | ast.Subscript]
|
|
31
|
+
|
|
32
|
+
# NOTE: the new "Recipe" concept will allow me to remove this
|
|
33
|
+
class YouOughtaKnow(NamedTuple):
|
|
34
|
+
callableSynthesized: str
|
|
35
|
+
pathFilenameForMe: Path
|
|
36
|
+
astForCompetentProgrammers: ast.ImportFrom
|
|
37
|
+
|
|
38
|
+
# listAsNode
|
|
39
|
+
|
|
40
|
+
class NodeCollector(ast.NodeVisitor):
|
|
41
|
+
# A node visitor that collects data via one or more actions when a predicate is met.
|
|
42
|
+
def __init__(self, findPredicate: Callable[[ast.AST], bool], actions: list[Callable[[ast.AST], None]]) -> None:
|
|
43
|
+
self.findPredicate = findPredicate
|
|
44
|
+
self.actions = actions
|
|
45
|
+
|
|
46
|
+
def visit(self, node: ast.AST) -> None:
|
|
47
|
+
if self.findPredicate(node):
|
|
48
|
+
for action in self.actions:
|
|
49
|
+
action(node)
|
|
50
|
+
self.generic_visit(node)
|
|
51
|
+
|
|
52
|
+
class NodeReplacer(ast.NodeTransformer):
|
|
53
|
+
"""
|
|
54
|
+
A node transformer that replaces or removes AST nodes based on a condition.
|
|
55
|
+
This transformer traverses an AST and for each node checks a predicate. If the predicate
|
|
56
|
+
returns True, the transformer uses the replacement builder to obtain a new node. Returning
|
|
57
|
+
None from the replacement builder indicates that the node should be removed.
|
|
58
|
+
|
|
59
|
+
Attributes:
|
|
60
|
+
findMe: A function that finds all locations that match a one or more conditions.
|
|
61
|
+
doThis: A function that does work at each location, such as make a new node, collect information or delete the node.
|
|
62
|
+
|
|
63
|
+
Methods:
|
|
64
|
+
visit(node: ast.AST) -> Optional[ast.AST]:
|
|
65
|
+
Visits each node in the AST, replacing or removing it based on the predicate.
|
|
66
|
+
"""
|
|
67
|
+
def __init__(self
|
|
68
|
+
, findMe: Callable[[ast.AST], bool]
|
|
69
|
+
, doThis: Callable[[ast.AST], ast.AST | Sequence[ast.AST] | None]
|
|
70
|
+
) -> None:
|
|
71
|
+
self.findMe = findMe
|
|
72
|
+
self.doThis = doThis
|
|
73
|
+
|
|
74
|
+
def visit(self, node: ast.AST) -> ast.AST | Sequence[ast.AST] | None:
|
|
75
|
+
if self.findMe(node):
|
|
76
|
+
return self.doThis(node)
|
|
77
|
+
return super().visit(node)
|
|
78
|
+
|
|
79
|
+
def descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callable[[ast.AST], bool]) -> bool:
|
|
80
|
+
""" Return True if any descendant of the node (or the node itself) matches the predicateFunction. """
|
|
81
|
+
matchFound = False
|
|
82
|
+
|
|
83
|
+
class DescendantFinder(ast.NodeVisitor):
|
|
84
|
+
def generic_visit(self, node: ast.AST) -> None:
|
|
85
|
+
nonlocal matchFound
|
|
86
|
+
if predicateFunction(node):
|
|
87
|
+
matchFound = True
|
|
88
|
+
else:
|
|
89
|
+
super().generic_visit(node)
|
|
90
|
+
|
|
91
|
+
DescendantFinder().visit(node)
|
|
92
|
+
return matchFound
|
|
93
|
+
|
|
94
|
+
def executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.AST], bool], actionFunction: Callable[[ast.AST], None]) -> Callable[[ast.AST], None]:
|
|
95
|
+
"""
|
|
96
|
+
Return a new action that will execute actionFunction only if no descendant (or the node itself)
|
|
97
|
+
matches exclusionPredicate.
|
|
98
|
+
"""
|
|
99
|
+
def wrappedAction(node: ast.AST) -> None:
|
|
100
|
+
if not descendantContainsMatchingNode(node, exclusionPredicate):
|
|
101
|
+
actionFunction(node)
|
|
102
|
+
return wrappedAction
|
|
103
|
+
|
|
104
|
+
class ifThis:
|
|
105
|
+
@staticmethod
|
|
106
|
+
def anyOf(*somePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
107
|
+
return lambda nodeTarget: any(predicate(nodeTarget) for predicate in somePredicates)
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], bool]:
|
|
111
|
+
return lambda node: node in container
|
|
112
|
+
|
|
113
|
+
# TODO is this only useable if namespace is not `None`? Yes, but use "" for namespace if necessary.
|
|
114
|
+
@staticmethod
|
|
115
|
+
def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
116
|
+
return lambda nodeFocus: ifThis.CallReallyIs(namespace, identifier)(nodeFocus) and 1 == sum(1 for descendant in ast.walk(nodeFocus) if ifThis.CallReallyIs(namespace, identifier)(descendant))
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], bool]:
|
|
120
|
+
return lambda nodeSubject: (ifThis.isCall(nodeSubject) and ifThis.isName(nodeSubject.func) and ifThis.ast_IdentifierIsIn(container)(nodeSubject.func.id) and ifThis.CallDoesNotCallItself("", nodeSubject.func.id)(nodeSubject))
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
124
|
+
return ifThis.anyOf(ifThis.isCall_Identifier(identifier), ifThis.isCallNamespace_Identifier(namespace, identifier))
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def is_keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
|
|
128
|
+
return isinstance(node, ast.keyword)
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def is_keywordAndValueIsConstant(nodeCheck: ast.AST) -> TypeGuard[ast.keyword]:
|
|
132
|
+
return ifThis.is_keyword(nodeCheck) and ifThis.isConstant(nodeCheck.value)
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
136
|
+
return lambda nodeInstant: ifThis.is_keyword(nodeInstant) and nodeInstant.arg == identifier
|
|
137
|
+
@staticmethod
|
|
138
|
+
def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], bool]:
|
|
139
|
+
return lambda astNode: (ifThis.is_keyword_Identifier(identifier)(astNode) and ifThis.is_keywordAndValueIsConstant(astNode) and ifThis.isConstantEquals(ConstantValue)(astNode.value))
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def isAnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
143
|
+
return isinstance(node, ast.AnnAssign)
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
147
|
+
return ifThis.isAnnAssign(node) and ifThis.isName(node.annotation)
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def isAnnAssignAndTargetIsName(whatNode: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
151
|
+
return ifThis.isAnnAssign(whatNode) and ifThis.isName(whatNode.target)
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def isAnnAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
155
|
+
return lambda nodeStop: ifThis.isAnnAssign(nodeStop) and ifThis.NameReallyIs(identifier)(nodeStop.target)
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def isAnyAssignmentTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
159
|
+
return ifThis.anyOf(ifThis.isAssignOnlyTo(identifier), ifThis.isAnnAssignTo(identifier), ifThis.isAugAssignTo(identifier))
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def isAssign(node: ast.AST) -> TypeGuard[ast.Assign]:
|
|
163
|
+
return isinstance(node, ast.Assign)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def isAssignOnlyTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
167
|
+
return lambda aNode: ifThis.isAssign(aNode) and ifThis.NameReallyIs(identifier)(aNode.targets[0])
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def isAttribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
171
|
+
return isinstance(node, ast.Attribute)
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def isAugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
|
|
175
|
+
return isinstance(node, ast.AugAssign)
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def isAugAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
179
|
+
return lambda nodeQuestion: ifThis.isAugAssign(nodeQuestion) and ifThis.NameReallyIs(identifier)(nodeQuestion.target)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def isCall(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
183
|
+
return isinstance(node, ast.Call)
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
187
|
+
return lambda ImaNode: ifThis.isCall(ImaNode) and ifThis.isName_Identifier(identifier)(ImaNode.func)
|
|
188
|
+
|
|
189
|
+
# TODO what happens if `None` is passed as the namespace?
|
|
190
|
+
@staticmethod
|
|
191
|
+
def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
192
|
+
return lambda node: ifThis.isCall(node) and ifThis.isNameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
196
|
+
return ifThis.isCall(node) and ifThis.isName(node.func)
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
|
|
200
|
+
return isinstance(node, ast.ClassDef)
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
204
|
+
return lambda node: ifThis.isClassDef(node) and node.name == identifier
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
|
|
208
|
+
return isinstance(node, ast.Constant)
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def isConstantEquals(value: Any) -> Callable[[ast.AST], bool]:
|
|
212
|
+
return lambda node: ifThis.isConstant(node) and node.value == value
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
|
|
216
|
+
return isinstance(node, ast.FunctionDef)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
220
|
+
return lambda node: ifThis.isFunctionDef(node) and node.name == identifier
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def isImport(node: ast.AST) -> TypeGuard[ast.Import]:
|
|
224
|
+
return isinstance(node, ast.Import)
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
def isName(node: ast.AST) -> TypeGuard[ast.Name]:
|
|
228
|
+
return isinstance(node, ast.Name)
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
232
|
+
return lambda node: ifThis.isName(node) and node.id == identifier
|
|
233
|
+
|
|
234
|
+
@staticmethod
|
|
235
|
+
def isNameDOTname(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
236
|
+
return ifThis.isAttribute(node) and ifThis.isName(node.value)
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def isNameDOTnameNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
240
|
+
return lambda node: ifThis.isNameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value) and node.attr == identifier
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def isSubscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
|
|
244
|
+
return isinstance(node, ast.Subscript)
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
248
|
+
return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value)
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def isSubscript_Identifier_Identifier(identifier: ast_Identifier, sliceIdentifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
252
|
+
return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) and ifThis.isName_Identifier(sliceIdentifier)(node.slice) # auto-generated
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def NameReallyIs(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
256
|
+
return ifThis.anyOf(ifThis.isName_Identifier(identifier), ifThis.isSubscript_Identifier(identifier))
|
|
257
|
+
|
|
258
|
+
class Make:
|
|
259
|
+
@staticmethod
|
|
260
|
+
def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
|
|
261
|
+
"""Extract keyword parameters from a decorator AST node."""
|
|
262
|
+
dictionaryKeywords: dict[str, Any] = {}
|
|
263
|
+
for keywordItem in astCall.keywords:
|
|
264
|
+
if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
|
|
265
|
+
dictionaryKeywords[keywordItem.arg] = keywordItem.value.value
|
|
266
|
+
return dictionaryKeywords
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def astAlias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
|
|
270
|
+
return ast.alias(name=name, asname=asname)
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def astAnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign:
|
|
274
|
+
""" `simple: int`: uses a clever int-from-boolean to assign the correct value to the `simple` attribute. So, don't add it as a parameter."""
|
|
275
|
+
return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def astAssign(listTargets: Any, value: ast.expr, **keywordArguments: strORintORNone) -> ast.Assign:
|
|
279
|
+
"""keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
280
|
+
return ast.Assign(targets=listTargets, value=value, **keywordArguments)
|
|
281
|
+
|
|
282
|
+
@staticmethod
|
|
283
|
+
def astArg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: strORintORNone) -> ast.arg:
|
|
284
|
+
"""keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
285
|
+
return ast.arg(identifier, annotation, **keywordArguments)
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
def astArgumentsSpecification(posonlyargs: list[ast.arg]=[], args: list[ast.arg]=[], vararg: ast.arg|None=None, kwonlyargs: list[ast.arg]=[], kw_defaults: list[ast.expr|None]=[None], kwarg: ast.arg|None=None, defaults: list[ast.expr]=[]) -> ast.arguments:
|
|
289
|
+
return ast.arguments(posonlyargs=posonlyargs, args=args, vararg=vararg, kwonlyargs=kwonlyargs, kw_defaults=kw_defaults, kwarg=kwarg, defaults=defaults)
|
|
290
|
+
|
|
291
|
+
@staticmethod
|
|
292
|
+
def astCall(caller: ast.Name | ast.Attribute, args: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
|
|
293
|
+
return ast.Call(func=caller, args=list(args) if args else [], keywords=list(list_astKeywords) if list_astKeywords else [])
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def astClassDef(name: ast_Identifier, listBases: list[ast.expr]=[], list_keyword: list[ast.keyword]=[], body: list[ast.stmt]=[], decorator_list: list[ast.expr]=[], **keywordArguments: list_ast_type_paramORintORNone) -> ast.ClassDef:
|
|
297
|
+
"""keywordArguments: type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
298
|
+
return ast.ClassDef(name=name, bases=listBases, keywords=list_keyword, body=body, decorator_list=decorator_list, **keywordArguments)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def astFunctionDef(name: ast_Identifier, argumentsSpecification: ast.arguments=ast.arguments(), body: list[ast.stmt]=[], decorator_list: list[ast.expr]=[], returns: ast.expr|None=None, **keywordArguments: strORlist_ast_type_paramORintORNone) -> ast.FunctionDef:
|
|
302
|
+
"""keywordArguments: type_comment:str|None, type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
303
|
+
return ast.FunctionDef(name=name, args=argumentsSpecification, body=body, decorator_list=decorator_list, returns=returns, **keywordArguments)
|
|
304
|
+
|
|
305
|
+
@staticmethod
|
|
306
|
+
def astImport(moduleName: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
|
|
307
|
+
return ast.Import(names=[Make.astAlias(moduleName, asname)], **keywordArguments)
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def astImportFrom(moduleName: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
|
|
311
|
+
return ast.ImportFrom(module=moduleName, names=list_astAlias, level=0, **keywordArguments)
|
|
312
|
+
|
|
313
|
+
@staticmethod
|
|
314
|
+
def astKeyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
|
|
315
|
+
return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
|
|
319
|
+
return ast.Module(body=body, type_ignores=type_ignores)
|
|
320
|
+
|
|
321
|
+
@staticmethod
|
|
322
|
+
def astName(identifier: ast_Identifier) -> ast.Name:
|
|
323
|
+
return ast.Name(id=identifier, ctx=ast.Load())
|
|
324
|
+
|
|
325
|
+
@staticmethod
|
|
326
|
+
def itDOTname(nameChain: ast.Name | ast.Attribute, dotName: str) -> ast.Attribute:
|
|
327
|
+
return ast.Attribute(value=nameChain, attr=dotName, ctx=ast.Load())
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def nameDOTname(identifier: ast_Identifier, *dotName: str) -> ast.Name | ast.Attribute:
|
|
331
|
+
nameDOTname: ast.Name | ast.Attribute = Make.astName(identifier)
|
|
332
|
+
if not dotName:
|
|
333
|
+
return nameDOTname
|
|
334
|
+
for suffix in dotName:
|
|
335
|
+
nameDOTname = Make.itDOTname(nameDOTname, suffix)
|
|
336
|
+
return nameDOTname
|
|
337
|
+
|
|
338
|
+
@staticmethod
|
|
339
|
+
def astReturn(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
|
|
340
|
+
return ast.Return(value=value, **keywordArguments)
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def astTuple(elements: Sequence[ast.expr], context: ast.expr_context | None = None, **keywordArguments: int) -> ast.Tuple:
|
|
344
|
+
"""context: Load/Store/Del"""
|
|
345
|
+
context = context or ast.Load()
|
|
346
|
+
return ast.Tuple(elts=list(elements), ctx=context, **keywordArguments)
|
|
347
|
+
|
|
348
|
+
class Then:
|
|
349
|
+
@staticmethod
|
|
350
|
+
def insertThisAbove(astStatement: ast.stmt) -> Callable[[ast.stmt], Sequence[ast.stmt]]:
|
|
351
|
+
return lambda aboveMe: [astStatement, aboveMe]
|
|
352
|
+
@staticmethod
|
|
353
|
+
def insertThisBelow(astStatement: ast.stmt) -> Callable[[ast.stmt], Sequence[ast.stmt]]:
|
|
354
|
+
return lambda belowMe: [belowMe, astStatement]
|
|
355
|
+
@staticmethod
|
|
356
|
+
def replaceWith(astStatement: ast.stmt) -> Callable[[ast.stmt], ast.stmt]:
|
|
357
|
+
return lambda replaceMe: astStatement
|
|
358
|
+
@staticmethod
|
|
359
|
+
def removeThis(node: ast.AST) -> None:
|
|
360
|
+
return None
|
|
361
|
+
from mapFolding.someAssemblyRequired.whatWillBe import LedgerOfImports
|
|
362
|
+
@staticmethod
|
|
363
|
+
def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AST], None]:
|
|
364
|
+
return lambda node: ledger.addImportFromStr(logicalPath, cast(ast.Name, cast(ast.AnnAssign, node).annotation).id)
|
|
365
|
+
@staticmethod
|
|
366
|
+
def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AST], None]:
|
|
367
|
+
return lambda node: list_keyword.append(Make.astKeyword(cast(ast.Name, cast(ast.AnnAssign, node).target).id, cast(ast.Name, cast(ast.AnnAssign, node).target)))
|
|
368
|
+
@staticmethod
|
|
369
|
+
def append_targetTo(listName: list[ast.Name]) -> Callable[[ast.AST], None]:
|
|
370
|
+
return lambda node: listName.append(cast(ast.Name, cast(ast.AnnAssign, node).target))
|
|
371
|
+
@staticmethod
|
|
372
|
+
def appendTo(listAST: Sequence[ast.AST]) -> Callable[[ast.AST], None]:
|
|
373
|
+
return lambda node: list(listAST).append(node)
|
|
374
|
+
@staticmethod
|
|
375
|
+
def Z0Z_appendAnnAssignOfNameDOTnameTo(identifier: ast_Identifier, listNameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AST], None]:
|
|
376
|
+
return lambda node: listNameDOTname.append(Make.astAnnAssign(cast(ast.AnnAssign, node).target, cast(ast.AnnAssign, node).annotation, Make.nameDOTname(identifier, cast(ast.Name, cast(ast.AnnAssign, node).target).id)))
|
|
377
|
+
|
|
378
|
+
class FunctionInliner(ast.NodeTransformer):
|
|
379
|
+
def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]) -> None:
|
|
380
|
+
self.dictionaryFunctions: dict[str, ast.FunctionDef] = dictionaryFunctions
|
|
381
|
+
|
|
382
|
+
def inlineFunctionBody(self, callableTargetName: str) -> ast.FunctionDef:
|
|
383
|
+
inlineDefinition: ast.FunctionDef = self.dictionaryFunctions[callableTargetName]
|
|
384
|
+
# Process nested calls within the inlined function
|
|
385
|
+
for astNode in ast.walk(inlineDefinition):
|
|
386
|
+
self.visit(astNode)
|
|
387
|
+
return inlineDefinition
|
|
388
|
+
|
|
389
|
+
def visit_Call(self, node: ast.Call):
|
|
390
|
+
astCall = self.generic_visit(node)
|
|
391
|
+
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryFunctions)(astCall):
|
|
392
|
+
inlineDefinition: ast.FunctionDef = self.inlineFunctionBody(cast(ast.Name, cast(ast.Call, astCall).func).id)
|
|
393
|
+
|
|
394
|
+
if (inlineDefinition and inlineDefinition.body):
|
|
395
|
+
statementTerminating: ast.stmt = inlineDefinition.body[-1]
|
|
396
|
+
|
|
397
|
+
if (isinstance(statementTerminating, ast.Return)
|
|
398
|
+
and statementTerminating.value is not None):
|
|
399
|
+
return self.visit(statementTerminating.value)
|
|
400
|
+
elif isinstance(statementTerminating, ast.Expr):
|
|
401
|
+
return self.visit(statementTerminating.value)
|
|
402
|
+
else:
|
|
403
|
+
return ast.Constant(value=None)
|
|
404
|
+
return astCall
|
|
405
|
+
|
|
406
|
+
def visit_Expr(self, node: ast.Expr):
|
|
407
|
+
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryFunctions)(node.value):
|
|
408
|
+
inlineDefinition: ast.FunctionDef = self.inlineFunctionBody(cast(ast.Name, cast(ast.Call, node.value).func).id)
|
|
409
|
+
return [self.visit(stmt) for stmt in inlineDefinition.body]
|
|
410
|
+
return self.generic_visit(node)
|
|
411
|
+
# TODO When resolving the ledger of imports, remove self-referential imports
|
|
412
|
+
|
|
413
|
+
def extractClassDef(identifier: ast_Identifier, module: ast.Module) -> ast.ClassDef | None:
|
|
414
|
+
sherpa: list[ast.ClassDef] = []
|
|
415
|
+
extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
|
|
416
|
+
extractor.visit(module)
|
|
417
|
+
astClassDef = sherpa[0] if sherpa else None
|
|
418
|
+
return astClassDef
|
|
419
|
+
|
|
420
|
+
def extractFunctionDef(identifier: ast_Identifier, module: ast.Module) -> ast.FunctionDef | None:
|
|
421
|
+
sherpa: list[ast.FunctionDef] = []
|
|
422
|
+
extractor = NodeCollector(ifThis.isFunctionDef_Identifier(identifier), [Then.appendTo(sherpa)])
|
|
423
|
+
extractor.visit(module)
|
|
424
|
+
astClassDef = sherpa[0] if sherpa else None
|
|
425
|
+
return astClassDef
|