mapFolding 0.8.3__py3-none-any.whl → 0.8.5__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 -3
- mapFolding/basecamp.py +13 -7
- mapFolding/beDRY.py +241 -68
- mapFolding/oeis.py +4 -4
- mapFolding/reference/hunterNumba.py +1 -1
- mapFolding/someAssemblyRequired/__init__.py +40 -20
- mapFolding/someAssemblyRequired/_theTypes.py +53 -0
- mapFolding/someAssemblyRequired/_tool_Make.py +99 -0
- mapFolding/someAssemblyRequired/_tool_Then.py +72 -0
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +358 -0
- mapFolding/someAssemblyRequired/_toolboxContainers.py +334 -0
- mapFolding/someAssemblyRequired/_toolboxPython.py +62 -0
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py +2 -2
- mapFolding/someAssemblyRequired/newInliner.py +22 -0
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +158 -0
- mapFolding/someAssemblyRequired/toolboxNumba.py +358 -0
- mapFolding/someAssemblyRequired/transformationTools.py +289 -698
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
- mapFolding/theDao.py +13 -11
- mapFolding/theSSOT.py +83 -128
- mapFolding/toolboxFilesystem.py +219 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.5.dist-info}/METADATA +4 -2
- mapfolding-0.8.5.dist-info/RECORD +48 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.5.dist-info}/WHEEL +1 -1
- tests/conftest.py +56 -52
- tests/test_computations.py +42 -32
- tests/test_filesystem.py +4 -4
- tests/test_other.py +2 -2
- tests/test_tasks.py +2 -2
- mapFolding/filesystem.py +0 -129
- mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -206
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -211
- mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
- mapFolding/someAssemblyRequired/transformDataStructures.py +0 -168
- mapfolding-0.8.3.dist-info/RECORD +0 -43
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.5.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.3.dist-info → mapfolding-0.8.5.dist-info}/top_level.txt +0 -0
|
@@ -23,633 +23,301 @@ 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
|
|
30
|
-
from
|
|
31
|
-
from mapFolding.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
from mapFolding.beDRY import outfitCountFolds
|
|
29
|
+
from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, writeStringToHere
|
|
30
|
+
from mapFolding.someAssemblyRequired import (
|
|
31
|
+
ast_Identifier,
|
|
32
|
+
be,
|
|
33
|
+
DOT,
|
|
34
|
+
ifThis,
|
|
35
|
+
ImaAnnotationType,
|
|
36
|
+
importLogicalPath2Callable,
|
|
37
|
+
IngredientsFunction,
|
|
38
|
+
IngredientsModule,
|
|
39
|
+
LedgerOfImports,
|
|
40
|
+
Make,
|
|
41
|
+
NodeChanger,
|
|
42
|
+
NodeTourist,
|
|
43
|
+
parseLogicalPath2astModule,
|
|
44
|
+
ShatteredDataclass,
|
|
45
|
+
str_nameDOTname,
|
|
46
|
+
Then,
|
|
47
|
+
TypeCertified,
|
|
48
|
+
又,
|
|
39
49
|
)
|
|
50
|
+
from mapFolding.theSSOT import ComputationState, The, raiseIfNoneGitHubIssueNumber3
|
|
40
51
|
from os import PathLike
|
|
41
|
-
from pathlib import Path, PurePath
|
|
42
|
-
from
|
|
43
|
-
from typing import Any, cast, Generic, TypeAlias, TypeGuard, TypeVar
|
|
44
|
-
from Z0Z_tools import updateExtendPolishDictionaryLists
|
|
52
|
+
from pathlib import Path, PurePath
|
|
53
|
+
from typing import Any, Literal, overload
|
|
45
54
|
import ast
|
|
46
55
|
import dataclasses
|
|
56
|
+
import pickle
|
|
57
|
+
|
|
58
|
+
def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
|
|
59
|
+
astFunctionDef = extractFunctionDef(astModule, identifierFunctionDef)
|
|
60
|
+
if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
|
|
61
|
+
return IngredientsFunction(astFunctionDef, LedgerOfImports(astModule))
|
|
62
|
+
|
|
63
|
+
def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
|
|
64
|
+
return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
|
|
65
|
+
|
|
66
|
+
def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
|
|
67
|
+
return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
|
|
68
|
+
|
|
69
|
+
def makeDictionaryFunctionDef(module: ast.AST) -> dict[ast_Identifier, ast.FunctionDef]:
|
|
70
|
+
dictionaryIdentifier2FunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
71
|
+
NodeTourist(be.FunctionDef, Then.updateKeyValueIn(DOT.name, Then.getIt, dictionaryIdentifier2FunctionDef)).visit(module)
|
|
72
|
+
return dictionaryIdentifier2FunctionDef
|
|
73
|
+
|
|
74
|
+
def makeDictionary4InliningFunction(identifierToInline: ast_Identifier, dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef], FunctionDefToInline: ast.FunctionDef | None = None) -> dict[str, ast.FunctionDef]:
|
|
75
|
+
"""
|
|
76
|
+
Creates a dictionary of function definitions required for inlining a target function.
|
|
77
|
+
This function analyzes a target function and recursively collects all function definitions
|
|
78
|
+
that are called within it (and any functions called by those functions), preparing them for inlining.
|
|
79
|
+
Parameters:
|
|
80
|
+
----------
|
|
81
|
+
identifierToInline : ast_Identifier
|
|
82
|
+
The identifier of the function to be inlined.
|
|
83
|
+
dictionaryFunctionDef : dict[ast_Identifier, ast.FunctionDef]
|
|
84
|
+
A dictionary mapping function identifiers to their AST function definitions.
|
|
85
|
+
FunctionDefToInline : ast.FunctionDef | None, optional
|
|
86
|
+
The AST function definition to inline. If None, it will be retrieved from dictionaryFunctionDef using identifierToInline.
|
|
87
|
+
Returns:
|
|
88
|
+
-------
|
|
89
|
+
dict[str, ast.FunctionDef]
|
|
90
|
+
A dictionary mapping function names to their AST function definitions, containing all functions needed for inlining.
|
|
91
|
+
Raises:
|
|
92
|
+
------
|
|
93
|
+
ValueError
|
|
94
|
+
If the function to inline is not found in the dictionary, or if recursion is detected during analysis.
|
|
95
|
+
Notes:
|
|
96
|
+
-----
|
|
97
|
+
The function performs a recursive analysis to find all dependent functions needed for inlining.
|
|
98
|
+
It detects and prevents recursive function calls that could cause infinite inlining.
|
|
99
|
+
"""
|
|
100
|
+
if FunctionDefToInline is None:
|
|
101
|
+
try:
|
|
102
|
+
FunctionDefToInline = dictionaryFunctionDef[identifierToInline]
|
|
103
|
+
except KeyError as ERRORmessage:
|
|
104
|
+
raise ValueError(f"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }") from ERRORmessage
|
|
105
|
+
|
|
106
|
+
listIdentifiersCalledFunctions: list[ast_Identifier] = []
|
|
107
|
+
findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) # pyright: ignore[reportArgumentType]
|
|
108
|
+
findIdentifiersToInline.visit(FunctionDefToInline)
|
|
109
|
+
|
|
110
|
+
dictionary4Inlining: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
111
|
+
for identifier in sorted(set(listIdentifiersCalledFunctions).intersection(dictionaryFunctionDef.keys())):
|
|
112
|
+
dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
|
|
47
113
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
def workhorse(node: ast.AST) -> TypeGuard[ast.Call] | bool: return ifThis.isCall(node) and ifThis.isName_Identifier(identifier)(node.func)
|
|
193
|
-
return workhorse
|
|
194
|
-
@staticmethod
|
|
195
|
-
def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
196
|
-
return lambda node: ifThis.isCall(node) and ifThis.is_nameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
|
|
197
|
-
@staticmethod
|
|
198
|
-
def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
199
|
-
return ifThis.isCall(node) and ifThis.isName(node.func)
|
|
200
|
-
@staticmethod
|
|
201
|
-
def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
|
|
202
|
-
return isinstance(node, ast.ClassDef)
|
|
203
|
-
@staticmethod
|
|
204
|
-
def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
|
|
205
|
-
return lambda node: ifThis.isClassDef(node) and node.name == identifier
|
|
206
|
-
@staticmethod
|
|
207
|
-
def isCompare(node: ast.AST) -> TypeGuard[ast.Compare]:
|
|
208
|
-
return isinstance(node, ast.Compare)
|
|
209
|
-
@staticmethod
|
|
210
|
-
def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
|
|
211
|
-
return isinstance(node, ast.Constant)
|
|
212
|
-
@staticmethod
|
|
213
|
-
def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
|
|
214
|
-
return lambda node: ifThis.isConstant(node) and node.value == value
|
|
215
|
-
@staticmethod
|
|
216
|
-
def isExpr(node: ast.AST) -> TypeGuard[ast.Expr]:
|
|
217
|
-
return isinstance(node, ast.Expr)
|
|
218
|
-
@staticmethod
|
|
219
|
-
def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]: 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
|
-
workhorse: function that checks if a node matches the criteria
|
|
270
|
-
"""
|
|
271
|
-
def workhorse(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 workhorse
|
|
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 workhorse(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 workhorse
|
|
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)
|
|
459
|
-
|
|
460
|
-
def walkThis(self, walkThis: ast.AST) -> None:
|
|
461
|
-
for smurf in ast.walk(walkThis):
|
|
462
|
-
if isinstance(smurf, (ast.Import, ast.ImportFrom)):
|
|
463
|
-
self.addAst(smurf)
|
|
464
|
-
|
|
465
|
-
class Then:
|
|
466
|
-
@staticmethod
|
|
467
|
-
def append_targetTo(listName: list[ast.AST]) -> Callable[[ast.AnnAssign], None]:
|
|
468
|
-
return lambda node: listName.append(node.target)
|
|
469
|
-
@staticmethod
|
|
470
|
-
def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST], None]:
|
|
471
|
-
return lambda node: listOfAny.append(node)
|
|
472
|
-
@staticmethod
|
|
473
|
-
def insertThisAbove(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
|
|
474
|
-
return lambda aboveMe: [*list_astAST, aboveMe]
|
|
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: return None
|
|
480
|
-
@staticmethod
|
|
481
|
-
def replaceWith(astAST: ast.AST) -> Callable[[ast.AST], ast.AST]: return lambda _replaceMe: astAST
|
|
482
|
-
@staticmethod
|
|
483
|
-
def replaceDOTfuncWith(ast_expr: ast.expr) -> Callable[[ast.Call], ast.Call]:
|
|
484
|
-
def workhorse(node: ast.Call) -> ast.Call:
|
|
485
|
-
node.func = ast_expr
|
|
486
|
-
return node
|
|
487
|
-
return workhorse
|
|
488
|
-
@staticmethod
|
|
489
|
-
def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
|
|
490
|
-
return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
|
|
491
|
-
@staticmethod
|
|
492
|
-
def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AnnAssign], None]:
|
|
493
|
-
return lambda node: ledger.addImportFromStr(logicalPath, node.annotation.id) # type: ignore
|
|
494
|
-
@staticmethod
|
|
495
|
-
def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AnnAssign], None]:
|
|
496
|
-
return lambda node: list_keyword.append(Make.ast_keyword(node.target.id, node.target)) # type: ignore
|
|
497
|
-
@staticmethod
|
|
498
|
-
def Z0Z_appendAnnAssignOf_nameDOTnameTo(identifier: ast_Identifier, list_nameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AnnAssign], None]:
|
|
499
|
-
return lambda node: list_nameDOTname.append(Make.astAnnAssign(node.target, node.annotation, Make.nameDOTname(identifier, node.target.id))) # type: ignore
|
|
114
|
+
keepGoing = True
|
|
115
|
+
while keepGoing:
|
|
116
|
+
keepGoing = False
|
|
117
|
+
listIdentifiersCalledFunctions.clear()
|
|
118
|
+
findIdentifiersToInline.visit(Make.Module(list(dictionary4Inlining.values())))
|
|
119
|
+
|
|
120
|
+
# NOTE: This is simple not comprehensive recursion protection. # TODO think about why I dislike `ifThis.CallDoesNotCallItself`
|
|
121
|
+
if identifierToInline in listIdentifiersCalledFunctions: raise ValueError(f"Recursion found: {identifierToInline = }.")
|
|
122
|
+
|
|
123
|
+
listIdentifiersCalledFunctions = sorted((set(listIdentifiersCalledFunctions).difference(dictionary4Inlining.keys())).intersection(dictionaryFunctionDef.keys()))
|
|
124
|
+
if len(listIdentifiersCalledFunctions) > 0:
|
|
125
|
+
keepGoing = True
|
|
126
|
+
for identifier in listIdentifiersCalledFunctions:
|
|
127
|
+
if identifier in dictionaryFunctionDef:
|
|
128
|
+
dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
|
|
129
|
+
|
|
130
|
+
return dictionary4Inlining
|
|
131
|
+
|
|
132
|
+
@overload
|
|
133
|
+
def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[True], *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> Path: ...
|
|
134
|
+
@overload
|
|
135
|
+
def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[False] = False, **keywordArguments: Any) -> ComputationState: ...
|
|
136
|
+
def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: bool = False, *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> ComputationState | Path:
|
|
137
|
+
"""
|
|
138
|
+
Initializes a computation state and optionally saves it to disk.
|
|
139
|
+
|
|
140
|
+
This function initializes a computation state using the source algorithm.
|
|
141
|
+
|
|
142
|
+
Hint: If you want an uninitialized state, call `outfitCountFolds` directly.
|
|
143
|
+
|
|
144
|
+
Parameters:
|
|
145
|
+
mapShape: List of integers representing the dimensions of the map to be folded.
|
|
146
|
+
writeJob (False): Whether to save the state to disk.
|
|
147
|
+
pathFilename (getPathFilenameFoldsTotal.pkl): The path and filename to save the state. If None, uses a default path.
|
|
148
|
+
**keywordArguments: computationDivisions:int|str|None=None,concurrencyLimit:int=1.
|
|
149
|
+
Returns:
|
|
150
|
+
stateUniversal|pathFilenameJob: The computation state for the map folding calculations, or
|
|
151
|
+
the path to the saved state file if writeJob is True.
|
|
152
|
+
"""
|
|
153
|
+
stateUniversal: ComputationState = outfitCountFolds(mapShape, **keywordArguments)
|
|
154
|
+
|
|
155
|
+
initializeState = importLogicalPath2Callable(The.logicalPathModuleSourceAlgorithm, The.sourceCallableInitialize)
|
|
156
|
+
stateUniversal = initializeState(stateUniversal)
|
|
157
|
+
|
|
158
|
+
if not writeJob:
|
|
159
|
+
return stateUniversal
|
|
160
|
+
|
|
161
|
+
if pathFilename:
|
|
162
|
+
pathFilenameJob = Path(pathFilename)
|
|
163
|
+
pathFilenameJob.parent.mkdir(parents=True, exist_ok=True)
|
|
164
|
+
else:
|
|
165
|
+
pathFilenameJob = getPathFilenameFoldsTotal(stateUniversal.mapShape).with_suffix('.pkl')
|
|
166
|
+
|
|
167
|
+
pathFilenameJob.write_bytes(pickle.dumps(stateUniversal))
|
|
168
|
+
return pathFilenameJob
|
|
500
169
|
|
|
501
170
|
@dataclasses.dataclass
|
|
502
|
-
class
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
171
|
+
class DeReConstructField2ast:
|
|
172
|
+
dataclassesDOTdataclassLogicalPathModule: dataclasses.InitVar[str_nameDOTname]
|
|
173
|
+
dataclassClassDef: dataclasses.InitVar[ast.ClassDef]
|
|
174
|
+
dataclassesDOTdataclassInstance_Identifier: dataclasses.InitVar[ast_Identifier]
|
|
175
|
+
field: dataclasses.InitVar[dataclasses.Field[Any]]
|
|
176
|
+
|
|
177
|
+
ledger: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
|
|
178
|
+
|
|
179
|
+
name: ast_Identifier = dataclasses.field(init=False)
|
|
180
|
+
typeBuffalo: type[Any] | str | Any = dataclasses.field(init=False)
|
|
181
|
+
default: Any | None = dataclasses.field(init=False)
|
|
182
|
+
default_factory: Callable[..., Any] | None = dataclasses.field(init=False)
|
|
183
|
+
repr: bool = dataclasses.field(init=False)
|
|
184
|
+
hash: bool | None = dataclasses.field(init=False)
|
|
185
|
+
init: bool = dataclasses.field(init=False)
|
|
186
|
+
compare: bool = dataclasses.field(init=False)
|
|
187
|
+
metadata: dict[Any, Any] = dataclasses.field(init=False)
|
|
188
|
+
kw_only: bool = dataclasses.field(init=False)
|
|
189
|
+
|
|
190
|
+
astName: ast.Name = dataclasses.field(init=False)
|
|
191
|
+
ast_keyword_field__field: ast.keyword = dataclasses.field(init=False)
|
|
192
|
+
ast_nameDOTname: ast.Attribute = dataclasses.field(init=False)
|
|
193
|
+
astAnnotation: ImaAnnotationType = dataclasses.field(init=False)
|
|
194
|
+
ast_argAnnotated: ast.arg = dataclasses.field(init=False)
|
|
195
|
+
astAnnAssignConstructor: ast.AnnAssign = dataclasses.field(init=False)
|
|
196
|
+
Z0Z_hack: tuple[ast.AnnAssign, str] = dataclasses.field(init=False)
|
|
197
|
+
|
|
198
|
+
def __post_init__(self, dataclassesDOTdataclassLogicalPathModule: str_nameDOTname, dataclassClassDef: ast.ClassDef, dataclassesDOTdataclassInstance_Identifier: ast_Identifier, field: dataclasses.Field[Any]) -> None:
|
|
199
|
+
self.compare = field.compare
|
|
200
|
+
self.default = field.default if field.default is not dataclasses.MISSING else None
|
|
201
|
+
self.default_factory = field.default_factory if field.default_factory is not dataclasses.MISSING else None
|
|
202
|
+
self.hash = field.hash
|
|
203
|
+
self.init = field.init
|
|
204
|
+
self.kw_only = field.kw_only if field.kw_only is not dataclasses.MISSING else False
|
|
205
|
+
self.metadata = dict(field.metadata)
|
|
206
|
+
self.name = field.name
|
|
207
|
+
self.repr = field.repr
|
|
208
|
+
self.typeBuffalo = field.type
|
|
209
|
+
|
|
210
|
+
self.astName = Make.Name(self.name)
|
|
211
|
+
self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
|
|
212
|
+
self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstance_Identifier), self.name)
|
|
213
|
+
|
|
214
|
+
sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), 又.annotation(Then.getIt)).captureLastMatch(dataclassClassDef)
|
|
215
|
+
if sherpa is None: raise raiseIfNoneGitHubIssueNumber3
|
|
216
|
+
else: self.astAnnotation = sherpa
|
|
217
|
+
|
|
218
|
+
self.ast_argAnnotated = Make.arg(self.name, self.astAnnotation)
|
|
219
|
+
|
|
220
|
+
dtype = self.metadata.get('dtype', None)
|
|
221
|
+
if dtype:
|
|
222
|
+
constructor = 'array'
|
|
223
|
+
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', Make.Name(dtype.__name__))]))
|
|
224
|
+
self.ledger.addImportFrom_asStr('numpy', constructor)
|
|
225
|
+
self.ledger.addImportFrom_asStr('numpy', dtype.__name__)
|
|
226
|
+
self.Z0Z_hack = (self.astAnnAssignConstructor, 'array')
|
|
227
|
+
elif be.Name(self.astAnnotation):
|
|
228
|
+
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(self.astAnnotation, [Make.Constant(-1)]))
|
|
229
|
+
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id)
|
|
230
|
+
self.Z0Z_hack = (self.astAnnAssignConstructor, 'scalar')
|
|
231
|
+
elif be.Subscript(self.astAnnotation):
|
|
232
|
+
elementConstructor: ast_Identifier = self.metadata['elementConstructor']
|
|
233
|
+
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
|
|
234
|
+
takeTheTuple: ast.Tuple = deepcopy(self.astAnnotation.slice) # type: ignore
|
|
235
|
+
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, takeTheTuple)
|
|
236
|
+
self.Z0Z_hack = (self.astAnnAssignConstructor, elementConstructor)
|
|
237
|
+
if be.Name(self.astAnnotation):
|
|
238
|
+
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id) # pyright: ignore [reportUnknownArgumentType, reportUnknownMemberType, reportIJustCalledATypeGuardMethod_WTF]
|
|
239
|
+
|
|
240
|
+
def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclass_Identifier: ast_Identifier, instance_Identifier: ast_Identifier) -> ShatteredDataclass:
|
|
241
|
+
"""
|
|
242
|
+
Parameters:
|
|
243
|
+
logicalPathModule: gimme string cuz python is stoopid
|
|
244
|
+
dataclass_Identifier: The identifier of the dataclass to be dismantled.
|
|
245
|
+
instance_Identifier: In the synthesized module/function/scope, the identifier that will be used for the instance.
|
|
246
|
+
"""
|
|
247
|
+
Official_fieldOrder: list[ast_Identifier] = []
|
|
248
|
+
dictionaryDeReConstruction: dict[ast_Identifier, DeReConstructField2ast] = {}
|
|
249
|
+
|
|
250
|
+
dataclassClassDef = extractClassDef(parseLogicalPath2astModule(logicalPathModule), dataclass_Identifier)
|
|
251
|
+
if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find {dataclass_Identifier=} in {logicalPathModule=}.")
|
|
252
|
+
|
|
253
|
+
countingVariable = None
|
|
254
|
+
for aField in dataclasses.fields(importLogicalPath2Callable(logicalPathModule, dataclass_Identifier)): # pyright: ignore [reportArgumentType]
|
|
255
|
+
Official_fieldOrder.append(aField.name)
|
|
256
|
+
dictionaryDeReConstruction[aField.name] = DeReConstructField2ast(logicalPathModule, dataclassClassDef, instance_Identifier, aField)
|
|
257
|
+
if aField.metadata.get('theCountingIdentifier', False):
|
|
258
|
+
countingVariable = dictionaryDeReConstruction[aField.name].name
|
|
259
|
+
|
|
260
|
+
if countingVariable is None:
|
|
261
|
+
raise ValueError(f"I could not find the counting variable in {dataclass_Identifier=} in {logicalPathModule=}.")
|
|
262
|
+
|
|
263
|
+
shatteredDataclass = ShatteredDataclass(
|
|
264
|
+
countingVariableAnnotation=dictionaryDeReConstruction[countingVariable].astAnnotation,
|
|
265
|
+
countingVariableName=dictionaryDeReConstruction[countingVariable].astName,
|
|
266
|
+
field2AnnAssign={dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].astAnnAssignConstructor for field in Official_fieldOrder},
|
|
267
|
+
Z0Z_field2AnnAssign={dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].Z0Z_hack for field in Official_fieldOrder},
|
|
268
|
+
list_argAnnotated4ArgumentsSpecification=[dictionaryDeReConstruction[field].ast_argAnnotated for field in Official_fieldOrder],
|
|
269
|
+
list_keyword_field__field4init=[dictionaryDeReConstruction[field].ast_keyword_field__field for field in Official_fieldOrder if dictionaryDeReConstruction[field].init],
|
|
270
|
+
listAnnotations=[dictionaryDeReConstruction[field].astAnnotation for field in Official_fieldOrder],
|
|
271
|
+
listName4Parameters=[dictionaryDeReConstruction[field].astName for field in Official_fieldOrder],
|
|
272
|
+
listUnpack=[Make.AnnAssign(dictionaryDeReConstruction[field].astName, dictionaryDeReConstruction[field].astAnnotation, dictionaryDeReConstruction[field].ast_nameDOTname) for field in Official_fieldOrder],
|
|
273
|
+
map_stateDOTfield2Name={dictionaryDeReConstruction[field].ast_nameDOTname: dictionaryDeReConstruction[field].astName for field in Official_fieldOrder},
|
|
274
|
+
)
|
|
275
|
+
shatteredDataclass.fragments4AssignmentOrParameters = Make.Tuple(shatteredDataclass.listName4Parameters, ast.Store())
|
|
276
|
+
shatteredDataclass.repack = Make.Assign(listTargets=[Make.Name(instance_Identifier)], value=Make.Call(Make.Name(dataclass_Identifier), list_astKeywords=shatteredDataclass.list_keyword_field__field4init))
|
|
277
|
+
shatteredDataclass.signatureReturnAnnotation = Make.Subscript(Make.Name('tuple'), Make.Tuple(shatteredDataclass.listAnnotations))
|
|
278
|
+
|
|
279
|
+
shatteredDataclass.ledger.update(*(dictionaryDeReConstruction[field].ledger for field in Official_fieldOrder))
|
|
280
|
+
shatteredDataclass.ledger.addImportFrom_asStr(logicalPathModule, dataclass_Identifier)
|
|
281
|
+
|
|
282
|
+
return shatteredDataclass
|
|
283
|
+
|
|
284
|
+
def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
285
|
+
astModule = Make.Module(ingredients.body, ingredients.type_ignores)
|
|
286
|
+
ast.fix_missing_locations(astModule)
|
|
287
|
+
pythonSource: str = ast.unparse(astModule)
|
|
288
|
+
if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
|
|
289
|
+
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleIdentifiers()
|
|
290
|
+
if packageName:
|
|
291
|
+
autoflake_additional_imports.append(packageName)
|
|
292
|
+
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)
|
|
293
|
+
writeStringToHere(pythonSource, pathFilename)
|
|
506
294
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
launcher: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
521
|
-
|
|
522
|
-
# parameter for `ast.Module` constructor
|
|
523
|
-
type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
|
|
524
|
-
|
|
525
|
-
def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
|
|
526
|
-
if ingredientsFunction is not None:
|
|
527
|
-
if isinstance(ingredientsFunction, IngredientsFunction):
|
|
528
|
-
self.addIngredientsFunction(ingredientsFunction)
|
|
529
|
-
else:
|
|
530
|
-
self.addIngredientsFunction(*ingredientsFunction)
|
|
531
|
-
|
|
532
|
-
def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
|
|
533
|
-
"""Add one or more `IngredientsFunction`."""
|
|
534
|
-
listLedgers: list[LedgerOfImports] = []
|
|
535
|
-
for definition in ingredientsFunction:
|
|
536
|
-
self.functions.append(definition.astFunctionDef)
|
|
537
|
-
listLedgers.append(definition.imports)
|
|
538
|
-
self.imports.update(*listLedgers)
|
|
539
|
-
|
|
540
|
-
def _makeModuleBody(self) -> list[ast.stmt]:
|
|
541
|
-
body: list[ast.stmt] = []
|
|
542
|
-
body.extend(self.imports.makeListAst())
|
|
543
|
-
body.extend(self.prologue)
|
|
544
|
-
body.extend(self.functions)
|
|
545
|
-
body.extend(self.epilogue)
|
|
546
|
-
body.extend(self.launcher)
|
|
547
|
-
# TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
|
|
548
|
-
return body
|
|
549
|
-
|
|
550
|
-
def export(self) -> ast.Module:
|
|
551
|
-
"""Create a new `ast.Module` from the ingredients."""
|
|
552
|
-
return Make.astModule(self._makeModuleBody(), self.type_ignores)
|
|
295
|
+
# END of acceptable classes and functions ======================================================
|
|
296
|
+
dictionaryEstimates: dict[tuple[int, ...], int] = {
|
|
297
|
+
(2,2,2,2,2,2,2,2): 362794844160000,
|
|
298
|
+
(2,21): 1493028892051200,
|
|
299
|
+
(3,15): 9842024675968800,
|
|
300
|
+
(3,3,3,3): 85109616000000000000000000000000,
|
|
301
|
+
(8,8): 129950723279272000,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# END of marginal classes and functions ======================================================
|
|
305
|
+
def Z0Z_lameFindReplace(astTree, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]):
|
|
306
|
+
keepGoing = True
|
|
307
|
+
newTree = deepcopy(astTree)
|
|
553
308
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
sourceParallelCallable: str = The.sourceParallelCallable
|
|
567
|
-
sourceSequentialCallable: str = The.sourceSequentialCallable
|
|
568
|
-
|
|
569
|
-
sourceDataclassIdentifier: str = The.dataclassIdentifier
|
|
570
|
-
sourceDataclassInstance: str = The.dataclassInstance
|
|
571
|
-
sourceDataclassInstanceTaskDistribution: str = The.dataclassInstanceTaskDistribution
|
|
572
|
-
sourcePathModuleDataclass: str = The.logicalPathModuleDataclass
|
|
573
|
-
|
|
574
|
-
sourceConcurrencyManagerNamespace = The.sourceConcurrencyManagerNamespace
|
|
575
|
-
sourceConcurrencyManagerIdentifier = The.sourceConcurrencyManagerIdentifier
|
|
576
|
-
# ========================================
|
|
577
|
-
# Filesystem
|
|
578
|
-
pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
|
|
579
|
-
fileExtension: str = The.fileExtension
|
|
580
|
-
|
|
581
|
-
# ========================================
|
|
582
|
-
# Logical identifiers
|
|
583
|
-
# meta
|
|
584
|
-
formatStrModuleSynthetic: str = theFormatStrModuleSynthetic
|
|
585
|
-
formatStrModuleForCallableSynthetic: str = theFormatStrModuleForCallableSynthetic
|
|
586
|
-
|
|
587
|
-
# Package
|
|
588
|
-
packageName: ast_Identifier | None = The.packageName
|
|
589
|
-
|
|
590
|
-
# Module
|
|
591
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
592
|
-
Z0Z_flowLogicalPathRoot: str | None = The.moduleOfSyntheticModules
|
|
593
|
-
moduleDispatcher: str = theModuleDispatcherSynthetic
|
|
594
|
-
logicalPathModuleDataclass: str = sourcePathModuleDataclass
|
|
595
|
-
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
596
|
-
# `theLogicalPathModuleDispatcherSynthetic` is a problem. It is defined in theSSOT, but it can also be calculated.
|
|
597
|
-
logicalPathModuleDispatcher: str = theLogicalPathModuleDispatcherSynthetic
|
|
598
|
-
|
|
599
|
-
# Function
|
|
600
|
-
dispatcherCallable: str = sourceDispatcherCallable
|
|
601
|
-
initializeCallable: str = sourceInitializeCallable
|
|
602
|
-
parallelCallable: str = sourceParallelCallable
|
|
603
|
-
sequentialCallable: str = sourceSequentialCallable
|
|
604
|
-
# initializeCallable: str = 'StartTheCommotion'
|
|
605
|
-
# parallelCallable: str = sourceParallelCallable
|
|
606
|
-
# sequentialCallable: str = sourceSequentialCallable
|
|
607
|
-
|
|
608
|
-
dataclassIdentifier: str = sourceDataclassIdentifier
|
|
609
|
-
|
|
610
|
-
# Variable
|
|
611
|
-
dataclassInstance: str = sourceDataclassInstance
|
|
612
|
-
|
|
613
|
-
def _makePathFilename(self, filenameStem: str,
|
|
614
|
-
pathRoot: PurePosixPath | None = None,
|
|
615
|
-
logicalPathINFIX: strDotStrCuzPyStoopid | None = None,
|
|
616
|
-
fileExtension: str | None = None,
|
|
617
|
-
) -> PurePosixPath:
|
|
618
|
-
"""filenameStem: (hint: the name of the logical module)"""
|
|
619
|
-
if pathRoot is None:
|
|
620
|
-
pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
|
|
621
|
-
if logicalPathINFIX:
|
|
622
|
-
whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
|
|
623
|
-
pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
|
|
624
|
-
if fileExtension is None:
|
|
625
|
-
fileExtension = self.fileExtension
|
|
626
|
-
filename: str = filenameStem + fileExtension
|
|
627
|
-
return pathRoot.joinpath(filename)
|
|
628
|
-
|
|
629
|
-
@property
|
|
630
|
-
def pathFilenameDispatcher(self) -> PurePosixPath:
|
|
631
|
-
return self._makePathFilename(filenameStem=self.moduleDispatcher, logicalPathINFIX=self.Z0Z_flowLogicalPathRoot)
|
|
632
|
-
|
|
633
|
-
def extractClassDef(module: ast.Module, identifier: ast_Identifier) -> ast.ClassDef | None:
|
|
634
|
-
sherpa: list[ast.ClassDef] = []
|
|
635
|
-
extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
|
|
636
|
-
extractor.visit(module)
|
|
637
|
-
astClassDef = sherpa[0] if sherpa else None
|
|
638
|
-
return astClassDef
|
|
639
|
-
|
|
640
|
-
def extractFunctionDef(module: ast.Module, identifier: ast_Identifier) -> ast.FunctionDef | None:
|
|
641
|
-
sherpa: list[ast.FunctionDef] = []
|
|
642
|
-
extractor = NodeCollector(ifThis.isFunctionDef_Identifier(identifier), [Then.appendTo(sherpa)])
|
|
643
|
-
extractor.visit(module)
|
|
644
|
-
astClassDef = sherpa[0] if sherpa else None
|
|
645
|
-
return astClassDef
|
|
646
|
-
|
|
647
|
-
def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.FunctionDef]:
|
|
648
|
-
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
649
|
-
NodeCollector(ifThis.isFunctionDef, [Then.updateThis(dictionaryFunctionDef)]).visit(module)
|
|
650
|
-
return dictionaryFunctionDef
|
|
651
|
-
|
|
652
|
-
def makeDictionaryReplacementStatements(module: ast.Module) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
|
|
309
|
+
while keepGoing:
|
|
310
|
+
for nodeFind, nodeReplace in mappingFindReplaceNodes.items():
|
|
311
|
+
NodeChanger(ifThis.Z0Z_unparseIs(nodeFind), Then.replaceWith(nodeReplace)).visit(newTree)
|
|
312
|
+
|
|
313
|
+
if ast.unparse(newTree) == ast.unparse(astTree):
|
|
314
|
+
keepGoing = False
|
|
315
|
+
else:
|
|
316
|
+
astTree = deepcopy(newTree)
|
|
317
|
+
return newTree
|
|
318
|
+
|
|
319
|
+
# Start of I HATE PROGRAMMING ==========================================================
|
|
320
|
+
def Z0Z_makeDictionaryReplacementStatements(module: ast.AST) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
|
|
653
321
|
"""Return a dictionary of function names and their replacement statements."""
|
|
654
322
|
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
655
323
|
dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
|
|
@@ -662,29 +330,7 @@ def makeDictionaryReplacementStatements(module: ast.Module) -> dict[ast_Identifi
|
|
|
662
330
|
dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
|
|
663
331
|
return dictionaryReplacementStatements
|
|
664
332
|
|
|
665
|
-
def
|
|
666
|
-
"""Return True if any descendant of the node (or the node itself) matches the predicateFunction."""
|
|
667
|
-
matchFound = False
|
|
668
|
-
|
|
669
|
-
class DescendantFinder(ast.NodeVisitor):
|
|
670
|
-
def generic_visit(self, node: ast.AST) -> None:
|
|
671
|
-
nonlocal matchFound
|
|
672
|
-
if predicateFunction(node):
|
|
673
|
-
matchFound = True
|
|
674
|
-
else:
|
|
675
|
-
super().generic_visit(node)
|
|
676
|
-
|
|
677
|
-
DescendantFinder().visit(node)
|
|
678
|
-
return matchFound
|
|
679
|
-
|
|
680
|
-
def Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.AST], bool], actionFunction: Callable[[ast.AST], None]) -> Callable[[ast.AST], None]:
|
|
681
|
-
"""Return a new action that will execute actionFunction only if no descendant (or the node itself) matches exclusionPredicate."""
|
|
682
|
-
def wrappedAction(node: ast.AST) -> None:
|
|
683
|
-
if not Z0Z_descendantContainsMatchingNode(node, exclusionPredicate):
|
|
684
|
-
actionFunction(node)
|
|
685
|
-
return wrappedAction
|
|
686
|
-
|
|
687
|
-
def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
|
|
333
|
+
def Z0Z_inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
|
|
688
334
|
class FunctionInliner(ast.NodeTransformer):
|
|
689
335
|
def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
|
|
690
336
|
self.dictionaryReplacementStatements = dictionaryReplacementStatements
|
|
@@ -695,17 +341,17 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
|
|
|
695
341
|
|
|
696
342
|
def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
|
|
697
343
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
698
|
-
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
344
|
+
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
699
345
|
return node
|
|
700
346
|
|
|
701
347
|
def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
|
|
702
348
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
703
|
-
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
349
|
+
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
704
350
|
return node
|
|
705
351
|
|
|
706
352
|
def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
|
|
707
353
|
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
|
|
708
|
-
replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore
|
|
354
|
+
replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore
|
|
709
355
|
if not isinstance(replacement, list):
|
|
710
356
|
return replacement
|
|
711
357
|
return node
|
|
@@ -721,58 +367,3 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
|
|
|
721
367
|
astFunctionDef = deepcopy(ImaInlineFunction)
|
|
722
368
|
ast.fix_missing_locations(astFunctionDef)
|
|
723
369
|
return ImaInlineFunction
|
|
724
|
-
|
|
725
|
-
def Z0Z_replaceMatchingASTnodes(astTree: ast.AST, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> ast.AST:
|
|
726
|
-
class TargetedNodeReplacer(ast.NodeTransformer):
|
|
727
|
-
def __init__(self, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> None:
|
|
728
|
-
self.mappingFindReplaceNodes = mappingFindReplaceNodes
|
|
729
|
-
|
|
730
|
-
def visit(self, node: ast.AST) -> ast.AST:
|
|
731
|
-
for nodeFind, nodeReplace in self.mappingFindReplaceNodes.items():
|
|
732
|
-
if self.nodesMatchStructurally(node, nodeFind):
|
|
733
|
-
return nodeReplace
|
|
734
|
-
return self.generic_visit(node)
|
|
735
|
-
|
|
736
|
-
def nodesMatchStructurally(self, nodeSubject: ast.AST | list[Any] | Any, nodePattern: ast.AST | list[Any] | Any) -> bool:
|
|
737
|
-
if nodeSubject is None or nodePattern is None:
|
|
738
|
-
return nodeSubject is None and nodePattern is None
|
|
739
|
-
|
|
740
|
-
if type(nodeSubject) != type(nodePattern):
|
|
741
|
-
return False
|
|
742
|
-
|
|
743
|
-
if isinstance(nodeSubject, ast.AST):
|
|
744
|
-
for field, fieldValueSubject in ast.iter_fields(nodeSubject):
|
|
745
|
-
if field in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'ctx'):
|
|
746
|
-
continue
|
|
747
|
-
attrPattern = getattr(nodePattern, field, None)
|
|
748
|
-
if not self.nodesMatchStructurally(fieldValueSubject, attrPattern):
|
|
749
|
-
return False
|
|
750
|
-
return True
|
|
751
|
-
|
|
752
|
-
if isinstance(nodeSubject, list) and isinstance(nodePattern, list):
|
|
753
|
-
nodeSubjectList: list[Any] = nodeSubject
|
|
754
|
-
nodePatternList: list[Any] = nodePattern
|
|
755
|
-
return len(nodeSubjectList) == len(nodePatternList) and all(
|
|
756
|
-
self.nodesMatchStructurally(elementSubject, elementPattern)
|
|
757
|
-
for elementSubject, elementPattern in zip(nodeSubjectList, nodePatternList)
|
|
758
|
-
)
|
|
759
|
-
|
|
760
|
-
return nodeSubject == nodePattern
|
|
761
|
-
|
|
762
|
-
astTreeCurrent, astTreePrevious = None, astTree
|
|
763
|
-
while astTreeCurrent is None or ast.unparse(astTreeCurrent) != ast.unparse(astTreePrevious):
|
|
764
|
-
astTreePrevious = astTreeCurrent if astTreeCurrent else astTree
|
|
765
|
-
astTreeCurrent = TargetedNodeReplacer(mappingFindReplaceNodes).visit(astTreePrevious)
|
|
766
|
-
|
|
767
|
-
return astTreeCurrent
|
|
768
|
-
|
|
769
|
-
def write_astModule(ingredients: IngredientsModule, pathFilename: str | PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
770
|
-
astModule = ingredients.export()
|
|
771
|
-
ast.fix_missing_locations(astModule)
|
|
772
|
-
pythonSource: str = ast.unparse(astModule)
|
|
773
|
-
if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
|
|
774
|
-
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleNames()
|
|
775
|
-
if packageName:
|
|
776
|
-
autoflake_additional_imports.append(packageName)
|
|
777
|
-
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)
|
|
778
|
-
writeStringToHere(pythonSource, pathFilename)
|