mapFolding 0.8.6__py3-none-any.whl → 0.9.1__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 +60 -13
- mapFolding/basecamp.py +32 -17
- mapFolding/beDRY.py +4 -5
- mapFolding/oeis.py +94 -7
- mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
- mapFolding/someAssemblyRequired/__init__.py +71 -50
- mapFolding/someAssemblyRequired/_theTypes.py +11 -15
- mapFolding/someAssemblyRequired/_tool_Make.py +36 -9
- mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +159 -272
- mapFolding/someAssemblyRequired/_toolboxContainers.py +155 -70
- mapFolding/someAssemblyRequired/_toolboxPython.py +168 -44
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +154 -39
- mapFolding/someAssemblyRequired/toolboxNumba.py +72 -230
- mapFolding/someAssemblyRequired/transformationTools.py +370 -141
- mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +7 -4
- mapFolding/theDao.py +19 -16
- mapFolding/theSSOT.py +165 -62
- mapFolding/toolboxFilesystem.py +1 -1
- mapfolding-0.9.1.dist-info/METADATA +177 -0
- mapfolding-0.9.1.dist-info/RECORD +47 -0
- tests/__init__.py +44 -0
- tests/conftest.py +75 -7
- tests/test_computations.py +92 -10
- tests/test_filesystem.py +32 -33
- tests/test_other.py +0 -1
- tests/test_tasks.py +1 -1
- mapFolding/someAssemblyRequired/newInliner.py +0 -22
- mapfolding-0.8.6.dist-info/METADATA +0 -190
- mapfolding-0.8.6.dist-info/RECORD +0 -47
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/WHEEL +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.6.dist-info → mapfolding-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Tools for
|
|
3
|
-
|
|
4
|
-
This module provides
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- Analysis tools: Extract and organize code information
|
|
20
|
-
|
|
21
|
-
While these tools were developed to transform the baseline algorithm into optimized formats,
|
|
22
|
-
they are designed as general-purpose utilities applicable to a wide range of code
|
|
23
|
-
transformation scenarios beyond the scope of this package.
|
|
2
|
+
AST Transformation Tools for Python Code Generation
|
|
3
|
+
|
|
4
|
+
This module provides tools for manipulating and transforming Python abstract syntax trees
|
|
5
|
+
to generate optimized code. It implements a system that:
|
|
6
|
+
|
|
7
|
+
1. Extracts functions and classes from existing modules.
|
|
8
|
+
2. Reshapes and transforms them through AST manipulation.
|
|
9
|
+
3. Manages dependencies and imports.
|
|
10
|
+
4. Generates optimized code with specialized implementations.
|
|
11
|
+
|
|
12
|
+
The module is particularly focused on transforming general-purpose Python code into
|
|
13
|
+
high-performance implementations, especially through dataclass decomposition and
|
|
14
|
+
function inlining for Numba compatibility.
|
|
15
|
+
|
|
16
|
+
At its core, the module implements a transformation assembly-line where code flows from
|
|
17
|
+
readable, maintainable implementations to highly optimized versions while preserving
|
|
18
|
+
logical structure and correctness.
|
|
24
19
|
"""
|
|
20
|
+
|
|
25
21
|
from autoflake import fix_code as autoflake_fix_code
|
|
26
22
|
from collections.abc import Callable, Mapping
|
|
27
23
|
from copy import deepcopy
|
|
@@ -31,8 +27,8 @@ from mapFolding.someAssemblyRequired import (
|
|
|
31
27
|
ast_Identifier,
|
|
32
28
|
be,
|
|
33
29
|
DOT,
|
|
30
|
+
grab,
|
|
34
31
|
ifThis,
|
|
35
|
-
ImaAnnotationType,
|
|
36
32
|
importLogicalPath2Callable,
|
|
37
33
|
IngredientsFunction,
|
|
38
34
|
IngredientsModule,
|
|
@@ -41,75 +37,124 @@ from mapFolding.someAssemblyRequired import (
|
|
|
41
37
|
NodeChanger,
|
|
42
38
|
NodeTourist,
|
|
43
39
|
parseLogicalPath2astModule,
|
|
40
|
+
RecipeSynthesizeFlow,
|
|
44
41
|
ShatteredDataclass,
|
|
45
42
|
str_nameDOTname,
|
|
46
43
|
Then,
|
|
47
|
-
|
|
48
|
-
又,
|
|
44
|
+
个,
|
|
49
45
|
)
|
|
50
|
-
from mapFolding.theSSOT import ComputationState,
|
|
46
|
+
from mapFolding.theSSOT import ComputationState, raiseIfNoneGitHubIssueNumber3, The
|
|
51
47
|
from os import PathLike
|
|
52
48
|
from pathlib import Path, PurePath
|
|
53
49
|
from typing import Any, Literal, overload
|
|
54
50
|
import ast
|
|
55
51
|
import dataclasses
|
|
56
52
|
import pickle
|
|
53
|
+
import python_minifier
|
|
57
54
|
|
|
58
55
|
def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
|
|
56
|
+
"""
|
|
57
|
+
Extract a function definition from an AST module and create an IngredientsFunction.
|
|
58
|
+
|
|
59
|
+
This function finds a function definition with the specified identifier in the given
|
|
60
|
+
AST module and wraps it in an IngredientsFunction object along with its import context.
|
|
61
|
+
|
|
62
|
+
Parameters:
|
|
63
|
+
astModule: The AST module containing the function definition.
|
|
64
|
+
identifierFunctionDef: The name of the function to extract.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
An IngredientsFunction object containing the function definition and its imports.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
raiseIfNoneGitHubIssueNumber3: If the function definition is not found.
|
|
71
|
+
"""
|
|
59
72
|
astFunctionDef = extractFunctionDef(astModule, identifierFunctionDef)
|
|
60
73
|
if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
|
|
61
74
|
return IngredientsFunction(astFunctionDef, LedgerOfImports(astModule))
|
|
62
75
|
|
|
63
76
|
def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
|
|
64
|
-
|
|
77
|
+
"""
|
|
78
|
+
Extract a class definition with a specific name from an AST module.
|
|
79
|
+
|
|
80
|
+
This function searches through an AST module for a class definition that
|
|
81
|
+
matches the provided identifier and returns it if found.
|
|
82
|
+
|
|
83
|
+
Parameters:
|
|
84
|
+
module: The AST module to search within.
|
|
85
|
+
identifier: The name of the class to find.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
The matching class definition AST node, or None if not found.
|
|
89
|
+
"""
|
|
90
|
+
return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
|
|
65
91
|
|
|
66
92
|
def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
|
|
67
|
-
|
|
93
|
+
"""
|
|
94
|
+
Extract a function definition with a specific name from an AST module.
|
|
95
|
+
|
|
96
|
+
This function searches through an AST module for a function definition that
|
|
97
|
+
matches the provided identifier and returns it if found.
|
|
98
|
+
|
|
99
|
+
Parameters:
|
|
100
|
+
module: The AST module to search within.
|
|
101
|
+
identifier: The name of the function to find.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
astFunctionDef: The matching function definition AST node, or None if not found.
|
|
105
|
+
"""
|
|
106
|
+
return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
|
|
107
|
+
|
|
108
|
+
def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.FunctionDef]:
|
|
109
|
+
"""
|
|
110
|
+
Create a dictionary mapping function names to their AST definitions.
|
|
111
|
+
|
|
112
|
+
This function creates a dictionary that maps function names to their AST function
|
|
113
|
+
definition nodes for all functions defined in the given module.
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
Parameters:
|
|
116
|
+
module: The AST module to extract function definitions from.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
A dictionary mapping function identifiers to their AST function definition nodes.
|
|
120
|
+
"""
|
|
70
121
|
dictionaryIdentifier2FunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
71
|
-
NodeTourist(
|
|
122
|
+
NodeTourist(lambda node: isinstance(node, ast.FunctionDef), Then.updateKeyValueIn(DOT.name, Then.extractIt, dictionaryIdentifier2FunctionDef)).visit(module) # type: ignore
|
|
72
123
|
return dictionaryIdentifier2FunctionDef
|
|
73
124
|
|
|
74
|
-
def
|
|
125
|
+
def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) -> ast.FunctionDef:
|
|
75
126
|
"""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
127
|
+
Inline function calls within a function definition to create a self-contained function.
|
|
128
|
+
|
|
129
|
+
This function takes a function identifier and a module, finds the function definition,
|
|
130
|
+
and then recursively inlines all function calls within that function with their
|
|
131
|
+
implementation bodies. This produces a fully inlined function that doesn't depend
|
|
132
|
+
on other function definitions from the module.
|
|
133
|
+
|
|
79
134
|
Parameters:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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.
|
|
135
|
+
identifierToInline: The name of the function to inline.
|
|
136
|
+
module: The AST module containing the function and its dependencies.
|
|
137
|
+
|
|
87
138
|
Returns:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
A dictionary mapping function names to their AST function definitions, containing all functions needed for inlining.
|
|
139
|
+
A modified function definition with all function calls inlined.
|
|
140
|
+
|
|
91
141
|
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.
|
|
142
|
+
ValueError: If the function to inline is not found in the module.
|
|
99
143
|
"""
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
144
|
+
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
145
|
+
try:
|
|
146
|
+
FunctionDefToInline = dictionaryFunctionDef[identifierToInline]
|
|
147
|
+
except KeyError as ERRORmessage:
|
|
148
|
+
raise ValueError(f"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }") from ERRORmessage
|
|
105
149
|
|
|
106
150
|
listIdentifiersCalledFunctions: list[ast_Identifier] = []
|
|
107
|
-
findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) #
|
|
151
|
+
findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) # type: ignore
|
|
108
152
|
findIdentifiersToInline.visit(FunctionDefToInline)
|
|
109
153
|
|
|
110
154
|
dictionary4Inlining: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
111
155
|
for identifier in sorted(set(listIdentifiersCalledFunctions).intersection(dictionaryFunctionDef.keys())):
|
|
112
|
-
|
|
156
|
+
if NodeTourist(ifThis.matchesMeButNotAnyDescendant(ifThis.isCall_Identifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
|
|
157
|
+
dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
|
|
113
158
|
|
|
114
159
|
keepGoing = True
|
|
115
160
|
while keepGoing:
|
|
@@ -117,17 +162,30 @@ def makeDictionary4InliningFunction(identifierToInline: ast_Identifier, dictiona
|
|
|
117
162
|
listIdentifiersCalledFunctions.clear()
|
|
118
163
|
findIdentifiersToInline.visit(Make.Module(list(dictionary4Inlining.values())))
|
|
119
164
|
|
|
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
165
|
listIdentifiersCalledFunctions = sorted((set(listIdentifiersCalledFunctions).difference(dictionary4Inlining.keys())).intersection(dictionaryFunctionDef.keys()))
|
|
124
166
|
if len(listIdentifiersCalledFunctions) > 0:
|
|
125
167
|
keepGoing = True
|
|
126
168
|
for identifier in listIdentifiersCalledFunctions:
|
|
127
|
-
if identifier
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
169
|
+
if NodeTourist(ifThis.matchesMeButNotAnyDescendant(ifThis.isCall_Identifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
|
|
170
|
+
FunctionDefTarget = dictionaryFunctionDef[identifier]
|
|
171
|
+
if len(FunctionDefTarget.body) == 1:
|
|
172
|
+
inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
|
|
173
|
+
for astFunctionDef in dictionary4Inlining.values():
|
|
174
|
+
inliner.visit(astFunctionDef)
|
|
175
|
+
else:
|
|
176
|
+
inliner = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(identifier)),Then.replaceWith(FunctionDefTarget.body[0:-1]))
|
|
177
|
+
for astFunctionDef in dictionary4Inlining.values():
|
|
178
|
+
inliner.visit(astFunctionDef)
|
|
179
|
+
|
|
180
|
+
for identifier, FunctionDefTarget in dictionary4Inlining.items():
|
|
181
|
+
if len(FunctionDefTarget.body) == 1:
|
|
182
|
+
inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
|
|
183
|
+
inliner.visit(FunctionDefToInline)
|
|
184
|
+
else:
|
|
185
|
+
inliner = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(identifier)),Then.replaceWith(FunctionDefTarget.body[0:-1]))
|
|
186
|
+
inliner.visit(FunctionDefToInline)
|
|
187
|
+
ast.fix_missing_locations(FunctionDefToInline)
|
|
188
|
+
return FunctionDefToInline
|
|
131
189
|
|
|
132
190
|
@overload
|
|
133
191
|
def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[True], *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> Path: ...
|
|
@@ -164,11 +222,28 @@ def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: bool =
|
|
|
164
222
|
else:
|
|
165
223
|
pathFilenameJob = getPathFilenameFoldsTotal(stateUniversal.mapShape).with_suffix('.pkl')
|
|
166
224
|
|
|
225
|
+
# Fix code scanning alert - Consider possible security implications associated with pickle module. #17
|
|
167
226
|
pathFilenameJob.write_bytes(pickle.dumps(stateUniversal))
|
|
168
227
|
return pathFilenameJob
|
|
169
228
|
|
|
170
229
|
@dataclasses.dataclass
|
|
171
230
|
class DeReConstructField2ast:
|
|
231
|
+
"""
|
|
232
|
+
Transform a dataclass field into AST node representations for code generation.
|
|
233
|
+
|
|
234
|
+
This class extracts and transforms a dataclass Field object into various AST node
|
|
235
|
+
representations needed for code generation. It handles the conversion of field
|
|
236
|
+
attributes, type annotations, and metadata into AST constructs that can be used
|
|
237
|
+
to reconstruct the field in generated code.
|
|
238
|
+
|
|
239
|
+
The class is particularly important for decomposing dataclass fields (like those in
|
|
240
|
+
ComputationState) to enable their use in specialized contexts like Numba-optimized
|
|
241
|
+
functions, where the full dataclass cannot be directly used but its contents need
|
|
242
|
+
to be accessible.
|
|
243
|
+
|
|
244
|
+
Each field is processed according to its type and metadata to create appropriate
|
|
245
|
+
variable declarations, type annotations, and initialization code as AST nodes.
|
|
246
|
+
"""
|
|
172
247
|
dataclassesDOTdataclassLogicalPathModule: dataclasses.InitVar[str_nameDOTname]
|
|
173
248
|
dataclassClassDef: dataclasses.InitVar[ast.ClassDef]
|
|
174
249
|
dataclassesDOTdataclassInstance_Identifier: dataclasses.InitVar[ast_Identifier]
|
|
@@ -190,10 +265,10 @@ class DeReConstructField2ast:
|
|
|
190
265
|
astName: ast.Name = dataclasses.field(init=False)
|
|
191
266
|
ast_keyword_field__field: ast.keyword = dataclasses.field(init=False)
|
|
192
267
|
ast_nameDOTname: ast.Attribute = dataclasses.field(init=False)
|
|
193
|
-
astAnnotation:
|
|
268
|
+
astAnnotation: ast.expr = dataclasses.field(init=False)
|
|
194
269
|
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)
|
|
270
|
+
astAnnAssignConstructor: ast.AnnAssign|ast.Assign = dataclasses.field(init=False)
|
|
271
|
+
Z0Z_hack: tuple[ast.AnnAssign|ast.Assign, str] = dataclasses.field(init=False)
|
|
197
272
|
|
|
198
273
|
def __post_init__(self, dataclassesDOTdataclassLogicalPathModule: str_nameDOTname, dataclassClassDef: ast.ClassDef, dataclassesDOTdataclassInstance_Identifier: ast_Identifier, field: dataclasses.Field[Any]) -> None:
|
|
199
274
|
self.compare = field.compare
|
|
@@ -211,52 +286,95 @@ class DeReConstructField2ast:
|
|
|
211
286
|
self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
|
|
212
287
|
self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstance_Identifier), self.name)
|
|
213
288
|
|
|
214
|
-
sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)),
|
|
289
|
+
sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), Then.extractIt(DOT.annotation)).captureLastMatch(dataclassClassDef) # type: ignore
|
|
215
290
|
if sherpa is None: raise raiseIfNoneGitHubIssueNumber3
|
|
216
291
|
else: self.astAnnotation = sherpa
|
|
217
292
|
|
|
218
293
|
self.ast_argAnnotated = Make.arg(self.name, self.astAnnotation)
|
|
294
|
+
"""
|
|
295
|
+
from ast import Module, Expr, Subscript, Name, Tuple, Load
|
|
296
|
+
Subscript(
|
|
297
|
+
value=Name(id='ndarray', ctx=Load()),
|
|
298
|
+
slice=Tuple(
|
|
299
|
+
elts=[
|
|
300
|
+
Subscript(
|
|
301
|
+
value=Name(id='tuple', ctx=Load()),
|
|
302
|
+
slice=Name(id='int', ctx=Load()),
|
|
303
|
+
ctx=Load()),
|
|
304
|
+
Subscript(
|
|
305
|
+
value=Name(id='dtype', ctx=Load()),
|
|
306
|
+
slice=Name(id='NumPyLeavesTotal', ctx=Load()),
|
|
307
|
+
ctx=Load())],
|
|
308
|
+
ctx=Load()),
|
|
309
|
+
ctx=Load()
|
|
310
|
+
)
|
|
219
311
|
|
|
312
|
+
"""
|
|
220
313
|
dtype = self.metadata.get('dtype', None)
|
|
221
314
|
if dtype:
|
|
222
315
|
moduleWithLogicalPath: str_nameDOTname = 'numpy'
|
|
223
|
-
|
|
224
|
-
self.ledger.addImportFrom_asStr(moduleWithLogicalPath,
|
|
316
|
+
annotationType = 'ndarray'
|
|
317
|
+
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, annotationType)
|
|
318
|
+
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, 'dtype')
|
|
319
|
+
axesSubscript = Make.Subscript(Make.Name('tuple'), Make.Name('uint8'))
|
|
320
|
+
dtype_asnameName: ast.Name = self.astAnnotation # type: ignore
|
|
321
|
+
if dtype_asnameName.id == 'Array3D':
|
|
322
|
+
axesSubscript = Make.Subscript(Make.Name('tuple'), Make.Tuple([Make.Name('uint8'), Make.Name('uint8'), Make.Name('uint8')]))
|
|
323
|
+
ast_expr = Make.Subscript(Make.Name(annotationType), Make.Tuple([axesSubscript, Make.Subscript(Make.Name('dtype'), dtype_asnameName)]))
|
|
225
324
|
constructor = 'array'
|
|
226
325
|
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, constructor)
|
|
227
326
|
dtypeIdentifier: ast_Identifier = dtype.__name__
|
|
228
|
-
dtype_asnameName: ast.Name = self.astAnnotation
|
|
229
|
-
# dtypeIdentifier_asname: ast_Identifier = moduleWithLogicalPath + '_' + dtypeIdentifier
|
|
230
327
|
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, dtypeIdentifier, dtype_asnameName.id)
|
|
231
|
-
self.astAnnAssignConstructor = Make.AnnAssign(self.astName,
|
|
232
|
-
|
|
233
|
-
# self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', Make.Name(dtypeIdentifier_asname))]))
|
|
328
|
+
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, ast_expr, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', dtype_asnameName)]))
|
|
329
|
+
self.astAnnAssignConstructor = Make.Assign([self.astName], Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', dtype_asnameName)]))
|
|
234
330
|
self.Z0Z_hack = (self.astAnnAssignConstructor, 'array')
|
|
235
|
-
elif
|
|
331
|
+
elif isinstance(self.astAnnotation, ast.Name):
|
|
236
332
|
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(self.astAnnotation, [Make.Constant(-1)]))
|
|
237
|
-
# self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id)
|
|
238
333
|
self.Z0Z_hack = (self.astAnnAssignConstructor, 'scalar')
|
|
239
|
-
elif
|
|
334
|
+
elif isinstance(self.astAnnotation, ast.Subscript):
|
|
240
335
|
elementConstructor: ast_Identifier = self.metadata['elementConstructor']
|
|
241
336
|
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
|
|
242
337
|
takeTheTuple: ast.Tuple = deepcopy(self.astAnnotation.slice) # type: ignore
|
|
243
338
|
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, takeTheTuple)
|
|
244
339
|
self.Z0Z_hack = (self.astAnnAssignConstructor, elementConstructor)
|
|
245
|
-
if
|
|
340
|
+
if isinstance(self.astAnnotation, ast.Name):
|
|
246
341
|
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id) # pyright: ignore [reportUnknownArgumentType, reportUnknownMemberType, reportIJustCalledATypeGuardMethod_WTF]
|
|
247
342
|
|
|
248
343
|
def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclass_Identifier: ast_Identifier, instance_Identifier: ast_Identifier) -> ShatteredDataclass:
|
|
249
344
|
"""
|
|
345
|
+
Decompose a dataclass definition into AST components for manipulation and code generation.
|
|
346
|
+
|
|
347
|
+
This function breaks down a complete dataclass (like ComputationState) into its constituent
|
|
348
|
+
parts as AST nodes, enabling fine-grained manipulation of its fields for code generation.
|
|
349
|
+
It extracts all field definitions, annotations, and metadata, organizing them into a
|
|
350
|
+
ShatteredDataclass that provides convenient access to AST representations needed for
|
|
351
|
+
different code generation contexts.
|
|
352
|
+
|
|
353
|
+
The function identifies a special "counting variable" (marked with 'theCountingIdentifier'
|
|
354
|
+
metadata) which is crucial for map folding algorithms, ensuring it's properly accessible
|
|
355
|
+
in the generated code.
|
|
356
|
+
|
|
357
|
+
This decomposition is particularly important when generating optimized code (e.g., for Numba)
|
|
358
|
+
where dataclass instances can't be directly used but their fields need to be individually
|
|
359
|
+
manipulated and passed to computational functions.
|
|
360
|
+
|
|
250
361
|
Parameters:
|
|
251
|
-
logicalPathModule:
|
|
252
|
-
dataclass_Identifier: The
|
|
253
|
-
instance_Identifier:
|
|
362
|
+
logicalPathModule: The fully qualified module path containing the dataclass definition.
|
|
363
|
+
dataclass_Identifier: The name of the dataclass to decompose.
|
|
364
|
+
instance_Identifier: The variable name to use for the dataclass instance in generated code.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
A ShatteredDataclass containing AST representations of all dataclass components,
|
|
368
|
+
with imports, field definitions, annotations, and repackaging code.
|
|
369
|
+
|
|
370
|
+
Raises:
|
|
371
|
+
ValueError: If the dataclass cannot be found in the specified module or if no counting variable is identified in the dataclass.
|
|
254
372
|
"""
|
|
255
373
|
Official_fieldOrder: list[ast_Identifier] = []
|
|
256
374
|
dictionaryDeReConstruction: dict[ast_Identifier, DeReConstructField2ast] = {}
|
|
257
375
|
|
|
258
376
|
dataclassClassDef = extractClassDef(parseLogicalPath2astModule(logicalPathModule), dataclass_Identifier)
|
|
259
|
-
if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find {dataclass_Identifier=} in {logicalPathModule=}
|
|
377
|
+
if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find `{dataclass_Identifier = }` in `{logicalPathModule = }`.")
|
|
260
378
|
|
|
261
379
|
countingVariable = None
|
|
262
380
|
for aField in dataclasses.fields(importLogicalPath2Callable(logicalPathModule, dataclass_Identifier)): # pyright: ignore [reportArgumentType]
|
|
@@ -266,7 +384,7 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclas
|
|
|
266
384
|
countingVariable = dictionaryDeReConstruction[aField.name].name
|
|
267
385
|
|
|
268
386
|
if countingVariable is None:
|
|
269
|
-
raise ValueError(f"I could not find the counting variable in {dataclass_Identifier=} in {logicalPathModule=}
|
|
387
|
+
raise ValueError(f"I could not find the counting variable in `{dataclass_Identifier = }` in `{logicalPathModule = }`.")
|
|
270
388
|
|
|
271
389
|
shatteredDataclass = ShatteredDataclass(
|
|
272
390
|
countingVariableAnnotation=dictionaryDeReConstruction[countingVariable].astAnnotation,
|
|
@@ -290,6 +408,31 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclas
|
|
|
290
408
|
return shatteredDataclass
|
|
291
409
|
|
|
292
410
|
def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
411
|
+
"""
|
|
412
|
+
Convert an IngredientsModule to Python source code and write it to a file.
|
|
413
|
+
|
|
414
|
+
This function renders an IngredientsModule into executable Python code,
|
|
415
|
+
applies code quality improvements like import organization via autoflake,
|
|
416
|
+
and writes the result to the specified file path.
|
|
417
|
+
|
|
418
|
+
The function performs several key steps:
|
|
419
|
+
1. Converts the AST module structure to a valid Python AST
|
|
420
|
+
2. Fixes location attributes in the AST for proper formatting
|
|
421
|
+
3. Converts the AST to Python source code
|
|
422
|
+
4. Optimizes imports using autoflake
|
|
423
|
+
5. Writes the final source code to the specified file location
|
|
424
|
+
|
|
425
|
+
This is typically the final step in the code generation pipeline,
|
|
426
|
+
producing optimized Python modules ready for execution.
|
|
427
|
+
|
|
428
|
+
Parameters:
|
|
429
|
+
ingredients: The IngredientsModule containing the module definition.
|
|
430
|
+
pathFilename: The file path where the module should be written.
|
|
431
|
+
packageName: Optional package name to preserve in import optimization.
|
|
432
|
+
|
|
433
|
+
Raises:
|
|
434
|
+
raiseIfNoneGitHubIssueNumber3: If the generated source code is empty.
|
|
435
|
+
"""
|
|
293
436
|
astModule = Make.Module(ingredients.body, ingredients.type_ignores)
|
|
294
437
|
ast.fix_missing_locations(astModule)
|
|
295
438
|
pythonSource: str = ast.unparse(astModule)
|
|
@@ -298,19 +441,47 @@ def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any]
|
|
|
298
441
|
if packageName:
|
|
299
442
|
autoflake_additional_imports.append(packageName)
|
|
300
443
|
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=True, remove_duplicate_keys = False, remove_unused_variables = False)
|
|
444
|
+
# pythonSource = python_minifier.minify(pythonSource)
|
|
301
445
|
writeStringToHere(pythonSource, pathFilename)
|
|
302
446
|
|
|
303
447
|
# END of acceptable classes and functions ======================================================
|
|
448
|
+
def removeUnusedParameters(ingredientsFunction: IngredientsFunction):
|
|
449
|
+
list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
|
|
450
|
+
list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
|
|
451
|
+
listName: list[ast.Name] = []
|
|
452
|
+
NodeTourist(be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
|
|
453
|
+
list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
|
|
454
|
+
list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
|
|
455
|
+
|
|
304
456
|
dictionaryEstimates: dict[tuple[int, ...], int] = {
|
|
305
|
-
(2,2,2,2,2,2,2,2):
|
|
306
|
-
(2,21):
|
|
307
|
-
(3,15):
|
|
457
|
+
(2,2,2,2,2,2,2,2): 798148657152000,
|
|
458
|
+
(2,21): 776374224866624,
|
|
459
|
+
(3,15): 824761667826225,
|
|
308
460
|
(3,3,3,3): 85109616000000000000000000000000,
|
|
309
|
-
(8,8):
|
|
461
|
+
(8,8): 791274195985524900,
|
|
310
462
|
}
|
|
311
463
|
|
|
312
464
|
# END of marginal classes and functions ======================================================
|
|
313
|
-
def Z0Z_lameFindReplace(astTree
|
|
465
|
+
def Z0Z_lameFindReplace(astTree: 个, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]) -> 个:
|
|
466
|
+
"""
|
|
467
|
+
Recursively replace AST nodes based on a mapping of find-replace pairs.
|
|
468
|
+
|
|
469
|
+
This function applies brute-force node replacement throughout an AST tree
|
|
470
|
+
by comparing textual representations of nodes. While not the most efficient
|
|
471
|
+
approach, it provides a reliable way to replace complex nested structures
|
|
472
|
+
when more precise targeting methods are difficult to implement.
|
|
473
|
+
|
|
474
|
+
The function continues replacing nodes until no more changes are detected
|
|
475
|
+
in the AST's textual representation, ensuring complete replacement throughout
|
|
476
|
+
the tree structure.
|
|
477
|
+
|
|
478
|
+
Parameters:
|
|
479
|
+
astTree: The AST structure to modify.
|
|
480
|
+
mappingFindReplaceNodes: A mapping from source nodes to replacement nodes.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
The modified AST structure with all matching nodes replaced.
|
|
484
|
+
"""
|
|
314
485
|
keepGoing = True
|
|
315
486
|
newTree = deepcopy(astTree)
|
|
316
487
|
|
|
@@ -324,54 +495,112 @@ def Z0Z_lameFindReplace(astTree, mappingFindReplaceNodes: Mapping[ast.AST, ast.A
|
|
|
324
495
|
astTree = deepcopy(newTree)
|
|
325
496
|
return newTree
|
|
326
497
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
498
|
+
def makeNewFlow(recipeFlow: RecipeSynthesizeFlow) -> IngredientsModule:
|
|
499
|
+
# TODO a tool to automatically remove unused variables from the ArgumentsSpecification (return, and returns) _might_ be nice.
|
|
500
|
+
# Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
|
|
501
|
+
listAllIngredientsFunctions = [
|
|
502
|
+
(ingredientsInitialize := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableInitialize)),
|
|
503
|
+
(ingredientsParallel := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableParallel)),
|
|
504
|
+
(ingredientsSequential := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableSequential)),
|
|
505
|
+
(ingredientsDispatcher := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableDispatcher)),
|
|
506
|
+
]
|
|
507
|
+
|
|
508
|
+
# Inline functions ========================================================
|
|
509
|
+
# NOTE Replacements statements are based on the identifiers in the _source_, so operate on the source identifiers.
|
|
510
|
+
ingredientsInitialize.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableInitialize, recipeFlow.source_astModule)
|
|
511
|
+
ingredientsParallel.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableParallel, recipeFlow.source_astModule)
|
|
512
|
+
ingredientsSequential.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableSequential, recipeFlow.source_astModule)
|
|
513
|
+
|
|
514
|
+
# assignRecipeIdentifiersToCallable. =============================
|
|
515
|
+
# Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
|
|
516
|
+
# How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
|
|
517
|
+
# NOTE reminder: you are updating these `ast.Name` here (and not in a more general search) because this is a
|
|
518
|
+
# narrow search for `ast.Call` so you won't accidentally replace unrelated `ast.Name`.
|
|
519
|
+
listFindReplace = [(recipeFlow.sourceCallableDispatcher, recipeFlow.callableDispatcher),
|
|
520
|
+
(recipeFlow.sourceCallableInitialize, recipeFlow.callableInitialize),
|
|
521
|
+
(recipeFlow.sourceCallableParallel, recipeFlow.callableParallel),
|
|
522
|
+
(recipeFlow.sourceCallableSequential, recipeFlow.callableSequential),]
|
|
523
|
+
for ingredients in listAllIngredientsFunctions:
|
|
524
|
+
for source_Identifier, recipe_Identifier in listFindReplace:
|
|
525
|
+
updateCallName = NodeChanger(ifThis.isCall_Identifier(source_Identifier), grab.funcAttribute(Then.replaceWith(Make.Name(recipe_Identifier))))
|
|
526
|
+
updateCallName.visit(ingredients.astFunctionDef)
|
|
527
|
+
|
|
528
|
+
ingredientsDispatcher.astFunctionDef.name = recipeFlow.callableDispatcher
|
|
529
|
+
ingredientsInitialize.astFunctionDef.name = recipeFlow.callableInitialize
|
|
530
|
+
ingredientsParallel.astFunctionDef.name = recipeFlow.callableParallel
|
|
531
|
+
ingredientsSequential.astFunctionDef.name = recipeFlow.callableSequential
|
|
532
|
+
|
|
533
|
+
# Assign identifiers per the recipe. ==============================
|
|
534
|
+
listFindReplace = [(recipeFlow.sourceDataclassInstance, recipeFlow.dataclassInstance),
|
|
535
|
+
(recipeFlow.sourceDataclassInstanceTaskDistribution, recipeFlow.dataclassInstanceTaskDistribution),
|
|
536
|
+
(recipeFlow.sourceConcurrencyManagerNamespace, recipeFlow.concurrencyManagerNamespace),]
|
|
537
|
+
for ingredients in listAllIngredientsFunctions:
|
|
538
|
+
for source_Identifier, recipe_Identifier in listFindReplace:
|
|
539
|
+
updateName = NodeChanger(ifThis.isName_Identifier(source_Identifier) , grab.idAttribute(Then.replaceWith(recipe_Identifier)))
|
|
540
|
+
update_arg = NodeChanger(ifThis.isArgument_Identifier(source_Identifier), grab.argAttribute(Then.replaceWith(recipe_Identifier)))
|
|
541
|
+
updateName.visit(ingredients.astFunctionDef)
|
|
542
|
+
update_arg.visit(ingredients.astFunctionDef)
|
|
543
|
+
|
|
544
|
+
updateConcurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(recipeFlow.sourceConcurrencyManagerNamespace, recipeFlow.sourceConcurrencyManagerIdentifier)
|
|
545
|
+
, grab.funcAttribute(Then.replaceWith(Make.Attribute(Make.Name(recipeFlow.concurrencyManagerNamespace), recipeFlow.concurrencyManagerIdentifier))))
|
|
546
|
+
updateConcurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
|
|
547
|
+
|
|
548
|
+
# shatter Dataclass =======================================================
|
|
549
|
+
instance_Identifier = recipeFlow.dataclassInstance
|
|
550
|
+
getTheOtherRecord_damn = recipeFlow.dataclassInstanceTaskDistribution
|
|
551
|
+
shatteredDataclass = shatter_dataclassesDOTdataclass(recipeFlow.logicalPathModuleDataclass, recipeFlow.sourceDataclassIdentifier, instance_Identifier)
|
|
552
|
+
ingredientsDispatcher.imports.update(shatteredDataclass.ledger)
|
|
553
|
+
|
|
554
|
+
# How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
|
|
555
|
+
# Change callable parameters and Call to the callable at the same time ====
|
|
556
|
+
# sequentialCallable =========================================================
|
|
557
|
+
if recipeFlow.removeDataclassSequential:
|
|
558
|
+
ingredientsSequential.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
|
|
559
|
+
astCallSequentialCallable = Make.Call(Make.Name(recipeFlow.callableSequential), shatteredDataclass.listName4Parameters)
|
|
560
|
+
changeReturnSequentialCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.fragments4AssignmentOrParameters)))
|
|
561
|
+
ingredientsSequential.astFunctionDef.returns = shatteredDataclass.signatureReturnAnnotation
|
|
562
|
+
replaceAssignSequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(recipeFlow.callableSequential)), Then.replaceWith(Make.Assign(listTargets=[shatteredDataclass.fragments4AssignmentOrParameters], value=astCallSequentialCallable)))
|
|
563
|
+
|
|
564
|
+
unpack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(recipeFlow.callableSequential)), Then.insertThisAbove(shatteredDataclass.listUnpack))
|
|
565
|
+
repack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(recipeFlow.callableSequential)), Then.insertThisBelow([shatteredDataclass.repack]))
|
|
566
|
+
|
|
567
|
+
changeReturnSequentialCallable.visit(ingredientsSequential.astFunctionDef)
|
|
568
|
+
replaceAssignSequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
|
|
569
|
+
unpack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
|
|
570
|
+
repack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
|
|
571
|
+
|
|
572
|
+
ingredientsSequential.astFunctionDef = Z0Z_lameFindReplace(ingredientsSequential.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
|
|
573
|
+
|
|
574
|
+
# parallelCallable =========================================================
|
|
575
|
+
if recipeFlow.removeDataclassParallel:
|
|
576
|
+
ingredientsParallel.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
|
|
577
|
+
replaceCall2concurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(recipeFlow.concurrencyManagerNamespace, recipeFlow.concurrencyManagerIdentifier), Then.replaceWith(Make.Call(Make.Attribute(Make.Name(recipeFlow.concurrencyManagerNamespace), recipeFlow.concurrencyManagerIdentifier), listArguments=[Make.Name(recipeFlow.callableParallel)] + shatteredDataclass.listName4Parameters)))
|
|
578
|
+
|
|
579
|
+
# NOTE I am dissatisfied with this logic for many reasons, including that it requires separate NodeCollector and NodeReplacer instances.
|
|
580
|
+
astCallConcurrencyResult: list[ast.Call] = []
|
|
581
|
+
get_astCallConcurrencyResult = NodeTourist(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), getIt(astCallConcurrencyResult))
|
|
582
|
+
get_astCallConcurrencyResult.visit(ingredientsDispatcher.astFunctionDef)
|
|
583
|
+
replaceAssignParallelCallable = NodeChanger(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), grab.valueAttribute(Then.replaceWith(astCallConcurrencyResult[0])))
|
|
584
|
+
replaceAssignParallelCallable.visit(ingredientsDispatcher.astFunctionDef)
|
|
585
|
+
changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.countingVariableName)))
|
|
586
|
+
ingredientsParallel.astFunctionDef.returns = shatteredDataclass.countingVariableAnnotation
|
|
587
|
+
|
|
588
|
+
unpack4parallelCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCallAttributeNamespace_Identifier(recipeFlow.concurrencyManagerNamespace, recipeFlow.concurrencyManagerIdentifier)), Then.insertThisAbove(shatteredDataclass.listUnpack))
|
|
589
|
+
|
|
590
|
+
unpack4parallelCallable.visit(ingredientsDispatcher.astFunctionDef)
|
|
591
|
+
replaceCall2concurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
|
|
592
|
+
changeReturnParallelCallable.visit(ingredientsParallel.astFunctionDef)
|
|
593
|
+
|
|
594
|
+
ingredientsParallel.astFunctionDef = Z0Z_lameFindReplace(ingredientsParallel.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
|
|
595
|
+
|
|
596
|
+
# Module-level transformations ===========================================================
|
|
597
|
+
ingredientsModuleNumbaUnified = IngredientsModule(ingredientsFunction=listAllIngredientsFunctions, imports=LedgerOfImports(recipeFlow.source_astModule))
|
|
598
|
+
ingredientsModuleNumbaUnified.removeImportFromModule('numpy')
|
|
599
|
+
|
|
600
|
+
return ingredientsModuleNumbaUnified
|
|
601
|
+
|
|
602
|
+
def getIt(astCallConcurrencyResult: list[ast.Call]) -> Callable[[ast.AST], ast.AST]:
|
|
603
|
+
def workhorse(node: ast.AST) -> ast.AST:
|
|
604
|
+
NodeTourist(be.Call, Then.appendTo(astCallConcurrencyResult)).visit(node)
|
|
605
|
+
return node
|
|
606
|
+
return workhorse
|