mapFolding 0.8.5__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapFolding/__init__.py +66 -18
- mapFolding/basecamp.py +32 -17
- mapFolding/beDRY.py +3 -3
- mapFolding/oeis.py +121 -25
- mapFolding/someAssemblyRequired/__init__.py +48 -27
- mapFolding/someAssemblyRequired/_theTypes.py +11 -15
- mapFolding/someAssemblyRequired/_tool_Make.py +40 -12
- mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +151 -276
- mapFolding/someAssemblyRequired/_toolboxContainers.py +185 -51
- mapFolding/someAssemblyRequired/_toolboxPython.py +165 -44
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +141 -20
- mapFolding/someAssemblyRequired/toolboxNumba.py +93 -52
- mapFolding/someAssemblyRequired/transformationTools.py +228 -138
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
- mapFolding/theSSOT.py +147 -55
- mapFolding/toolboxFilesystem.py +1 -1
- mapfolding-0.9.0.dist-info/METADATA +177 -0
- mapfolding-0.9.0.dist-info/RECORD +46 -0
- tests/__init__.py +44 -0
- tests/conftest.py +75 -7
- tests/test_computations.py +90 -9
- tests/test_filesystem.py +32 -33
- tests/test_other.py +0 -1
- tests/test_tasks.py +2 -2
- mapFolding/noHomeYet.py +0 -32
- mapFolding/someAssemblyRequired/newInliner.py +0 -22
- mapfolding-0.8.5.dist-info/METADATA +0 -190
- mapfolding-0.8.5.dist-info/RECORD +0 -48
- {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/WHEEL +0 -0
- {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.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
|
|
@@ -29,10 +25,8 @@ from mapFolding.beDRY import outfitCountFolds
|
|
|
29
25
|
from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, writeStringToHere
|
|
30
26
|
from mapFolding.someAssemblyRequired import (
|
|
31
27
|
ast_Identifier,
|
|
32
|
-
be,
|
|
33
28
|
DOT,
|
|
34
29
|
ifThis,
|
|
35
|
-
ImaAnnotationType,
|
|
36
30
|
importLogicalPath2Callable,
|
|
37
31
|
IngredientsFunction,
|
|
38
32
|
IngredientsModule,
|
|
@@ -44,10 +38,9 @@ from mapFolding.someAssemblyRequired import (
|
|
|
44
38
|
ShatteredDataclass,
|
|
45
39
|
str_nameDOTname,
|
|
46
40
|
Then,
|
|
47
|
-
|
|
48
|
-
又,
|
|
41
|
+
个,
|
|
49
42
|
)
|
|
50
|
-
from mapFolding.theSSOT import ComputationState,
|
|
43
|
+
from mapFolding.theSSOT import ComputationState, raiseIfNoneGitHubIssueNumber3, The
|
|
51
44
|
from os import PathLike
|
|
52
45
|
from pathlib import Path, PurePath
|
|
53
46
|
from typing import Any, Literal, overload
|
|
@@ -56,60 +49,108 @@ import dataclasses
|
|
|
56
49
|
import pickle
|
|
57
50
|
|
|
58
51
|
def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
|
|
52
|
+
"""
|
|
53
|
+
Extract a function definition from an AST module and create an IngredientsFunction.
|
|
54
|
+
|
|
55
|
+
This function finds a function definition with the specified identifier in the given
|
|
56
|
+
AST module and wraps it in an IngredientsFunction object along with its import context.
|
|
57
|
+
|
|
58
|
+
Parameters:
|
|
59
|
+
astModule: The AST module containing the function definition.
|
|
60
|
+
identifierFunctionDef: The name of the function to extract.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
An IngredientsFunction object containing the function definition and its imports.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
raiseIfNoneGitHubIssueNumber3: If the function definition is not found.
|
|
67
|
+
"""
|
|
59
68
|
astFunctionDef = extractFunctionDef(astModule, identifierFunctionDef)
|
|
60
69
|
if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
|
|
61
70
|
return IngredientsFunction(astFunctionDef, LedgerOfImports(astModule))
|
|
62
71
|
|
|
63
72
|
def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
|
|
64
|
-
|
|
73
|
+
"""
|
|
74
|
+
Extract a class definition with a specific name from an AST module.
|
|
75
|
+
|
|
76
|
+
This function searches through an AST module for a class definition that
|
|
77
|
+
matches the provided identifier and returns it if found.
|
|
78
|
+
|
|
79
|
+
Parameters:
|
|
80
|
+
module: The AST module to search within.
|
|
81
|
+
identifier: The name of the class to find.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The matching class definition AST node, or None if not found.
|
|
85
|
+
"""
|
|
86
|
+
return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
|
|
65
87
|
|
|
66
88
|
def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
|
|
67
|
-
|
|
89
|
+
"""
|
|
90
|
+
Extract a function definition with a specific name from an AST module.
|
|
91
|
+
|
|
92
|
+
This function searches through an AST module for a function definition that
|
|
93
|
+
matches the provided identifier and returns it if found.
|
|
94
|
+
|
|
95
|
+
Parameters:
|
|
96
|
+
module: The AST module to search within.
|
|
97
|
+
identifier: The name of the function to find.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The matching function definition AST node, or None if not found.
|
|
101
|
+
"""
|
|
102
|
+
return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
|
|
103
|
+
|
|
104
|
+
def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.FunctionDef]:
|
|
105
|
+
"""
|
|
106
|
+
Create a dictionary mapping function names to their AST definitions.
|
|
107
|
+
|
|
108
|
+
This function creates a dictionary that maps function names to their AST function
|
|
109
|
+
definition nodes for all functions defined in the given module.
|
|
68
110
|
|
|
69
|
-
|
|
111
|
+
Parameters:
|
|
112
|
+
module: The AST module to extract function definitions from.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
A dictionary mapping function identifiers to their AST function definition nodes.
|
|
116
|
+
"""
|
|
70
117
|
dictionaryIdentifier2FunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
71
|
-
NodeTourist(
|
|
118
|
+
NodeTourist(lambda node: isinstance(node, ast.FunctionDef), Then.updateKeyValueIn(DOT.name, Then.extractIt, dictionaryIdentifier2FunctionDef)).visit(module) # type: ignore
|
|
72
119
|
return dictionaryIdentifier2FunctionDef
|
|
73
120
|
|
|
74
|
-
def
|
|
121
|
+
def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) -> ast.FunctionDef:
|
|
75
122
|
"""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
123
|
+
Inline function calls within a function definition to create a self-contained function.
|
|
124
|
+
|
|
125
|
+
This function takes a function identifier and a module, finds the function definition,
|
|
126
|
+
and then recursively inlines all function calls within that function with their
|
|
127
|
+
implementation bodies. This produces a fully inlined function that doesn't depend
|
|
128
|
+
on other function definitions from the module.
|
|
129
|
+
|
|
79
130
|
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.
|
|
131
|
+
identifierToInline: The name of the function to inline.
|
|
132
|
+
module: The AST module containing the function and its dependencies.
|
|
133
|
+
|
|
87
134
|
Returns:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
A dictionary mapping function names to their AST function definitions, containing all functions needed for inlining.
|
|
135
|
+
A modified function definition with all function calls inlined.
|
|
136
|
+
|
|
91
137
|
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.
|
|
138
|
+
ValueError: If the function to inline is not found in the module.
|
|
99
139
|
"""
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
141
|
+
try:
|
|
142
|
+
FunctionDefToInline = dictionaryFunctionDef[identifierToInline]
|
|
143
|
+
except KeyError as ERRORmessage:
|
|
144
|
+
raise ValueError(f"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }") from ERRORmessage
|
|
105
145
|
|
|
106
146
|
listIdentifiersCalledFunctions: list[ast_Identifier] = []
|
|
107
|
-
findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) #
|
|
147
|
+
findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) # type: ignore
|
|
108
148
|
findIdentifiersToInline.visit(FunctionDefToInline)
|
|
109
149
|
|
|
110
150
|
dictionary4Inlining: dict[ast_Identifier, ast.FunctionDef] = {}
|
|
111
151
|
for identifier in sorted(set(listIdentifiersCalledFunctions).intersection(dictionaryFunctionDef.keys())):
|
|
112
|
-
|
|
152
|
+
if NodeTourist(ifThis.matchesMeButNotAnyDescendant(ifThis.isCall_Identifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
|
|
153
|
+
dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
|
|
113
154
|
|
|
114
155
|
keepGoing = True
|
|
115
156
|
while keepGoing:
|
|
@@ -117,17 +158,30 @@ def makeDictionary4InliningFunction(identifierToInline: ast_Identifier, dictiona
|
|
|
117
158
|
listIdentifiersCalledFunctions.clear()
|
|
118
159
|
findIdentifiersToInline.visit(Make.Module(list(dictionary4Inlining.values())))
|
|
119
160
|
|
|
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
161
|
listIdentifiersCalledFunctions = sorted((set(listIdentifiersCalledFunctions).difference(dictionary4Inlining.keys())).intersection(dictionaryFunctionDef.keys()))
|
|
124
162
|
if len(listIdentifiersCalledFunctions) > 0:
|
|
125
163
|
keepGoing = True
|
|
126
164
|
for identifier in listIdentifiersCalledFunctions:
|
|
127
|
-
if identifier
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
165
|
+
if NodeTourist(ifThis.matchesMeButNotAnyDescendant(ifThis.isCall_Identifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
|
|
166
|
+
FunctionDefTarget = dictionaryFunctionDef[identifier]
|
|
167
|
+
if len(FunctionDefTarget.body) == 1:
|
|
168
|
+
inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
|
|
169
|
+
for astFunctionDef in dictionary4Inlining.values():
|
|
170
|
+
inliner.visit(astFunctionDef)
|
|
171
|
+
else:
|
|
172
|
+
inliner = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(identifier)),Then.replaceWith(FunctionDefTarget.body[0:-1]))
|
|
173
|
+
for astFunctionDef in dictionary4Inlining.values():
|
|
174
|
+
inliner.visit(astFunctionDef)
|
|
175
|
+
|
|
176
|
+
for identifier, FunctionDefTarget in dictionary4Inlining.items():
|
|
177
|
+
if len(FunctionDefTarget.body) == 1:
|
|
178
|
+
inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
|
|
179
|
+
inliner.visit(FunctionDefToInline)
|
|
180
|
+
else:
|
|
181
|
+
inliner = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(identifier)),Then.replaceWith(FunctionDefTarget.body[0:-1]))
|
|
182
|
+
inliner.visit(FunctionDefToInline)
|
|
183
|
+
ast.fix_missing_locations(FunctionDefToInline)
|
|
184
|
+
return FunctionDefToInline
|
|
131
185
|
|
|
132
186
|
@overload
|
|
133
187
|
def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[True], *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> Path: ...
|
|
@@ -164,11 +218,28 @@ def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: bool =
|
|
|
164
218
|
else:
|
|
165
219
|
pathFilenameJob = getPathFilenameFoldsTotal(stateUniversal.mapShape).with_suffix('.pkl')
|
|
166
220
|
|
|
221
|
+
# Fix code scanning alert - Consider possible security implications associated with pickle module. #17
|
|
167
222
|
pathFilenameJob.write_bytes(pickle.dumps(stateUniversal))
|
|
168
223
|
return pathFilenameJob
|
|
169
224
|
|
|
170
225
|
@dataclasses.dataclass
|
|
171
226
|
class DeReConstructField2ast:
|
|
227
|
+
"""
|
|
228
|
+
Transform a dataclass field into AST node representations for code generation.
|
|
229
|
+
|
|
230
|
+
This class extracts and transforms a dataclass Field object into various AST node
|
|
231
|
+
representations needed for code generation. It handles the conversion of field
|
|
232
|
+
attributes, type annotations, and metadata into AST constructs that can be used
|
|
233
|
+
to reconstruct the field in generated code.
|
|
234
|
+
|
|
235
|
+
The class is particularly important for decomposing dataclass fields (like those in
|
|
236
|
+
ComputationState) to enable their use in specialized contexts like Numba-optimized
|
|
237
|
+
functions, where the full dataclass cannot be directly used but its contents need
|
|
238
|
+
to be accessible.
|
|
239
|
+
|
|
240
|
+
Each field is processed according to its type and metadata to create appropriate
|
|
241
|
+
variable declarations, type annotations, and initialization code as AST nodes.
|
|
242
|
+
"""
|
|
172
243
|
dataclassesDOTdataclassLogicalPathModule: dataclasses.InitVar[str_nameDOTname]
|
|
173
244
|
dataclassClassDef: dataclasses.InitVar[ast.ClassDef]
|
|
174
245
|
dataclassesDOTdataclassInstance_Identifier: dataclasses.InitVar[ast_Identifier]
|
|
@@ -190,7 +261,7 @@ class DeReConstructField2ast:
|
|
|
190
261
|
astName: ast.Name = dataclasses.field(init=False)
|
|
191
262
|
ast_keyword_field__field: ast.keyword = dataclasses.field(init=False)
|
|
192
263
|
ast_nameDOTname: ast.Attribute = dataclasses.field(init=False)
|
|
193
|
-
astAnnotation:
|
|
264
|
+
astAnnotation: ast.expr = dataclasses.field(init=False)
|
|
194
265
|
ast_argAnnotated: ast.arg = dataclasses.field(init=False)
|
|
195
266
|
astAnnAssignConstructor: ast.AnnAssign = dataclasses.field(init=False)
|
|
196
267
|
Z0Z_hack: tuple[ast.AnnAssign, str] = dataclasses.field(init=False)
|
|
@@ -211,7 +282,7 @@ class DeReConstructField2ast:
|
|
|
211
282
|
self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
|
|
212
283
|
self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstance_Identifier), self.name)
|
|
213
284
|
|
|
214
|
-
sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)),
|
|
285
|
+
sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), Then.extractIt(DOT.annotation)).captureLastMatch(dataclassClassDef) # type: ignore
|
|
215
286
|
if sherpa is None: raise raiseIfNoneGitHubIssueNumber3
|
|
216
287
|
else: self.astAnnotation = sherpa
|
|
217
288
|
|
|
@@ -219,36 +290,63 @@ class DeReConstructField2ast:
|
|
|
219
290
|
|
|
220
291
|
dtype = self.metadata.get('dtype', None)
|
|
221
292
|
if dtype:
|
|
293
|
+
moduleWithLogicalPath: str_nameDOTname = 'numpy'
|
|
294
|
+
annotation = 'ndarray'
|
|
295
|
+
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, annotation)
|
|
222
296
|
constructor = 'array'
|
|
223
|
-
self.
|
|
224
|
-
|
|
225
|
-
self.
|
|
297
|
+
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, constructor)
|
|
298
|
+
dtypeIdentifier: ast_Identifier = dtype.__name__
|
|
299
|
+
dtype_asnameName: ast.Name = self.astAnnotation # type: ignore
|
|
300
|
+
self.ledger.addImportFrom_asStr(moduleWithLogicalPath, dtypeIdentifier, dtype_asnameName.id)
|
|
301
|
+
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, Make.Name(annotation), Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', dtype_asnameName)]))
|
|
226
302
|
self.Z0Z_hack = (self.astAnnAssignConstructor, 'array')
|
|
227
|
-
elif
|
|
303
|
+
elif isinstance(self.astAnnotation, ast.Name):
|
|
228
304
|
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
305
|
self.Z0Z_hack = (self.astAnnAssignConstructor, 'scalar')
|
|
231
|
-
elif
|
|
306
|
+
elif isinstance(self.astAnnotation, ast.Subscript):
|
|
232
307
|
elementConstructor: ast_Identifier = self.metadata['elementConstructor']
|
|
233
308
|
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
|
|
234
309
|
takeTheTuple: ast.Tuple = deepcopy(self.astAnnotation.slice) # type: ignore
|
|
235
310
|
self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, takeTheTuple)
|
|
236
311
|
self.Z0Z_hack = (self.astAnnAssignConstructor, elementConstructor)
|
|
237
|
-
if
|
|
312
|
+
if isinstance(self.astAnnotation, ast.Name):
|
|
238
313
|
self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id) # pyright: ignore [reportUnknownArgumentType, reportUnknownMemberType, reportIJustCalledATypeGuardMethod_WTF]
|
|
239
314
|
|
|
240
315
|
def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclass_Identifier: ast_Identifier, instance_Identifier: ast_Identifier) -> ShatteredDataclass:
|
|
241
316
|
"""
|
|
317
|
+
Decompose a dataclass definition into AST components for manipulation and code generation.
|
|
318
|
+
|
|
319
|
+
This function breaks down a complete dataclass (like ComputationState) into its constituent
|
|
320
|
+
parts as AST nodes, enabling fine-grained manipulation of its fields for code generation.
|
|
321
|
+
It extracts all field definitions, annotations, and metadata, organizing them into a
|
|
322
|
+
ShatteredDataclass that provides convenient access to AST representations needed for
|
|
323
|
+
different code generation contexts.
|
|
324
|
+
|
|
325
|
+
The function identifies a special "counting variable" (marked with 'theCountingIdentifier'
|
|
326
|
+
metadata) which is crucial for map folding algorithms, ensuring it's properly accessible
|
|
327
|
+
in the generated code.
|
|
328
|
+
|
|
329
|
+
This decomposition is particularly important when generating optimized code (e.g., for Numba)
|
|
330
|
+
where dataclass instances can't be directly used but their fields need to be individually
|
|
331
|
+
manipulated and passed to computational functions.
|
|
332
|
+
|
|
242
333
|
Parameters:
|
|
243
|
-
logicalPathModule:
|
|
244
|
-
dataclass_Identifier: The
|
|
245
|
-
instance_Identifier:
|
|
334
|
+
logicalPathModule: The fully qualified module path containing the dataclass definition.
|
|
335
|
+
dataclass_Identifier: The name of the dataclass to decompose.
|
|
336
|
+
instance_Identifier: The variable name to use for the dataclass instance in generated code.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
A ShatteredDataclass containing AST representations of all dataclass components,
|
|
340
|
+
with imports, field definitions, annotations, and repackaging code.
|
|
341
|
+
|
|
342
|
+
Raises:
|
|
343
|
+
ValueError: If the dataclass cannot be found in the specified module or if no counting variable is identified in the dataclass.
|
|
246
344
|
"""
|
|
247
345
|
Official_fieldOrder: list[ast_Identifier] = []
|
|
248
346
|
dictionaryDeReConstruction: dict[ast_Identifier, DeReConstructField2ast] = {}
|
|
249
347
|
|
|
250
348
|
dataclassClassDef = extractClassDef(parseLogicalPath2astModule(logicalPathModule), dataclass_Identifier)
|
|
251
|
-
if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find {dataclass_Identifier=} in {logicalPathModule=}
|
|
349
|
+
if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find `{dataclass_Identifier = }` in `{logicalPathModule = }`.")
|
|
252
350
|
|
|
253
351
|
countingVariable = None
|
|
254
352
|
for aField in dataclasses.fields(importLogicalPath2Callable(logicalPathModule, dataclass_Identifier)): # pyright: ignore [reportArgumentType]
|
|
@@ -258,7 +356,7 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclas
|
|
|
258
356
|
countingVariable = dictionaryDeReConstruction[aField.name].name
|
|
259
357
|
|
|
260
358
|
if countingVariable is None:
|
|
261
|
-
raise ValueError(f"I could not find the counting variable in {dataclass_Identifier=} in {logicalPathModule=}
|
|
359
|
+
raise ValueError(f"I could not find the counting variable in `{dataclass_Identifier = }` in `{logicalPathModule = }`.")
|
|
262
360
|
|
|
263
361
|
shatteredDataclass = ShatteredDataclass(
|
|
264
362
|
countingVariableAnnotation=dictionaryDeReConstruction[countingVariable].astAnnotation,
|
|
@@ -282,6 +380,31 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclas
|
|
|
282
380
|
return shatteredDataclass
|
|
283
381
|
|
|
284
382
|
def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
|
|
383
|
+
"""
|
|
384
|
+
Convert an IngredientsModule to Python source code and write it to a file.
|
|
385
|
+
|
|
386
|
+
This function renders an IngredientsModule into executable Python code,
|
|
387
|
+
applies code quality improvements like import organization via autoflake,
|
|
388
|
+
and writes the result to the specified file path.
|
|
389
|
+
|
|
390
|
+
The function performs several key steps:
|
|
391
|
+
1. Converts the AST module structure to a valid Python AST
|
|
392
|
+
2. Fixes location attributes in the AST for proper formatting
|
|
393
|
+
3. Converts the AST to Python source code
|
|
394
|
+
4. Optimizes imports using autoflake
|
|
395
|
+
5. Writes the final source code to the specified file location
|
|
396
|
+
|
|
397
|
+
This is typically the final step in the code generation pipeline,
|
|
398
|
+
producing optimized Python modules ready for execution.
|
|
399
|
+
|
|
400
|
+
Parameters:
|
|
401
|
+
ingredients: The IngredientsModule containing the module definition.
|
|
402
|
+
pathFilename: The file path where the module should be written.
|
|
403
|
+
packageName: Optional package name to preserve in import optimization.
|
|
404
|
+
|
|
405
|
+
Raises:
|
|
406
|
+
raiseIfNoneGitHubIssueNumber3: If the generated source code is empty.
|
|
407
|
+
"""
|
|
285
408
|
astModule = Make.Module(ingredients.body, ingredients.type_ignores)
|
|
286
409
|
ast.fix_missing_locations(astModule)
|
|
287
410
|
pythonSource: str = ast.unparse(astModule)
|
|
@@ -289,20 +412,39 @@ def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any]
|
|
|
289
412
|
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleIdentifiers()
|
|
290
413
|
if packageName:
|
|
291
414
|
autoflake_additional_imports.append(packageName)
|
|
292
|
-
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=
|
|
415
|
+
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)
|
|
293
416
|
writeStringToHere(pythonSource, pathFilename)
|
|
294
417
|
|
|
295
418
|
# END of acceptable classes and functions ======================================================
|
|
296
419
|
dictionaryEstimates: dict[tuple[int, ...], int] = {
|
|
297
|
-
(2,2,2,2,2,2,2,2):
|
|
298
|
-
(2,21):
|
|
299
|
-
(3,15):
|
|
420
|
+
(2,2,2,2,2,2,2,2): 798148657152000,
|
|
421
|
+
(2,21): 776374224866624,
|
|
422
|
+
(3,15): 824761667826225,
|
|
300
423
|
(3,3,3,3): 85109616000000000000000000000000,
|
|
301
|
-
(8,8):
|
|
424
|
+
(8,8): 791274195985524900,
|
|
302
425
|
}
|
|
303
426
|
|
|
304
427
|
# END of marginal classes and functions ======================================================
|
|
305
|
-
def Z0Z_lameFindReplace(astTree
|
|
428
|
+
def Z0Z_lameFindReplace(astTree: 个, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]) -> 个:
|
|
429
|
+
"""
|
|
430
|
+
Recursively replace AST nodes based on a mapping of find-replace pairs.
|
|
431
|
+
|
|
432
|
+
This function applies brute-force node replacement throughout an AST tree
|
|
433
|
+
by comparing textual representations of nodes. While not the most efficient
|
|
434
|
+
approach, it provides a reliable way to replace complex nested structures
|
|
435
|
+
when more precise targeting methods are difficult to implement.
|
|
436
|
+
|
|
437
|
+
The function continues replacing nodes until no more changes are detected
|
|
438
|
+
in the AST's textual representation, ensuring complete replacement throughout
|
|
439
|
+
the tree structure.
|
|
440
|
+
|
|
441
|
+
Parameters:
|
|
442
|
+
astTree: The AST structure to modify.
|
|
443
|
+
mappingFindReplaceNodes: A mapping from source nodes to replacement nodes.
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
The modified AST structure with all matching nodes replaced.
|
|
447
|
+
"""
|
|
306
448
|
keepGoing = True
|
|
307
449
|
newTree = deepcopy(astTree)
|
|
308
450
|
|
|
@@ -315,55 +457,3 @@ def Z0Z_lameFindReplace(astTree, mappingFindReplaceNodes: Mapping[ast.AST, ast.A
|
|
|
315
457
|
else:
|
|
316
458
|
astTree = deepcopy(newTree)
|
|
317
459
|
return newTree
|
|
318
|
-
|
|
319
|
-
# Start of I HATE PROGRAMMING ==========================================================
|
|
320
|
-
def Z0Z_makeDictionaryReplacementStatements(module: ast.AST) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
|
|
321
|
-
"""Return a dictionary of function names and their replacement statements."""
|
|
322
|
-
dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
|
|
323
|
-
dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
|
|
324
|
-
for name, astFunctionDef in dictionaryFunctionDef.items():
|
|
325
|
-
if ifThis.onlyReturnAnyCompare(astFunctionDef):
|
|
326
|
-
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
|
|
327
|
-
elif ifThis.onlyReturnUnaryOp(astFunctionDef):
|
|
328
|
-
dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
|
|
329
|
-
else:
|
|
330
|
-
dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
|
|
331
|
-
return dictionaryReplacementStatements
|
|
332
|
-
|
|
333
|
-
def Z0Z_inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
|
|
334
|
-
class FunctionInliner(ast.NodeTransformer):
|
|
335
|
-
def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
|
|
336
|
-
self.dictionaryReplacementStatements = dictionaryReplacementStatements
|
|
337
|
-
|
|
338
|
-
def generic_visit(self, node: ast.AST) -> ast.AST:
|
|
339
|
-
"""Visit all nodes and replace them if necessary."""
|
|
340
|
-
return super().generic_visit(node)
|
|
341
|
-
|
|
342
|
-
def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
|
|
343
|
-
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
344
|
-
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
345
|
-
return node
|
|
346
|
-
|
|
347
|
-
def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
|
|
348
|
-
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
|
|
349
|
-
return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore
|
|
350
|
-
return node
|
|
351
|
-
|
|
352
|
-
def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
|
|
353
|
-
if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
|
|
354
|
-
replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore
|
|
355
|
-
if not isinstance(replacement, list):
|
|
356
|
-
return replacement
|
|
357
|
-
return node
|
|
358
|
-
|
|
359
|
-
keepGoing = True
|
|
360
|
-
ImaInlineFunction = deepcopy(astFunctionDef)
|
|
361
|
-
while keepGoing:
|
|
362
|
-
ImaInlineFunction = deepcopy(astFunctionDef)
|
|
363
|
-
FunctionInliner(deepcopy(dictionaryReplacementStatements)).visit(ImaInlineFunction)
|
|
364
|
-
if ast.unparse(ImaInlineFunction) == ast.unparse(astFunctionDef):
|
|
365
|
-
keepGoing = False
|
|
366
|
-
else:
|
|
367
|
-
astFunctionDef = deepcopy(ImaInlineFunction)
|
|
368
|
-
ast.fix_missing_locations(astFunctionDef)
|
|
369
|
-
return ImaInlineFunction
|
|
@@ -2,7 +2,6 @@ from concurrent.futures import Future as ConcurrentFuture, ProcessPoolExecutor
|
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from mapFolding.theSSOT import Array1DElephino, Array1DFoldsTotal, Array1DLeavesTotal, Array3D, ComputationState, DatatypeElephino, DatatypeFoldsTotal, DatatypeLeavesTotal
|
|
4
4
|
from numba import jit
|
|
5
|
-
from numpy import array, int16, int64
|
|
6
5
|
|
|
7
6
|
def countInitialize(state: ComputationState) -> ComputationState:
|
|
8
7
|
while state.leaf1ndex > 0:
|