mapFolding 0.8.2__py3-none-any.whl → 0.8.4__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 -2
- mapFolding/basecamp.py +11 -5
- mapFolding/filesystem.py +134 -109
- mapFolding/oeis.py +1 -1
- mapFolding/reference/__init__.py +7 -0
- mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +197 -0
- mapFolding/reference/jobsCompleted/__init__.py +50 -0
- mapFolding/reference/jobsCompleted/p2x19/p2x19.py +29 -0
- mapFolding/someAssemblyRequired/__init__.py +37 -18
- mapFolding/someAssemblyRequired/_theTypes.py +35 -0
- mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
- mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
- mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
- mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +20 -1
- mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +112 -149
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
- mapFolding/someAssemblyRequired/transformDataStructures.py +167 -100
- mapFolding/someAssemblyRequired/transformationTools.py +63 -678
- mapFolding/syntheticModules/__init__.py +1 -0
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
- mapFolding/theDao.py +13 -11
- mapFolding/theSSOT.py +69 -119
- {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/METADATA +4 -2
- mapfolding-0.8.4.dist-info/RECORD +49 -0
- {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/WHEEL +1 -1
- tests/conftest.py +34 -29
- tests/test_computations.py +40 -31
- tests/test_filesystem.py +3 -3
- tests/test_other.py +4 -3
- mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
- mapfolding-0.8.2.dist-info/RECORD +0 -39
- {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/top_level.txt +0 -0
|
@@ -23,642 +23,69 @@ they are designed as general-purpose utilities applicable to a wide range of cod
|
|
|
23
23
|
transformation scenarios beyond the scope of this package.
|
|
24
24
|
"""
|
|
25
25
|
from autoflake import fix_code as autoflake_fix_code
|
|
26
|
-
from collections import
|
|
27
|
-
from collections.abc import Callable, Container, Sequence
|
|
26
|
+
from collections.abc import Callable, Mapping
|
|
28
27
|
from copy import deepcopy
|
|
29
|
-
from importlib import import_module as importlib_import_module
|
|
30
|
-
from inspect import getsource as inspect_getsource
|
|
31
28
|
from mapFolding.filesystem import writeStringToHere
|
|
32
|
-
from mapFolding.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
theFormatStrModuleForCallableSynthetic,
|
|
36
|
-
theFormatStrModuleSynthetic,
|
|
37
|
-
theLogicalPathModuleDispatcherSynthetic,
|
|
38
|
-
theModuleDispatcherSynthetic,
|
|
39
|
-
)
|
|
29
|
+
from mapFolding.someAssemblyRequired import ast_Identifier, be, ifThis, Make, NodeChanger, NodeTourist, Then, typeCertified
|
|
30
|
+
from mapFolding.someAssemblyRequired._toolboxContainers import IngredientsModule
|
|
31
|
+
from mapFolding.theSSOT import raiseIfNoneGitHubIssueNumber3
|
|
40
32
|
from os import PathLike
|
|
41
|
-
from pathlib import
|
|
42
|
-
from
|
|
43
|
-
from typing import Any, cast, Generic, TypeAlias, TypeGuard, TypeVar
|
|
44
|
-
from Z0Z_tools import updateExtendPolishDictionaryLists
|
|
33
|
+
from pathlib import PurePath
|
|
34
|
+
from typing import Any
|
|
45
35
|
import ast
|
|
46
|
-
import dataclasses
|
|
47
36
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
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.
|
|
51
|
-
|
|
52
|
-
astName: always means `ast.Name`.
|
|
53
|
-
Name: uppercase, _should_ be interchangeable with astName, even in camelCase.
|
|
54
|
-
Hunter: ^^ did you do that ^^ ? Are you sure? You just fixed some that should have been "_name" because it confused you.
|
|
55
|
-
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.
|
|
56
|
-
_Identifier: very strongly correlates with the private `ast._Identifier`, which is a TypeAlias for `str`.
|
|
57
|
-
identifier: lowercase, a general term that includes the above and other Python identifiers.
|
|
58
|
-
Identifier: uppercase, without the leading underscore should only appear in camelCase and means "identifier", lowercase.
|
|
59
|
-
namespace: lowercase, in dotted-names, such as `pathlib.Path` or `collections.abc`, "namespace" is the part before the dot.
|
|
60
|
-
Namespace: uppercase, should only appear in camelCase and means "namespace", lowercase.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
# Would `LibCST` be better than `ast` in some cases? https://github.com/hunterhogan/mapFolding/issues/7
|
|
64
|
-
|
|
65
|
-
ast_expr_Slice: TypeAlias = ast.expr
|
|
66
|
-
ast_Identifier: TypeAlias = str
|
|
67
|
-
astClassHasAttributeDOTname: TypeAlias = ast.FunctionDef | ast.ClassDef | ast.AsyncFunctionDef
|
|
68
|
-
astMosDef = TypeVar('astMosDef', bound=astClassHasAttributeDOTname)
|
|
69
|
-
list_ast_type_paramORintORNone: TypeAlias = Any
|
|
70
|
-
nodeType = TypeVar('nodeType', bound=ast.AST)
|
|
71
|
-
strDotStrCuzPyStoopid: TypeAlias = str
|
|
72
|
-
strORintORNone: TypeAlias = Any
|
|
73
|
-
strORlist_ast_type_paramORintORNone: TypeAlias = Any
|
|
74
|
-
|
|
75
|
-
class NodeCollector(Generic[nodeType], ast.NodeVisitor):
|
|
76
|
-
"""A node visitor that collects data via one or more actions when a predicate is met."""
|
|
77
|
-
def __init__(self, findThis: Callable[[ast.AST], TypeGuard[nodeType] | bool], doThat: list[Callable[[nodeType], Any]]) -> None:
|
|
78
|
-
self.findThis = findThis
|
|
79
|
-
self.doThat = doThat
|
|
80
|
-
|
|
81
|
-
def visit(self, node: ast.AST) -> None:
|
|
82
|
-
if self.findThis(node):
|
|
83
|
-
for action in self.doThat:
|
|
84
|
-
action(cast(nodeType, node))
|
|
85
|
-
self.generic_visit(node)
|
|
86
|
-
|
|
87
|
-
class NodeReplacer(Generic[nodeType], ast.NodeTransformer):
|
|
88
|
-
"""A node transformer that replaces or removes AST nodes based on a condition."""
|
|
89
|
-
def __init__(self, findThis: Callable[[ast.AST], TypeGuard[nodeType] | bool], doThat: Callable[[nodeType], ast.AST | Sequence[ast.AST] | None]) -> None:
|
|
90
|
-
self.findThis = findThis
|
|
91
|
-
self.doThat = doThat
|
|
92
|
-
|
|
93
|
-
def visit(self, node: ast.AST) -> ast.AST | Sequence[ast.AST] | None:
|
|
94
|
-
if self.findThis(node):
|
|
95
|
-
return self.doThat(cast(nodeType, node))
|
|
96
|
-
return super().visit(node)
|
|
97
|
-
|
|
98
|
-
class ifThis:
|
|
99
|
-
@staticmethod
|
|
100
|
-
def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]:
|
|
101
|
-
return lambda node: node in container
|
|
102
|
-
@staticmethod
|
|
103
|
-
def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
104
|
-
"""If `namespace` is not applicable to your case, then call with `namespace=""`."""
|
|
105
|
-
return lambda node: ifThis.matchesMeButNotAnyDescendant(ifThis.CallReallyIs(namespace, identifier))(node)
|
|
106
|
-
@staticmethod
|
|
107
|
-
def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
108
|
-
return lambda node: ifThis.isCall(node) and ifThis.isName(node.func) and ifThis.ast_IdentifierIsIn(container)(node.func.id) and ifThis.CallDoesNotCallItself("", node.func.id)(node)
|
|
109
|
-
@staticmethod
|
|
110
|
-
def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
111
|
-
return ifThis.isAnyOf(ifThis.isCall_Identifier(identifier), ifThis.isCallNamespace_Identifier(namespace, identifier))
|
|
112
|
-
@staticmethod
|
|
113
|
-
def is_keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
|
|
114
|
-
return isinstance(node, ast.keyword)
|
|
115
|
-
@staticmethod
|
|
116
|
-
def is_keywordAndValueIsConstant(node: ast.AST) -> TypeGuard[ast.keyword]:
|
|
117
|
-
return ifThis.is_keyword(node) and ifThis.isConstant(node.value)
|
|
118
|
-
@staticmethod
|
|
119
|
-
def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
|
|
120
|
-
def workhorse(node: ast.AST) -> TypeGuard[ast.keyword] | bool:
|
|
121
|
-
return ifThis.is_keyword(node) and node.arg == identifier
|
|
122
|
-
return workhorse
|
|
123
|
-
@staticmethod
|
|
124
|
-
def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
|
|
125
|
-
return lambda node: ifThis.is_keyword_Identifier(identifier)(node) and ifThis.is_keywordAndValueIsConstant(node) and ifThis.isConstantEquals(ConstantValue)(node.value)
|
|
126
|
-
@staticmethod
|
|
127
|
-
def isAllOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
128
|
-
return lambda node: all(predicate(node) for predicate in thesePredicates)
|
|
129
|
-
@staticmethod
|
|
130
|
-
def isAnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
131
|
-
return isinstance(node, ast.AnnAssign)
|
|
132
|
-
@staticmethod
|
|
133
|
-
def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
134
|
-
return ifThis.isAnnAssign(node) and ifThis.isName(node.annotation)
|
|
135
|
-
@staticmethod
|
|
136
|
-
def isAnnAssignAndTargetIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
137
|
-
return ifThis.isAnnAssign(node) and ifThis.isName(node.target)
|
|
138
|
-
@staticmethod
|
|
139
|
-
def isAnnAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
|
|
140
|
-
return lambda node: ifThis.isAnnAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target)
|
|
141
|
-
@staticmethod
|
|
142
|
-
def isAnyAssignmentTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
143
|
-
return ifThis.isAnyOf(ifThis.isAssignOnlyTo(identifier), ifThis.isAnnAssignTo(identifier), ifThis.isAugAssignTo(identifier))
|
|
144
|
-
@staticmethod
|
|
145
|
-
def isAnyCompare(node: ast.AST) -> TypeGuard[ast.Compare] | TypeGuard[ast.BoolOp]:
|
|
146
|
-
return ifThis.isCompare(node) or ifThis.isBoolOp(node)
|
|
147
|
-
@staticmethod
|
|
148
|
-
def isAnyOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
149
|
-
return lambda node: any(predicate(node) for predicate in thesePredicates)
|
|
150
|
-
@staticmethod
|
|
151
|
-
def isAssign(node: ast.AST) -> TypeGuard[ast.Assign]:
|
|
152
|
-
return isinstance(node, ast.Assign)
|
|
153
|
-
@staticmethod
|
|
154
|
-
def isAssignAndValueIsCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
155
|
-
return lambda node: ifThis.isAssign(node) and ifThis.isCall_Identifier(identifier)(node.value)
|
|
156
|
-
@staticmethod
|
|
157
|
-
def isAssignAndValueIsCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
158
|
-
return ifThis.isAssignAndValueIs(ifThis.isCallNamespace_Identifier(namespace, identifier))
|
|
159
|
-
@staticmethod
|
|
160
|
-
def isAssignOnlyTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
161
|
-
return lambda node: ifThis.isAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.targets[0])
|
|
162
|
-
@staticmethod
|
|
163
|
-
def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
164
|
-
"""node is Assign and node.targets[0] matches `targets0Predicate`."""
|
|
165
|
-
return lambda node: ifThis.isAssign(node) and targets0Predicate(node.targets[0])
|
|
166
|
-
@staticmethod
|
|
167
|
-
def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
168
|
-
"""node is ast.Assign and node.value matches `valuePredicate`.
|
|
169
|
-
Parameters:
|
|
170
|
-
valuePredicate: Function that evaluates the value of the assignment
|
|
171
|
-
Returns:
|
|
172
|
-
predicate: matches assignments with values meeting the criteria
|
|
173
|
-
"""
|
|
174
|
-
return lambda node: ifThis.isAssign(node) and valuePredicate(node.value)
|
|
175
|
-
@staticmethod
|
|
176
|
-
def isAttribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
177
|
-
return isinstance(node, ast.Attribute)
|
|
178
|
-
@staticmethod
|
|
179
|
-
def isAugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
|
|
180
|
-
return isinstance(node, ast.AugAssign)
|
|
181
|
-
@staticmethod
|
|
182
|
-
def isAugAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AugAssign] | bool]:
|
|
183
|
-
return lambda node: ifThis.isAugAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target)
|
|
184
|
-
@staticmethod
|
|
185
|
-
def isBoolOp(node: ast.AST) -> TypeGuard[ast.BoolOp]:
|
|
186
|
-
return isinstance(node, ast.BoolOp)
|
|
187
|
-
@staticmethod
|
|
188
|
-
def isCall(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
189
|
-
return isinstance(node, ast.Call)
|
|
190
|
-
@staticmethod
|
|
191
|
-
def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
192
|
-
return lambda node: ifThis.isCall(node) and ifThis.isName_Identifier(identifier)(node.func)
|
|
193
|
-
@staticmethod
|
|
194
|
-
def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
195
|
-
return lambda node: ifThis.isCall(node) and ifThis.is_nameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
|
|
196
|
-
@staticmethod
|
|
197
|
-
def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
198
|
-
return ifThis.isCall(node) and ifThis.isName(node.func)
|
|
199
|
-
@staticmethod
|
|
200
|
-
def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
|
|
201
|
-
return isinstance(node, ast.ClassDef)
|
|
202
|
-
@staticmethod
|
|
203
|
-
def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
|
|
204
|
-
return lambda node: ifThis.isClassDef(node) and node.name == identifier
|
|
205
|
-
@staticmethod
|
|
206
|
-
def isCompare(node: ast.AST) -> TypeGuard[ast.Compare]:
|
|
207
|
-
return isinstance(node, ast.Compare)
|
|
208
|
-
@staticmethod
|
|
209
|
-
def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
|
|
210
|
-
return isinstance(node, ast.Constant)
|
|
211
|
-
@staticmethod
|
|
212
|
-
def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
|
|
213
|
-
return lambda node: ifThis.isConstant(node) and node.value == value
|
|
214
|
-
@staticmethod
|
|
215
|
-
def isExpr(node: ast.AST) -> TypeGuard[ast.Expr]:
|
|
216
|
-
return isinstance(node, ast.Expr)
|
|
217
|
-
@staticmethod
|
|
218
|
-
def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
|
|
219
|
-
return isinstance(node, ast.FunctionDef)
|
|
220
|
-
@staticmethod
|
|
221
|
-
def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef] | bool]:
|
|
222
|
-
return lambda node: ifThis.isFunctionDef(node) and node.name == identifier
|
|
223
|
-
@staticmethod
|
|
224
|
-
def isImport(node: ast.AST) -> TypeGuard[ast.Import]:
|
|
225
|
-
return isinstance(node, ast.Import)
|
|
226
|
-
@staticmethod
|
|
227
|
-
def isName(node: ast.AST) -> TypeGuard[ast.Name]:
|
|
228
|
-
"""TODO
|
|
229
|
-
ast.Name()
|
|
230
|
-
ast.Attribute()
|
|
231
|
-
ast.Subscript()
|
|
232
|
-
ast.Starred()
|
|
233
|
-
"""
|
|
234
|
-
return isinstance(node, ast.Name)
|
|
235
|
-
@staticmethod
|
|
236
|
-
def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name] | bool]:
|
|
237
|
-
return lambda node: ifThis.isName(node) and node.id == identifier
|
|
238
|
-
@staticmethod
|
|
239
|
-
def is_nameDOTname(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
240
|
-
return ifThis.isAttribute(node) and ifThis.isName(node.value)
|
|
241
|
-
@staticmethod
|
|
242
|
-
def is_nameDOTnameNamespace(namespace: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
|
|
243
|
-
return lambda node: ifThis.is_nameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value)
|
|
244
|
-
@staticmethod
|
|
245
|
-
def is_nameDOTnameNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
|
|
246
|
-
return lambda node: ifThis.is_nameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value) and node.attr == identifier
|
|
247
|
-
@staticmethod
|
|
248
|
-
def NameReallyIs_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
|
|
249
|
-
# The following logic is incomplete.
|
|
250
|
-
return ifThis.isAnyOf(ifThis.isName_Identifier(identifier), ifThis.isSubscriptIsName_Identifier(identifier))
|
|
251
|
-
@staticmethod
|
|
252
|
-
def isReturn(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
253
|
-
return isinstance(node, ast.Return)
|
|
254
|
-
@staticmethod
|
|
255
|
-
def isReturnAnyCompare(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
256
|
-
return ifThis.isReturn(node) and node.value is not None and ifThis.isAnyCompare(node.value)
|
|
257
|
-
@staticmethod
|
|
258
|
-
def isReturnUnaryOp(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
259
|
-
return ifThis.isReturn(node) and node.value is not None and ifThis.isUnaryOp(node.value)
|
|
260
|
-
@staticmethod
|
|
261
|
-
def isSubscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
|
|
262
|
-
return isinstance(node, ast.Subscript)
|
|
263
|
-
@staticmethod
|
|
264
|
-
def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript]]:
|
|
265
|
-
"""node is `ast.Subscript` and the top-level `ast.Name` is `identifier`
|
|
266
|
-
Parameters:
|
|
267
|
-
identifier: The identifier to look for in the value chain
|
|
268
|
-
Returns:
|
|
269
|
-
predicate: function that checks if a node matches the criteria
|
|
270
|
-
"""
|
|
271
|
-
def predicate(node: ast.AST) -> TypeGuard[ast.Subscript]:
|
|
272
|
-
if not ifThis.isSubscript(node):
|
|
273
|
-
return False
|
|
274
|
-
def checkNodeDOTvalue(nodeDOTvalue: ast.AST) -> bool:
|
|
275
|
-
if ifThis.isName(nodeDOTvalue):
|
|
276
|
-
if nodeDOTvalue.id == identifier:
|
|
277
|
-
return True
|
|
278
|
-
elif hasattr(nodeDOTvalue, "value"):
|
|
279
|
-
return checkNodeDOTvalue(nodeDOTvalue.value) # type: ignore
|
|
280
|
-
return False
|
|
281
|
-
return checkNodeDOTvalue(node.value)
|
|
282
|
-
return predicate
|
|
283
|
-
@staticmethod
|
|
284
|
-
def isSubscriptIsName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
|
|
285
|
-
return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value)
|
|
286
|
-
@staticmethod
|
|
287
|
-
def isSubscript_Identifier_Identifier(identifier: ast_Identifier, sliceIdentifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
|
|
288
|
-
return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) and ifThis.isName_Identifier(sliceIdentifier)(node.slice)
|
|
289
|
-
@staticmethod
|
|
290
|
-
def isUnaryOp(node: ast.AST) -> TypeGuard[ast.UnaryOp]:
|
|
291
|
-
return isinstance(node, ast.UnaryOp)
|
|
292
|
-
# TODO Does this work?
|
|
293
|
-
@staticmethod
|
|
294
|
-
def matchesAtLeast1Descendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
295
|
-
"""Create a predicate that returns True if any descendant of the node matches the given predicate."""
|
|
296
|
-
return lambda node: not ifThis.matchesNoDescendant(predicate)(node)
|
|
297
|
-
# TODO Does this work?
|
|
298
|
-
@staticmethod
|
|
299
|
-
def matchesMeAndMyDescendantsExactlyNTimes(predicate: Callable[[ast.AST], bool], nTimes: int) -> Callable[[ast.AST], bool]:
|
|
300
|
-
"""Create a predicate that returns True if exactly 'count' nodes in the tree match the predicate."""
|
|
301
|
-
def countMatchingNodes(node: ast.AST) -> bool:
|
|
302
|
-
matches = sum(1 for descendant in ast.walk(node) if predicate(descendant))
|
|
303
|
-
return matches == nTimes
|
|
304
|
-
return countMatchingNodes
|
|
305
|
-
@staticmethod
|
|
306
|
-
def matchesMeButNotAnyDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
307
|
-
"""Create a predicate that returns True if the node matches but none of its descendants match the predicate."""
|
|
308
|
-
return lambda node: predicate(node) and ifThis.matchesNoDescendant(predicate)(node)
|
|
309
|
-
@staticmethod
|
|
310
|
-
def matchesNoDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
|
|
311
|
-
"""Create a predicate that returns True if no descendant of the node matches the given predicate."""
|
|
312
|
-
def checkNoMatchingDescendant(node: ast.AST) -> bool:
|
|
313
|
-
for descendant in ast.walk(node):
|
|
314
|
-
if descendant is not node and predicate(descendant):
|
|
315
|
-
return False
|
|
316
|
-
return True
|
|
317
|
-
return checkNoMatchingDescendant
|
|
318
|
-
@staticmethod
|
|
319
|
-
def onlyReturnAnyCompare(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
|
|
320
|
-
return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnAnyCompare(astFunctionDef.body[0])
|
|
321
|
-
@staticmethod
|
|
322
|
-
def onlyReturnUnaryOp(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
|
|
323
|
-
return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnUnaryOp(astFunctionDef.body[0])
|
|
324
|
-
|
|
325
|
-
class Make:
|
|
326
|
-
@staticmethod
|
|
327
|
-
def ast_arg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: strORintORNone) -> ast.arg:
|
|
328
|
-
"""keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
329
|
-
return ast.arg(identifier, annotation, **keywordArguments)
|
|
330
|
-
@staticmethod
|
|
331
|
-
def ast_keyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
|
|
332
|
-
return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
|
|
333
|
-
@staticmethod
|
|
334
|
-
def astAlias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
|
|
335
|
-
return ast.alias(name, asname)
|
|
336
|
-
@staticmethod
|
|
337
|
-
def astAnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign:
|
|
338
|
-
"""`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."""
|
|
339
|
-
return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
|
|
340
|
-
@staticmethod
|
|
341
|
-
def astAssign(listTargets: Any, value: ast.expr, **keywordArguments: strORintORNone) -> ast.Assign:
|
|
342
|
-
"""keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
343
|
-
return ast.Assign(targets=listTargets, value=value, **keywordArguments)
|
|
344
|
-
@staticmethod
|
|
345
|
-
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:
|
|
346
|
-
return ast.arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
|
|
347
|
-
@staticmethod
|
|
348
|
-
def astAttribute(value: ast.expr, attribute: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Attribute:
|
|
349
|
-
"""
|
|
350
|
-
Parameters:
|
|
351
|
-
value: the part before the dot (hint `ast.Name` for nameDOTname)
|
|
352
|
-
attribute: the `str` after the dot
|
|
353
|
-
context (ast.Load()): Load/Store/Del"""
|
|
354
|
-
return ast.Attribute(value, attribute, context, **keywordArguments)
|
|
355
|
-
@staticmethod
|
|
356
|
-
def astCall(caller: ast.Name | ast.Attribute, listArguments: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
|
|
357
|
-
return ast.Call(func=caller, args=list(listArguments) if listArguments else [], keywords=list(list_astKeywords) if list_astKeywords else [])
|
|
358
|
-
@staticmethod
|
|
359
|
-
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:
|
|
360
|
-
"""keywordArguments: type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
361
|
-
return ast.ClassDef(name=name, bases=listBases, keywords=list_keyword, body=body, decorator_list=decorator_list, **keywordArguments)
|
|
362
|
-
@staticmethod
|
|
363
|
-
def astConstant(value: Any, **keywordArguments: strORintORNone) -> ast.Constant:
|
|
364
|
-
"""value: str|int|float|bool|None|bytes|bytearray|memoryview|complex|list|tuple|dict|set, or any other type that can be represented as a constant in Python.
|
|
365
|
-
keywordArguments: kind:str, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
|
|
366
|
-
return ast.Constant(value, **keywordArguments)
|
|
367
|
-
@staticmethod
|
|
368
|
-
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:
|
|
369
|
-
"""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"""
|
|
370
|
-
return ast.FunctionDef(name=name, args=argumentsSpecification, body=body, decorator_list=decorator_list, returns=returns, **keywordArguments)
|
|
371
|
-
@staticmethod
|
|
372
|
-
def astImport(moduleName: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
|
|
373
|
-
return ast.Import(names=[Make.astAlias(moduleName, asname)], **keywordArguments)
|
|
374
|
-
@staticmethod
|
|
375
|
-
def astImportFrom(moduleName: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
|
|
376
|
-
return ast.ImportFrom(module=moduleName, names=list_astAlias, level=0, **keywordArguments)
|
|
377
|
-
@staticmethod
|
|
378
|
-
def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
|
|
379
|
-
return ast.Module(body, type_ignores)
|
|
380
|
-
@staticmethod
|
|
381
|
-
def astName(identifier: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Name:
|
|
382
|
-
return ast.Name(identifier, context, **keywordArguments)
|
|
383
|
-
@staticmethod
|
|
384
|
-
def itDOTname(nameChain: ast.Name | ast.Attribute, dotName: str) -> ast.Attribute:
|
|
385
|
-
return ast.Attribute(value=nameChain, attr=dotName, ctx=ast.Load())
|
|
386
|
-
@staticmethod
|
|
387
|
-
# TODO rewrite with all parameters
|
|
388
|
-
def nameDOTname(identifier: ast_Identifier, *dotName: str) -> ast.Name | ast.Attribute:
|
|
389
|
-
nameDOTname: ast.Name | ast.Attribute = Make.astName(identifier)
|
|
390
|
-
if not dotName:
|
|
391
|
-
return nameDOTname
|
|
392
|
-
for suffix in dotName:
|
|
393
|
-
nameDOTname = Make.itDOTname(nameDOTname, suffix)
|
|
394
|
-
return nameDOTname
|
|
395
|
-
@staticmethod
|
|
396
|
-
def astReturn(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
|
|
397
|
-
return ast.Return(value, **keywordArguments)
|
|
398
|
-
@staticmethod
|
|
399
|
-
def astSubscript(value: ast.expr, slice: ast_expr_Slice, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Subscript:
|
|
400
|
-
return ast.Subscript(value, slice, ctx=context, **keywordArguments)
|
|
401
|
-
@staticmethod
|
|
402
|
-
def astTuple(elements: Sequence[ast.expr], context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Tuple:
|
|
403
|
-
"""context: Load/Store/Del"""
|
|
404
|
-
return ast.Tuple(elts=list(elements), ctx=context, **keywordArguments)
|
|
405
|
-
|
|
406
|
-
class LedgerOfImports:
|
|
407
|
-
# TODO When resolving the ledger of imports, remove self-referential imports
|
|
408
|
-
|
|
409
|
-
def __init__(self, startWith: ast.AST | None = None) -> None:
|
|
410
|
-
self.dictionaryImportFrom: dict[str, list[tuple[str, str | None]]] = defaultdict(list)
|
|
411
|
-
self.listImport: list[str] = []
|
|
412
|
-
|
|
413
|
-
if startWith:
|
|
414
|
-
self.walkThis(startWith)
|
|
415
|
-
|
|
416
|
-
def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
|
|
417
|
-
assert isinstance(astImport_, (ast.Import, ast.ImportFrom)), f"Expected ast.Import or ast.ImportFrom, got {type(astImport_)}"
|
|
418
|
-
if isinstance(astImport_, ast.Import):
|
|
419
|
-
for alias in astImport_.names:
|
|
420
|
-
self.listImport.append(alias.name)
|
|
421
|
-
else:
|
|
422
|
-
if astImport_.module is not None:
|
|
423
|
-
for alias in astImport_.names:
|
|
424
|
-
self.dictionaryImportFrom[astImport_.module].append((alias.name, alias.asname))
|
|
425
|
-
|
|
426
|
-
def addImportStr(self, module: str) -> None:
|
|
427
|
-
self.listImport.append(module)
|
|
428
|
-
|
|
429
|
-
def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
|
|
430
|
-
self.dictionaryImportFrom[module].append((name, asname))
|
|
431
|
-
|
|
432
|
-
def exportListModuleNames(self) -> list[str]:
|
|
433
|
-
listModuleNames: list[str] = list(self.dictionaryImportFrom.keys())
|
|
434
|
-
listModuleNames.extend(self.listImport)
|
|
435
|
-
return sorted(set(listModuleNames))
|
|
436
|
-
|
|
437
|
-
def makeListAst(self) -> list[ast.ImportFrom | ast.Import]:
|
|
438
|
-
listAstImportFrom: list[ast.ImportFrom] = []
|
|
439
|
-
|
|
440
|
-
for module, listOfNameTuples in sorted(self.dictionaryImportFrom.items()):
|
|
441
|
-
listOfNameTuples = sorted(list(set(listOfNameTuples)), key=lambda nameTuple: nameTuple[0])
|
|
442
|
-
listAlias: list[ast.alias] = []
|
|
443
|
-
for name, asname in listOfNameTuples:
|
|
444
|
-
listAlias.append(Make.astAlias(name, asname))
|
|
445
|
-
listAstImportFrom.append(Make.astImportFrom(module, listAlias))
|
|
446
|
-
|
|
447
|
-
listAstImport: list[ast.Import] = [Make.astImport(name) for name in sorted(set(self.listImport))]
|
|
448
|
-
return listAstImportFrom + listAstImport
|
|
449
|
-
|
|
450
|
-
def update(self, *fromLedger: 'LedgerOfImports') -> None:
|
|
451
|
-
"""Update this ledger with imports from one or more other ledgers.
|
|
452
|
-
Parameters:
|
|
453
|
-
*fromLedger: One or more other `LedgerOfImports` objects from which to merge.
|
|
454
|
-
"""
|
|
455
|
-
self.dictionaryImportFrom = updateExtendPolishDictionaryLists(self.dictionaryImportFrom, *(ledger.dictionaryImportFrom for ledger in fromLedger), destroyDuplicates=True, reorderLists=True)
|
|
456
|
-
|
|
457
|
-
for ledger in fromLedger:
|
|
458
|
-
self.listImport.extend(ledger.listImport)
|
|
37
|
+
def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
|
|
38
|
+
return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
|
|
459
39
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if isinstance(smurf, (ast.Import, ast.ImportFrom)):
|
|
463
|
-
self.addAst(smurf)
|
|
40
|
+
def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
|
|
41
|
+
return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
|
|
464
42
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
@staticmethod
|
|
476
|
-
def insertThisBelow(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
|
|
477
|
-
return lambda belowMe: [belowMe, *list_astAST]
|
|
478
|
-
@staticmethod
|
|
479
|
-
def removeThis(_node: ast.AST) -> None:
|
|
480
|
-
return None
|
|
481
|
-
@staticmethod
|
|
482
|
-
def replaceWith(astAST: ast.AST) -> Callable[[ast.AST], ast.AST]:
|
|
483
|
-
return lambda _replaceMe: astAST
|
|
484
|
-
@staticmethod
|
|
485
|
-
def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
|
|
486
|
-
return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
|
|
487
|
-
@staticmethod
|
|
488
|
-
def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AnnAssign], None]:
|
|
489
|
-
return lambda node: ledger.addImportFromStr(logicalPath, node.annotation.id) # type: ignore
|
|
490
|
-
@staticmethod
|
|
491
|
-
def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AnnAssign], None]:
|
|
492
|
-
return lambda node: list_keyword.append(Make.ast_keyword(node.target.id, node.target)) # type: ignore
|
|
493
|
-
@staticmethod
|
|
494
|
-
def Z0Z_appendAnnAssignOf_nameDOTnameTo(identifier: ast_Identifier, list_nameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AnnAssign], None]:
|
|
495
|
-
return lambda node: list_nameDOTname.append(Make.astAnnAssign(node.target, node.annotation, Make.nameDOTname(identifier, node.target.id))) # type: ignore
|
|
496
|
-
|
|
497
|
-
@dataclasses.dataclass
|
|
498
|
-
class IngredientsFunction:
|
|
499
|
-
"""Everything necessary to integrate a function into a module should be here."""
|
|
500
|
-
astFunctionDef: ast.FunctionDef # hint `Make.astFunctionDef`
|
|
501
|
-
imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
|
|
502
|
-
|
|
503
|
-
@dataclasses.dataclass
|
|
504
|
-
class IngredientsModule:
|
|
505
|
-
"""Everything necessary to create one _logical_ `ast.Module` should be here.
|
|
506
|
-
Extrinsic qualities should _probably_ be handled externally."""
|
|
507
|
-
ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
|
|
508
|
-
|
|
509
|
-
# init var with an existing module? method to deconstruct an existing module?
|
|
510
|
-
|
|
511
|
-
# `body` attribute of `ast.Module`
|
|
512
|
-
imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
|
|
513
|
-
prologue: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
514
|
-
functions: list[ast.FunctionDef | ast.stmt] = dataclasses.field(default_factory=list)
|
|
515
|
-
epilogue: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
516
|
-
launcher: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
517
|
-
|
|
518
|
-
# parameter for `ast.Module` constructor
|
|
519
|
-
type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
|
|
520
|
-
|
|
521
|
-
def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
|
|
522
|
-
if ingredientsFunction is not None:
|
|
523
|
-
if isinstance(ingredientsFunction, IngredientsFunction):
|
|
524
|
-
self.addIngredientsFunction(ingredientsFunction)
|
|
525
|
-
else:
|
|
526
|
-
self.addIngredientsFunction(*ingredientsFunction)
|
|
527
|
-
|
|
528
|
-
def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
|
|
529
|
-
"""Add one or more `IngredientsFunction`."""
|
|
530
|
-
listLedgers: list[LedgerOfImports] = []
|
|
531
|
-
for definition in ingredientsFunction:
|
|
532
|
-
self.functions.append(definition.astFunctionDef)
|
|
533
|
-
listLedgers.append(definition.imports)
|
|
534
|
-
self.imports.update(*listLedgers)
|
|
535
|
-
|
|
536
|
-
def _makeModuleBody(self) -> list[ast.stmt]:
|
|
537
|
-
body: list[ast.stmt] = []
|
|
538
|
-
body.extend(self.imports.makeListAst())
|
|
539
|
-
body.extend(self.prologue)
|
|
540
|
-
body.extend(self.functions)
|
|
541
|
-
body.extend(self.epilogue)
|
|
542
|
-
body.extend(self.launcher)
|
|
543
|
-
# TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
|
|
544
|
-
return body
|
|
545
|
-
|
|
546
|
-
def export(self) -> ast.Module:
|
|
547
|
-
"""Create a new `ast.Module` from the ingredients."""
|
|
548
|
-
return Make.astModule(self._makeModuleBody(), self.type_ignores)
|
|
549
|
-
|
|
550
|
-
@dataclasses.dataclass
|
|
551
|
-
class RecipeSynthesizeFlow:
|
|
552
|
-
"""Settings for synthesizing flow."""
|
|
553
|
-
# ========================================
|
|
554
|
-
# Source
|
|
555
|
-
sourceAlgorithm: ModuleType = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
|
|
556
|
-
sourcePython: str = inspect_getsource(sourceAlgorithm)
|
|
557
|
-
source_astModule: ast.Module = ast.parse(sourcePython)
|
|
558
|
-
|
|
559
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
560
|
-
sourceDispatcherCallable: str = The.dispatcherCallable
|
|
561
|
-
sourceInitializeCallable: str = The.sourceInitializeCallable
|
|
562
|
-
sourceParallelCallable: str = The.sourceParallelCallable
|
|
563
|
-
sourceSequentialCallable: str = The.sourceSequentialCallable
|
|
564
|
-
|
|
565
|
-
sourceDataclassIdentifier: str = The.dataclassIdentifier
|
|
566
|
-
sourceDataclassInstance: str = The.dataclassInstance
|
|
567
|
-
sourceDataclassInstanceTaskDistribution: str = The.dataclassInstanceTaskDistribution
|
|
568
|
-
sourcePathModuleDataclass: str = The.logicalPathModuleDataclass
|
|
569
|
-
|
|
570
|
-
sourceConcurrencyManagerNamespace = The.sourceConcurrencyManagerNamespace
|
|
571
|
-
sourceConcurrencyManagerIdentifier = The.sourceConcurrencyManagerIdentifier
|
|
572
|
-
# ========================================
|
|
573
|
-
# Filesystem
|
|
574
|
-
pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
|
|
575
|
-
fileExtension: str = The.fileExtension
|
|
576
|
-
|
|
577
|
-
# ========================================
|
|
578
|
-
# Logical identifiers
|
|
579
|
-
# meta
|
|
580
|
-
formatStrModuleSynthetic: str = theFormatStrModuleSynthetic
|
|
581
|
-
formatStrModuleForCallableSynthetic: str = theFormatStrModuleForCallableSynthetic
|
|
582
|
-
|
|
583
|
-
# Package
|
|
584
|
-
packageName: ast_Identifier | None = The.packageName
|
|
585
|
-
|
|
586
|
-
# Module
|
|
587
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
588
|
-
Z0Z_flowLogicalPathRoot: str | None = The.moduleOfSyntheticModules
|
|
589
|
-
moduleDispatcher: str = theModuleDispatcherSynthetic
|
|
590
|
-
logicalPathModuleDataclass: str = sourcePathModuleDataclass
|
|
591
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
592
|
-
# `theLogicalPathModuleDispatcherSynthetic` is a problem. It is defined in theSSOT, but it can also be calculated.
|
|
593
|
-
logicalPathModuleDispatcher: str = theLogicalPathModuleDispatcherSynthetic
|
|
594
|
-
|
|
595
|
-
# Function
|
|
596
|
-
dispatcherCallable: str = sourceDispatcherCallable
|
|
597
|
-
initializeCallable: str = sourceInitializeCallable
|
|
598
|
-
parallelCallable: str = sourceParallelCallable
|
|
599
|
-
sequentialCallable: str = sourceSequentialCallable
|
|
600
|
-
|
|
601
|
-
dataclassIdentifier: str = sourceDataclassIdentifier
|
|
602
|
-
|
|
603
|
-
# Variable
|
|
604
|
-
dataclassInstance: str = sourceDataclassInstance
|
|
43
|
+
def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
44
|
+
astModule = Make.Module(ingredients.body, ingredients.type_ignores)
|
|
45
|
+
ast.fix_missing_locations(astModule)
|
|
46
|
+
pythonSource: str = ast.unparse(astModule)
|
|
47
|
+
if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
|
|
48
|
+
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleIdentifiers()
|
|
49
|
+
if packageName:
|
|
50
|
+
autoflake_additional_imports.append(packageName)
|
|
51
|
+
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=False, remove_duplicate_keys = False, remove_unused_variables = False)
|
|
52
|
+
writeStringToHere(pythonSource, pathFilename)
|
|
605
53
|
|
|
606
|
-
|
|
607
|
-
pathRoot: PurePosixPath | None = None,
|
|
608
|
-
logicalPathINFIX: strDotStrCuzPyStoopid | None = None,
|
|
609
|
-
fileExtension: str | None = None,
|
|
610
|
-
) -> PurePosixPath:
|
|
611
|
-
"""filenameStem: (hint: the name of the logical module)"""
|
|
612
|
-
if pathRoot is None:
|
|
613
|
-
pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
|
|
614
|
-
if logicalPathINFIX:
|
|
615
|
-
whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
|
|
616
|
-
pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
|
|
617
|
-
if fileExtension is None:
|
|
618
|
-
fileExtension = self.fileExtension
|
|
619
|
-
filename: str = filenameStem + fileExtension
|
|
620
|
-
return pathRoot.joinpath(filename)
|
|
54
|
+
# END of acceptable classes and functions ======================================================
|
|
621
55
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
56
|
+
def makeDictionaryFunctionDef(module: ast.AST) -> dict[ast_Identifier, ast.FunctionDef]:
|
|
57
|
+
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
58
|
+
NodeTourist(be.FunctionDef, Then.updateThis(dictionaryFunctionDef)).visit(module)
|
|
59
|
+
return dictionaryFunctionDef
|
|
625
60
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
61
|
+
dictionaryEstimates: dict[tuple[int, ...], int] = {
|
|
62
|
+
(2,2,2,2,2,2,2,2): 362794844160000,
|
|
63
|
+
(2,21): 1493028892051200,
|
|
64
|
+
(3,15): 9842024675968800,
|
|
65
|
+
(3,3,3,3): 85109616000000000000000000000000,
|
|
66
|
+
(8,8): 129950723279272000,
|
|
67
|
+
}
|
|
632
68
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
astClassDef = sherpa[0] if sherpa else None
|
|
638
|
-
return astClassDef
|
|
69
|
+
# END of marginal classes and functions ======================================================
|
|
70
|
+
def Z0Z_lameFindReplace(astTree: typeCertified, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]) -> typeCertified:
|
|
71
|
+
keepGoing = True
|
|
72
|
+
newTree = deepcopy(astTree)
|
|
639
73
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
return dictionaryFunctionDef
|
|
74
|
+
while keepGoing:
|
|
75
|
+
for nodeFind, nodeReplace in mappingFindReplaceNodes.items():
|
|
76
|
+
NodeChanger(ifThis.Z0Z_unparseIs(nodeFind), Then.replaceWith(nodeReplace)).visit(newTree)
|
|
644
77
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
648
|
-
dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
|
|
649
|
-
for name, astFunctionDef in dictionaryFunctionDef.items():
|
|
650
|
-
if ifThis.onlyReturnAnyCompare(astFunctionDef):
|
|
651
|
-
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
|
|
652
|
-
elif ifThis.onlyReturnUnaryOp(astFunctionDef):
|
|
653
|
-
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
|
|
78
|
+
if ast.unparse(newTree) == ast.unparse(astTree):
|
|
79
|
+
keepGoing = False
|
|
654
80
|
else:
|
|
655
|
-
|
|
656
|
-
return
|
|
81
|
+
astTree = deepcopy(newTree)
|
|
82
|
+
return newTree
|
|
657
83
|
|
|
84
|
+
# Start of I HATE PROGRAMMING ==========================================================
|
|
85
|
+
# Similar functionality to call does not call itself, but it is used for something else. I hate this function, too.
|
|
658
86
|
def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callable[[ast.AST], bool]) -> bool:
|
|
659
87
|
"""Return True if any descendant of the node (or the node itself) matches the predicateFunction."""
|
|
660
88
|
matchFound = False
|
|
661
|
-
|
|
662
89
|
class DescendantFinder(ast.NodeVisitor):
|
|
663
90
|
def generic_visit(self, node: ast.AST) -> None:
|
|
664
91
|
nonlocal matchFound
|
|
@@ -666,7 +93,6 @@ def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callabl
|
|
|
666
93
|
matchFound = True
|
|
667
94
|
else:
|
|
668
95
|
super().generic_visit(node)
|
|
669
|
-
|
|
670
96
|
DescendantFinder().visit(node)
|
|
671
97
|
return matchFound
|
|
672
98
|
|
|
@@ -677,7 +103,21 @@ def Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.A
|
|
|
677
103
|
actionFunction(node)
|
|
678
104
|
return wrappedAction
|
|
679
105
|
|
|
680
|
-
|
|
106
|
+
# Inlining functions ==========================================================
|
|
107
|
+
def Z0Z_makeDictionaryReplacementStatements(module: ast.AST) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
|
|
108
|
+
"""Return a dictionary of function names and their replacement statements."""
|
|
109
|
+
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
110
|
+
dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
|
|
111
|
+
for name, astFunctionDef in dictionaryFunctionDef.items():
|
|
112
|
+
if ifThis.onlyReturnAnyCompare(astFunctionDef):
|
|
113
|
+
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value
|
|
114
|
+
elif ifThis.onlyReturnUnaryOp(astFunctionDef):
|
|
115
|
+
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value
|
|
116
|
+
else:
|
|
117
|
+
dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
|
|
118
|
+
return dictionaryReplacementStatements
|
|
119
|
+
|
|
120
|
+
def Z0Z_inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
|
|
681
121
|
class FunctionInliner(ast.NodeTransformer):
|
|
682
122
|
def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
|
|
683
123
|
self.dictionaryReplacementStatements = dictionaryReplacementStatements
|
|
@@ -688,17 +128,17 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
|
|
|
688
128
|
|
|
689
129
|
def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
|
|
690
130
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
691
|
-
return self.dictionaryReplacementStatements[node.value.func.id]
|
|
131
|
+
return self.dictionaryReplacementStatements[node.value.func.id]
|
|
692
132
|
return node
|
|
693
133
|
|
|
694
134
|
def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
|
|
695
135
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
696
|
-
return self.dictionaryReplacementStatements[node.value.func.id]
|
|
136
|
+
return self.dictionaryReplacementStatements[node.value.func.id]
|
|
697
137
|
return node
|
|
698
138
|
|
|
699
139
|
def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
|
|
700
140
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
|
|
701
|
-
replacement = self.dictionaryReplacementStatements[node.func.id]
|
|
141
|
+
replacement = self.dictionaryReplacementStatements[node.func.id]
|
|
702
142
|
if not isinstance(replacement, list):
|
|
703
143
|
return replacement
|
|
704
144
|
return node
|
|
@@ -714,58 +154,3 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
|
|
|
714
154
|
astFunctionDef = deepcopy(ImaInlineFunction)
|
|
715
155
|
ast.fix_missing_locations(astFunctionDef)
|
|
716
156
|
return ImaInlineFunction
|
|
717
|
-
|
|
718
|
-
def Z0Z_replaceMatchingASTnodes(astTree: ast.AST, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> ast.AST:
|
|
719
|
-
class TargetedNodeReplacer(ast.NodeTransformer):
|
|
720
|
-
def __init__(self, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> None:
|
|
721
|
-
self.mappingFindReplaceNodes = mappingFindReplaceNodes
|
|
722
|
-
|
|
723
|
-
def visit(self, node: ast.AST) -> ast.AST:
|
|
724
|
-
for nodeFind, nodeReplace in self.mappingFindReplaceNodes.items():
|
|
725
|
-
if self.nodesMatchStructurally(node, nodeFind):
|
|
726
|
-
return nodeReplace
|
|
727
|
-
return self.generic_visit(node)
|
|
728
|
-
|
|
729
|
-
def nodesMatchStructurally(self, nodeSubject: ast.AST | list[Any] | Any, nodePattern: ast.AST | list[Any] | Any) -> bool:
|
|
730
|
-
if nodeSubject is None or nodePattern is None:
|
|
731
|
-
return nodeSubject is None and nodePattern is None
|
|
732
|
-
|
|
733
|
-
if type(nodeSubject) != type(nodePattern):
|
|
734
|
-
return False
|
|
735
|
-
|
|
736
|
-
if isinstance(nodeSubject, ast.AST):
|
|
737
|
-
for field, fieldValueSubject in ast.iter_fields(nodeSubject):
|
|
738
|
-
if field in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'ctx'):
|
|
739
|
-
continue
|
|
740
|
-
attrPattern = getattr(nodePattern, field, None)
|
|
741
|
-
if not self.nodesMatchStructurally(fieldValueSubject, attrPattern):
|
|
742
|
-
return False
|
|
743
|
-
return True
|
|
744
|
-
|
|
745
|
-
if isinstance(nodeSubject, list) and isinstance(nodePattern, list):
|
|
746
|
-
nodeSubjectList: list[Any] = nodeSubject
|
|
747
|
-
nodePatternList: list[Any] = nodePattern
|
|
748
|
-
return len(nodeSubjectList) == len(nodePatternList) and all(
|
|
749
|
-
self.nodesMatchStructurally(elementSubject, elementPattern)
|
|
750
|
-
for elementSubject, elementPattern in zip(nodeSubjectList, nodePatternList)
|
|
751
|
-
)
|
|
752
|
-
|
|
753
|
-
return nodeSubject == nodePattern
|
|
754
|
-
|
|
755
|
-
astTreeCurrent, astTreePrevious = None, astTree
|
|
756
|
-
while astTreeCurrent is None or ast.unparse(astTreeCurrent) != ast.unparse(astTreePrevious):
|
|
757
|
-
astTreePrevious = astTreeCurrent if astTreeCurrent else astTree
|
|
758
|
-
astTreeCurrent = TargetedNodeReplacer(mappingFindReplaceNodes).visit(astTreePrevious)
|
|
759
|
-
|
|
760
|
-
return astTreeCurrent
|
|
761
|
-
|
|
762
|
-
def write_astModule(ingredients: IngredientsModule, pathFilename: str | PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
763
|
-
astModule = ingredients.export()
|
|
764
|
-
ast.fix_missing_locations(astModule)
|
|
765
|
-
pythonSource: str = ast.unparse(astModule)
|
|
766
|
-
if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
|
|
767
|
-
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleNames()
|
|
768
|
-
if packageName:
|
|
769
|
-
autoflake_additional_imports.append(packageName)
|
|
770
|
-
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=False, remove_duplicate_keys = False, remove_unused_variables = False)
|
|
771
|
-
writeStringToHere(pythonSource, pathFilename)
|