mapFolding 0.7.0__py3-none-any.whl → 0.8.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.
Files changed (39) hide show
  1. mapFolding/__init__.py +1 -1
  2. mapFolding/basecamp.py +2 -2
  3. mapFolding/beDRY.py +88 -85
  4. mapFolding/filesystem.py +37 -29
  5. mapFolding/noHomeYet.py +2 -2
  6. mapFolding/oeis.py +2 -2
  7. mapFolding/someAssemblyRequired/Z0Z_workbench.py +347 -31
  8. mapFolding/someAssemblyRequired/__init__.py +4 -3
  9. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -1
  10. mapFolding/someAssemblyRequired/ingredientsNumba.py +87 -2
  11. mapFolding/someAssemblyRequired/synthesizeDataConverters.py +34 -52
  12. mapFolding/someAssemblyRequired/{synthesizeNumbaJob.py → synthesizeNumbaJobVESTIGIAL.py} +18 -21
  13. mapFolding/someAssemblyRequired/transformationTools.py +547 -209
  14. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +197 -12
  15. mapFolding/theDao.py +57 -39
  16. mapFolding/theSSOT.py +59 -59
  17. {mapfolding-0.7.0.dist-info → mapfolding-0.8.0.dist-info}/METADATA +6 -7
  18. mapfolding-0.8.0.dist-info/RECORD +41 -0
  19. {mapfolding-0.7.0.dist-info → mapfolding-0.8.0.dist-info}/WHEEL +1 -1
  20. tests/conftest.py +2 -3
  21. tests/test_computations.py +9 -5
  22. tests/test_filesystem.py +0 -2
  23. tests/test_other.py +2 -3
  24. tests/test_tasks.py +7 -5
  25. mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +0 -7
  26. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -91
  27. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -91
  28. mapFolding/someAssemblyRequired/whatWillBe.py +0 -311
  29. mapFolding/syntheticModules/__init__.py +0 -0
  30. mapFolding/syntheticModules/dataNamespaceFlattened.py +0 -30
  31. mapFolding/syntheticModules/numbaCount.py +0 -90
  32. mapFolding/syntheticModules/numbaCountSequential.py +0 -110
  33. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -12
  34. mapfolding-0.7.0.dist-info/RECORD +0 -50
  35. /mapFolding/syntheticModules/{numbaCountExample.py → numbaCountHistoricalExample.py} +0 -0
  36. /mapFolding/syntheticModules/{numba_doTheNeedfulExample.py → numba_doTheNeedfulHistoricalExample.py} +0 -0
  37. {mapfolding-0.7.0.dist-info → mapfolding-0.8.0.dist-info}/LICENSE +0 -0
  38. {mapfolding-0.7.0.dist-info → mapfolding-0.8.0.dist-info}/entry_points.txt +0 -0
  39. {mapfolding-0.7.0.dist-info → mapfolding-0.8.0.dist-info}/top_level.txt +0 -0
@@ -1,34 +1,350 @@
1
- from mapFolding.someAssemblyRequired.synthesizeDataConverters import makeStateJob
2
- from mapFolding.someAssemblyRequired.synthesizeDataConverters import makeDataclassConverter
3
- from mapFolding.someAssemblyRequired.whatWillBe import IngredientsFunction, IngredientsModule, numbaFlow
4
- from mapFolding.someAssemblyRequired.synthesizeCountingFunctions import Z0Z_makeCountingFunction
1
+ from autoflake import fix_code as autoflake_fix_code
2
+ from mapFolding.filesystem import writeStringToHere
3
+ from mapFolding.someAssemblyRequired import (
4
+ ast_Identifier,
5
+ extractFunctionDef,
6
+ ifThis,
7
+ IngredientsFunction,
8
+ IngredientsModule,
9
+ LedgerOfImports,
10
+ Make,
11
+ makeDictionaryReplacementStatements,
12
+ NodeCollector,
13
+ NodeReplacer,
14
+ RecipeSynthesizeFlow,
15
+ strDotStrCuzPyStoopid,
16
+ Then,
17
+ )
18
+ from mapFolding.someAssemblyRequired.ingredientsNumba import decorateCallableWithNumba
19
+ from mapFolding.someAssemblyRequired.synthesizeDataConverters import shatter_dataclassesDOTdataclass
20
+ from mapFolding.theSSOT import raiseIfNoneGitHubIssueNumber3
21
+ from pathlib import Path
5
22
  import ast
6
23
 
24
+ # Would `LibCST` be better than `ast` in some cases? https://github.com/hunterhogan/mapFolding/issues/7
25
+
26
+ def Z0Z_alphaTest_putModuleOnDisk(ingredients: IngredientsModule, recipeFlow: RecipeSynthesizeFlow):
27
+ # Physical namespace
28
+ filenameStem: str = recipeFlow.moduleDispatcher
29
+ fileExtension: str = recipeFlow.fileExtension
30
+ pathPackage: Path = Path(recipeFlow.pathPackage)
31
+
32
+ # Physical and logical namespace
33
+ packageName: ast_Identifier | None = recipeFlow.packageName # module name of the package, if any
34
+ logicalPathINFIX: ast_Identifier | strDotStrCuzPyStoopid | None = recipeFlow.Z0Z_flowLogicalPathRoot
35
+
36
+ def _getLogicalPathParent() -> str | None:
37
+ listModules: list[ast_Identifier] = []
38
+ if packageName:
39
+ listModules.append(packageName)
40
+ if logicalPathINFIX:
41
+ listModules.append(logicalPathINFIX)
42
+ if listModules:
43
+ return '.'.join(listModules)
44
+ return None
45
+
46
+ def _getLogicalPathAbsolute() -> str:
47
+ listModules: list[ast_Identifier] = []
48
+ logicalPathParent: str | None = _getLogicalPathParent()
49
+ if logicalPathParent:
50
+ listModules.append(logicalPathParent)
51
+ listModules.append(filenameStem)
52
+ return '.'.join(listModules)
53
+
54
+ def getPathFilename():
55
+ pathRoot: Path = pathPackage
56
+ filename: str = filenameStem + fileExtension
57
+ if logicalPathINFIX:
58
+ whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
59
+ pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
60
+ return pathRoot.joinpath(filename)
61
+
62
+ def absoluteImport() -> ast.Import:
63
+ return Make.astImport(_getLogicalPathAbsolute())
64
+
65
+ def absoluteImportFrom() -> ast.ImportFrom:
66
+ """ `from . import theModule` """
67
+ logicalPathParent: str = _getLogicalPathParent() or '.'
68
+ return Make.astImportFrom(logicalPathParent, [Make.astAlias(filenameStem)])
69
+
70
+ def writeModule() -> None:
71
+ astModule = ingredients.export()
72
+ ast.fix_missing_locations(astModule)
73
+ pythonSource: str = ast.unparse(astModule)
74
+ if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
75
+ autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleNames()
76
+ if packageName:
77
+ autoflake_additional_imports.append(packageName)
78
+ 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,)
79
+ pathFilename = getPathFilename()
80
+ writeStringToHere(pythonSource, pathFilename)
81
+
82
+ writeModule()
83
+
84
+ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
85
+ class FunctionInliner(ast.NodeTransformer):
86
+ def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
87
+ self.dictionaryReplacementStatements = dictionaryReplacementStatements
88
+
89
+ def generic_visit(self, node: ast.AST) -> ast.AST:
90
+ """Visit all nodes and replace them if necessary."""
91
+ return super().generic_visit(node)
92
+
93
+ def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
94
+ """Visit Expr nodes and replace value if it's a function call in our dictionary."""
95
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
96
+ return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
97
+ return node
98
+
99
+ def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
100
+ """Visit Assign nodes and replace value if it's a function call in our dictionary."""
101
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
102
+ return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
103
+ return node
104
+
105
+ def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
106
+ """Replace call nodes with their replacement statements if they're in the dictionary."""
107
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
108
+ replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore[attr-defined]
109
+ if not isinstance(replacement, list):
110
+ return replacement
111
+ return node
112
+
113
+ import copy
114
+ keepGoing = True
115
+ ImaInlineFunction = copy.deepcopy(astFunctionDef)
116
+ while keepGoing:
117
+ ImaInlineFunction = copy.deepcopy(astFunctionDef)
118
+ FunctionInliner(copy.deepcopy(dictionaryReplacementStatements)).visit(ImaInlineFunction)
119
+ if ast.unparse(ImaInlineFunction) == ast.unparse(astFunctionDef):
120
+ keepGoing = False
121
+ else:
122
+ astFunctionDef = copy.deepcopy(ImaInlineFunction)
123
+ return ImaInlineFunction
124
+
125
+ def replaceMatchingASTnodes(astTree: ast.AST, replacementMap: list[tuple[ast.AST, ast.AST]]) -> ast.AST:
126
+ """Replace matching AST nodes using type-specific visitors.
127
+
128
+ Parameters:
129
+ astTree: The AST to transform
130
+ replacementMap: List of (find, replace) node pairs
131
+
132
+ Returns:
133
+ The transformed AST
134
+ """
135
+ class TargetedNodeReplacer(ast.NodeTransformer):
136
+ def __init__(self, replacementMap: list[tuple[ast.AST, ast.AST]]) -> None:
137
+ # Group replacements by node type for more efficient lookups
138
+ self.replacementByType: dict[type[ast.AST], list[tuple[ast.AST, ast.AST]]] = {}
139
+ for findNode, replaceNode in replacementMap:
140
+ nodeType = type(findNode)
141
+ if nodeType not in self.replacementByType:
142
+ self.replacementByType[nodeType] = []
143
+ self.replacementByType[nodeType].append((findNode, replaceNode))
144
+
145
+ def visit(self, node: ast.AST) -> ast.AST:
146
+ """Check if this node should be replaced before continuing traversal."""
147
+ nodeType = type(node)
148
+ if nodeType in self.replacementByType:
149
+ for findNode, replaceNode in self.replacementByType[nodeType]:
150
+ if self.nodesMatchStructurally(node, findNode):
151
+ return replaceNode
152
+ return super().visit(node)
153
+
154
+ def nodesMatchStructurally(self, node1: ast.AST | list, node2: ast.AST | list) -> bool:
155
+ """Compare two AST nodes structurally, ignoring position information."""
156
+ # Different types can't be equal
157
+ if type(node1) != type(node2):
158
+ return False
159
+
160
+ if isinstance(node1, ast.AST):
161
+ # Compare fields that matter for structural equality
162
+ fields = [f for f in node1._fields
163
+ if f not in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'ctx')]
164
+
165
+ for field in fields:
166
+ smurf1 = getattr(node1, field, None)
167
+ smurf2 = getattr(node2, field, None)
168
+
169
+ if isinstance(smurf1, (ast.AST, list)) and isinstance(smurf2, (ast.AST, list)):
170
+ if not self.nodesMatchStructurally(smurf1, smurf2):
171
+ return False
172
+ elif smurf1 != smurf2:
173
+ return False
174
+ return True
175
+
176
+ elif isinstance(node1, list) and isinstance(node2, list):
177
+ if len(node1) != len(node2):
178
+ return False
179
+ return all(self.nodesMatchStructurally(x, y) for x, y in zip(node1, node2))
180
+
181
+ else:
182
+ # Direct comparison for non-AST objects (strings, numbers, etc.)
183
+ return node1 == node2
184
+
185
+ import copy
186
+ keepGoing = True
187
+ astResult = copy.deepcopy(astTree)
188
+
189
+ while keepGoing:
190
+ astBeforeChange = copy.deepcopy(astResult)
191
+ TargetedNodeReplacer(copy.deepcopy(replacementMap)).visit(astResult)
192
+
193
+ # Check if we've reached a fixed point (no more changes)
194
+ if ast.unparse(astResult) == ast.unparse(astBeforeChange):
195
+ keepGoing = False
196
+
197
+ return astResult
198
+
199
+ def Z0Z_main() -> None:
200
+ numbaFlow: RecipeSynthesizeFlow = RecipeSynthesizeFlow()
201
+ dictionaryReplacementStatements = makeDictionaryReplacementStatements(numbaFlow.source_astModule)
202
+ # TODO remove hardcoding
203
+ theCountingIdentifierHARDCODED = 'groupsOfFolds'
204
+ theCountingIdentifier = theCountingIdentifierHARDCODED
205
+
206
+ # TODO remember that `sequentialCallable` and `sourceSequentialCallable` are two different values.
207
+ # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
208
+
209
+ # ===========================================================
210
+ sourcePython = numbaFlow.sourceDispatcherCallable
211
+ astFunctionDef = extractFunctionDef(sourcePython, numbaFlow.source_astModule)
212
+ if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
213
+ ingredientsDispatcher = IngredientsFunction(astFunctionDef, LedgerOfImports(numbaFlow.source_astModule))
214
+
215
+ # sourceParallelCallable
216
+ (astName_dataclassesDOTdataclass, ledgerDataclassANDFragments, listAnnAssign4DataclassUnpack,
217
+ astTuple4AssignTargetsToFragments, listNameDataclassFragments4Parameters, list_ast_argAnnotated4ArgumentsSpecification,
218
+ astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns, astAssignDataclassRepack, list_keyword4DataclassInitialization) = shatter_dataclassesDOTdataclass(
219
+ numbaFlow.logicalPathModuleDataclass, numbaFlow.sourceDataclassIdentifier, numbaFlow.sourceDataclassInstanceTaskDistribution)
220
+ ingredientsDispatcher.imports.update(ledgerDataclassANDFragments)
221
+
222
+ # TODO remove hardcoding
223
+ namespaceHARDCODED = 'concurrencyManager'
224
+ identifierHARDCODED = 'submit'
225
+ namespace = namespaceHARDCODED
226
+ identifier = identifierHARDCODED
227
+ NodeReplacer(
228
+ findThis = ifThis.isAssignAndValueIsCallNamespace_Identifier(namespace, identifier)
229
+ , doThat = Then.insertThisAbove(listAnnAssign4DataclassUnpack)
230
+ ).visit(ingredientsDispatcher.astFunctionDef)
231
+ NodeReplacer(
232
+ findThis = ifThis.isCallNamespace_Identifier(namespace, identifier)
233
+ , doThat = Then.replaceWith(Make.astCall(Make.astAttribute(Make.astName(namespace), identifier)
234
+ , listArguments=[Make.astName(numbaFlow.parallelCallable)] + listNameDataclassFragments4Parameters))
235
+ ).visit(ingredientsDispatcher.astFunctionDef)
236
+
237
+ CapturedAssign: list[ast.AST] = []
238
+ CapturedCall: list[ast.Call] = []
239
+ findThis = ifThis.isCall
240
+ doThat = [Then.appendTo(CapturedCall)]
241
+ capture = NodeCollector(findThis, doThat)
242
+
243
+ NodeCollector(
244
+ findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(numbaFlow.sourceDataclassInstance))
245
+ , doThat = [Then.appendTo(CapturedAssign)
246
+ , lambda node: capture.visit(node)]
247
+ ).visit(ingredientsDispatcher.astFunctionDef)
248
+
249
+ newAssign = CapturedAssign[0]
250
+ NodeReplacer(
251
+ findThis = lambda node: ifThis.isSubscript(node) and ifThis.isAttribute(node.value) and ifThis.isCall(node.value.value)
252
+ , doThat = Then.replaceWith(CapturedCall[0])
253
+ ).visit(newAssign)
254
+
255
+ NodeReplacer(
256
+ findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(numbaFlow.sourceDataclassInstance))
257
+ , doThat = Then.replaceWith(newAssign)
258
+ ).visit(ingredientsDispatcher.astFunctionDef)
259
+
260
+ # sourceSequentialCallable
261
+ (astName_dataclassesDOTdataclass, ledgerDataclassANDFragments, listAnnAssign4DataclassUnpack,
262
+ astTuple4AssignTargetsToFragments, listNameDataclassFragments4Parameters, list_ast_argAnnotated4ArgumentsSpecification,
263
+ astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns, astAssignDataclassRepack, list_keyword4DataclassInitialization) = shatter_dataclassesDOTdataclass(
264
+ numbaFlow.logicalPathModuleDataclass, numbaFlow.sourceDataclassIdentifier, numbaFlow.sourceDataclassInstance)
265
+ ingredientsDispatcher.imports.update(ledgerDataclassANDFragments)
266
+
267
+ NodeReplacer(
268
+ findThis = ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.sourceSequentialCallable)
269
+ , doThat = Then.insertThisAbove(listAnnAssign4DataclassUnpack)
270
+ ).visit(ingredientsDispatcher.astFunctionDef)
271
+ NodeReplacer(
272
+ findThis = ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.sourceSequentialCallable)
273
+ # findThis = ifThis.isReturn
274
+ , doThat = Then.insertThisBelow([astAssignDataclassRepack])
275
+ ).visit(ingredientsDispatcher.astFunctionDef)
276
+ # TODO reconsider: This calls a function, but I don't inspect the function for its parameters or return.
277
+ NodeReplacer(
278
+ findThis = ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.sourceSequentialCallable)
279
+ , doThat = Then.replaceWith(Make.astAssign(listTargets=[astTuple4AssignTargetsToFragments], value=Make.astCall(Make.astName(numbaFlow.sequentialCallable), listNameDataclassFragments4Parameters)))
280
+ ).visit(ingredientsDispatcher.astFunctionDef)
281
+
282
+ # ===========================================================
283
+ sourcePython = numbaFlow.sourceInitializeCallable
284
+ astFunctionDef = extractFunctionDef(sourcePython, numbaFlow.source_astModule)
285
+ if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
286
+ astFunctionDef = inlineThisFunctionWithTheseValues(astFunctionDef, dictionaryReplacementStatements)
287
+ ingredientsInitialize = IngredientsFunction(astFunctionDef, LedgerOfImports(numbaFlow.source_astModule))
288
+
289
+ # ===========================================================
290
+ sourcePython = numbaFlow.sourceParallelCallable
291
+ astFunctionDef = extractFunctionDef(sourcePython, numbaFlow.source_astModule)
292
+ if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
293
+ astFunctionDef = inlineThisFunctionWithTheseValues(astFunctionDef, dictionaryReplacementStatements)
294
+ ingredientsParallel = IngredientsFunction(astFunctionDef, LedgerOfImports(numbaFlow.source_astModule))
295
+ ingredientsParallel.astFunctionDef.name = numbaFlow.parallelCallable
296
+ ingredientsParallel.astFunctionDef.args = Make.astArgumentsSpecification(args=list_ast_argAnnotated4ArgumentsSpecification)
297
+ NodeReplacer(
298
+ findThis = ifThis.isReturn
299
+ , doThat = Then.replaceWith(Make.astReturn(astTuple4AssignTargetsToFragments))
300
+ ).visit(ingredientsParallel.astFunctionDef)
301
+ NodeReplacer(
302
+ findThis = ifThis.isReturn
303
+ # , doThat = Then.replaceWith(Make.astReturn(astTuple4AssignTargetsToFragments))
304
+ , doThat = Then.replaceWith(Make.astReturn(Make.astName(theCountingIdentifier)))
305
+ ).visit(ingredientsParallel.astFunctionDef)
306
+ theCountingIdentifierAnnotation = next(
307
+ ast_arg.annotation for ast_arg in list_ast_argAnnotated4ArgumentsSpecification if ast_arg.arg == theCountingIdentifier)
308
+ ingredientsParallel.astFunctionDef.returns = theCountingIdentifierAnnotation
309
+ # ingredientsParallel.astFunctionDef.returns = astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns
310
+ replacementMap = [(statement.value, statement.target) for statement in listAnnAssign4DataclassUnpack]
311
+ ingredientsParallel.astFunctionDef = replaceMatchingASTnodes(
312
+ ingredientsParallel.astFunctionDef, replacementMap) # type: ignore
313
+ # TODO a tool to automatically remove unused variables from the ArgumentsSpecification (return, and returns) _might_ be nice.
314
+ # But, I would need to update the calling function, too.
315
+ ingredientsParallel = decorateCallableWithNumba(ingredientsParallel) # parametersNumbaParallelDEFAULT
316
+
317
+ # ===========================================================
318
+ sourcePython = numbaFlow.sourceSequentialCallable
319
+ astFunctionDef = extractFunctionDef(sourcePython, numbaFlow.source_astModule)
320
+ if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
321
+ astFunctionDef = inlineThisFunctionWithTheseValues(astFunctionDef, dictionaryReplacementStatements)
322
+ ingredientsSequential = IngredientsFunction(astFunctionDef, LedgerOfImports(numbaFlow.source_astModule))
323
+ ingredientsSequential.astFunctionDef.name = numbaFlow.sequentialCallable
324
+ ingredientsSequential.astFunctionDef.args = Make.astArgumentsSpecification(args=list_ast_argAnnotated4ArgumentsSpecification)
325
+ NodeReplacer(
326
+ findThis = ifThis.isReturn
327
+ , doThat = Then.replaceWith(Make.astReturn(astTuple4AssignTargetsToFragments))
328
+ ).visit(ingredientsSequential.astFunctionDef)
329
+ NodeReplacer(
330
+ findThis = ifThis.isReturn
331
+ , doThat = Then.replaceWith(Make.astReturn(astTuple4AssignTargetsToFragments))
332
+ ).visit(ingredientsSequential.astFunctionDef)
333
+ ingredientsSequential.astFunctionDef.returns = astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns
334
+ replacementMap = [(statement.value, statement.target) for statement in listAnnAssign4DataclassUnpack]
335
+ ingredientsSequential.astFunctionDef = replaceMatchingASTnodes(
336
+ ingredientsSequential.astFunctionDef, replacementMap) # type: ignore
337
+ # TODO a tool to automatically remove unused variables from the ArgumentsSpecification (return, and returns) _might_ be nice.
338
+ # But, I would need to update the calling function, too.
339
+ ingredientsSequential = decorateCallableWithNumba(ingredientsSequential)
340
+
341
+ ingredientsModuleNumbaUnified = IngredientsModule(
342
+ ingredientsFunction=[ingredientsInitialize,
343
+ ingredientsParallel,
344
+ ingredientsSequential,
345
+ ingredientsDispatcher], imports=LedgerOfImports(numbaFlow.source_astModule))
346
+
347
+ Z0Z_alphaTest_putModuleOnDisk(ingredientsModuleNumbaUnified, numbaFlow)
348
+
7
349
  if __name__ == '__main__':
8
- ingredientsFunctionDataConverter = makeDataclassConverter(
9
- dataclassIdentifierAsStr=numbaFlow.dataclassIdentifierAsStr
10
- , logicalPathModuleDataclass=numbaFlow.logicalPathModuleDataclass
11
- , dataclassInstanceAsStr=numbaFlow.dataclassInstanceAsStr
12
-
13
- , dispatcherCallableAsStr=numbaFlow.dispatcherCallableAsStr
14
- , logicalPathModuleDispatcher=numbaFlow.logicalPathModuleDispatcher
15
- , dataConverterCallableAsStr=numbaFlow.dataConverterCallableAsStr
16
- )
17
-
18
- # initialize with theDao
19
- dataInitializationHack = "state=makeStateJob(state.mapShape,writeJob=False)"
20
- ingredientsFunctionDataConverter.FunctionDef.body.insert(0, ast.parse(dataInitializationHack).body[0])
21
- ingredientsFunctionDataConverter.imports.addImportFromStr('mapFolding.someAssemblyRequired', 'makeStateJob')
22
-
23
- ingredientsSequential = Z0Z_makeCountingFunction(numbaFlow.sequentialCallableAsStr
24
- , numbaFlow.sourceAlgorithm
25
- , inline=True
26
- , dataclass=False)
27
-
28
- ingredientsModuleDataConverter = IngredientsModule(
29
- name=numbaFlow.dataConverterModule,
30
- ingredientsFunction=ingredientsFunctionDataConverter,
31
- logicalPathINFIX=numbaFlow.moduleOfSyntheticModules,
32
- )
33
-
34
- ingredientsModuleDataConverter.writeModule()
350
+ Z0Z_main()
@@ -1,16 +1,17 @@
1
- from mapFolding.someAssemblyRequired.whatWillBe import (
1
+ from mapFolding.someAssemblyRequired.transformationTools import (
2
2
  ast_Identifier as ast_Identifier,
3
3
  extractClassDef as extractClassDef,
4
4
  extractFunctionDef as extractFunctionDef,
5
- executeActionUnlessDescendantMatches as executeActionUnlessDescendantMatches,
6
5
  ifThis as ifThis,
7
6
  IngredientsFunction as IngredientsFunction,
8
7
  IngredientsModule as IngredientsModule,
9
8
  LedgerOfImports as LedgerOfImports,
10
- listNumbaCallableDispatchees as listNumbaCallableDispatchees,
11
9
  Make as Make,
10
+ makeDictionaryReplacementStatements as makeDictionaryReplacementStatements,
12
11
  NodeCollector as NodeCollector,
13
12
  NodeReplacer as NodeReplacer,
13
+ RecipeSynthesizeFlow as RecipeSynthesizeFlow,
14
14
  strDotStrCuzPyStoopid as strDotStrCuzPyStoopid,
15
15
  Then as Then,
16
+ Z0Z_executeActionUnlessDescendantMatches as Z0Z_executeActionUnlessDescendantMatches,
16
17
  )
@@ -1,4 +1,3 @@
1
- import importlib
2
1
  from importlib.machinery import ModuleSpec
3
2
  from types import ModuleType
4
3
  import importlib.util
@@ -1,6 +1,8 @@
1
- from collections.abc import Callable
1
+ from collections.abc import Callable, Sequence
2
+ from mapFolding.someAssemblyRequired import ifThis, IngredientsFunction, Make
2
3
  from numba.core.compiler import CompilerBase as numbaCompilerBase
3
- from typing import Any, TYPE_CHECKING, Final
4
+ from typing import Any, TYPE_CHECKING, Final, cast
5
+ import ast
4
6
 
5
7
  try:
6
8
  from typing import NotRequired
@@ -98,3 +100,86 @@ parametersNumbaMinimum: Final[ParametersNumba] = {
98
100
  'nopython': False,
99
101
  'forceobj': True,
100
102
  'parallel': False, }
103
+
104
+ Z0Z_numbaDataTypeModule = 'numba'
105
+ Z0Z_decoratorCallable = 'jit'
106
+
107
+ def thisIsNumbaDotJit(Ima: ast.AST) -> bool:
108
+ return ifThis.isCallNamespace_Identifier(Z0Z_numbaDataTypeModule, Z0Z_decoratorCallable)(Ima)
109
+
110
+ def thisIsJit(Ima: ast.AST) -> bool:
111
+ return ifThis.isCall_Identifier(Z0Z_decoratorCallable)(Ima)
112
+
113
+ def thisIsAnyNumbaJitDecorator(Ima: ast.AST) -> bool:
114
+ return thisIsNumbaDotJit(Ima) or thisIsJit(Ima)
115
+
116
+ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, parametersNumba: ParametersNumba | None = None) -> IngredientsFunction:
117
+ def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
118
+ # TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
119
+ for decoratorItem in astCallable.decorator_list.copy():
120
+ import warnings
121
+ astCallable.decorator_list.remove(decoratorItem)
122
+ warnings.warn(f"Removed decorator {ast.unparse(decoratorItem)} from {astCallable.name}")
123
+ return astCallable
124
+
125
+ def makeSpecialSignatureForNumba(signatureElement: ast.arg) -> ast.Subscript | ast.Name | None:
126
+ if isinstance(signatureElement.annotation, ast.Subscript) and isinstance(signatureElement.annotation.slice, ast.Tuple):
127
+ annotationShape: ast.expr = signatureElement.annotation.slice.elts[0]
128
+ if isinstance(annotationShape, ast.Subscript) and isinstance(annotationShape.slice, ast.Tuple):
129
+ shapeAsListSlices: list[ast.Slice] = [ast.Slice() for _axis in range(len(annotationShape.slice.elts))]
130
+ shapeAsListSlices[-1] = ast.Slice(step=ast.Constant(value=1))
131
+ shapeAST: ast.Slice | ast.Tuple = ast.Tuple(elts=list(shapeAsListSlices), ctx=ast.Load())
132
+ else:
133
+ shapeAST = ast.Slice(step=ast.Constant(value=1))
134
+
135
+ annotationDtype: ast.expr = signatureElement.annotation.slice.elts[1]
136
+ if (isinstance(annotationDtype, ast.Subscript) and isinstance(annotationDtype.slice, ast.Attribute)):
137
+ datatypeAST = annotationDtype.slice.attr
138
+ else:
139
+ datatypeAST = None
140
+
141
+ ndarrayName = signatureElement.arg
142
+ Z0Z_hacky_dtype: str = ndarrayName
143
+ datatype_attr = datatypeAST or Z0Z_hacky_dtype
144
+ ingredientsFunction.imports.addImportFromStr(datatypeModuleDecorator, datatype_attr)
145
+ datatypeNumba = ast.Name(id=datatype_attr, ctx=ast.Load())
146
+
147
+ return ast.Subscript(value=datatypeNumba, slice=shapeAST, ctx=ast.Load())
148
+
149
+ elif isinstance(signatureElement.annotation, ast.Name):
150
+ return signatureElement.annotation
151
+ return None
152
+
153
+ datatypeModuleDecorator: str = Z0Z_numbaDataTypeModule
154
+ list_argsDecorator: Sequence[ast.expr] = []
155
+
156
+ list_arg4signature_or_function: list[ast.expr] = []
157
+ for parameter in ingredientsFunction.astFunctionDef.args.args:
158
+ # Efficient translation of Python scalar types to Numba types https://github.com/hunterhogan/mapFolding/issues/8
159
+ # For now, let Numba infer them.
160
+ continue
161
+ signatureElement: ast.Subscript | ast.Name | None = makeSpecialSignatureForNumba(parameter)
162
+ if signatureElement:
163
+ list_arg4signature_or_function.append(signatureElement)
164
+
165
+ if ingredientsFunction.astFunctionDef.returns and isinstance(ingredientsFunction.astFunctionDef.returns, ast.Name):
166
+ theReturn: ast.Name = ingredientsFunction.astFunctionDef.returns
167
+ list_argsDecorator = [cast(ast.expr, ast.Call(func=ast.Name(id=theReturn.id, ctx=ast.Load())
168
+ , args=list_arg4signature_or_function if list_arg4signature_or_function else [], keywords=[] ) )]
169
+ elif list_arg4signature_or_function:
170
+ list_argsDecorator = [cast(ast.expr, ast.Tuple(elts=list_arg4signature_or_function, ctx=ast.Load()))]
171
+
172
+ ingredientsFunction.astFunctionDef = Z0Z_UnhandledDecorators(ingredientsFunction.astFunctionDef)
173
+ if parametersNumba is None:
174
+ parametersNumba = parametersNumbaDEFAULT
175
+ listDecoratorKeywords: list[ast.keyword] = [Make.ast_keyword(parameterName, Make.astConstant(parameterValue)) for parameterName, parameterValue in parametersNumba.items()]
176
+
177
+ decoratorModule: str = Z0Z_numbaDataTypeModule
178
+ decoratorCallable: str = Z0Z_decoratorCallable
179
+ ingredientsFunction.imports.addImportFromStr(decoratorModule, decoratorCallable)
180
+ # Leave this line in so that global edits will change it.
181
+ astDecorator: ast.Call = Make.astCall(Make.astName(decoratorCallable), list_argsDecorator, listDecoratorKeywords)
182
+ astDecorator: ast.Call = Make.astCall(Make.astName(decoratorCallable), list_astKeywords=listDecoratorKeywords) # type: ignore[no-redef]
183
+
184
+ ingredientsFunction.astFunctionDef.decorator_list = [astDecorator]
185
+ return ingredientsFunction
@@ -1,29 +1,30 @@
1
1
  from collections.abc import Sequence
2
2
  from importlib import import_module
3
3
  from inspect import getsource as inspect_getsource
4
- from pathlib import Path
5
- from types import ModuleType
6
- from typing import Any, cast, overload, Literal
7
- import ast
8
- import pickle
9
- from mapFolding.beDRY import ComputationState, outfitCountFolds, validateListDimensions
4
+ from mapFolding.beDRY import outfitCountFolds, validateListDimensions
10
5
  from mapFolding.filesystem import getPathFilenameFoldsTotal
11
6
  from mapFolding.someAssemblyRequired import (
12
7
  ast_Identifier,
13
- executeActionUnlessDescendantMatches,
14
8
  extractClassDef,
15
9
  ifThis,
16
- IngredientsFunction,
17
10
  LedgerOfImports,
18
11
  Make,
19
12
  NodeCollector,
20
13
  strDotStrCuzPyStoopid,
21
14
  Then,
15
+ Z0Z_executeActionUnlessDescendantMatches,
22
16
  )
23
- from mapFolding.theSSOT import getSourceAlgorithm
17
+ from mapFolding.theSSOT import ComputationState, getSourceAlgorithm
18
+ from pathlib import Path
19
+ from types import ModuleType
20
+ from typing import Any, Literal, overload
21
+ import ast
22
+ import pickle
23
+
24
+ # Would `LibCST` be better than `ast` in some cases? https://github.com/hunterhogan/mapFolding/issues/7
24
25
 
25
26
  def shatter_dataclassesDOTdataclass(logicalPathModule: strDotStrCuzPyStoopid, dataclass_Identifier: ast_Identifier, instance_Identifier: ast_Identifier
26
- ) -> tuple[ast.Name, LedgerOfImports, list[ast.AnnAssign], list[ast.Name], list[ast.keyword], ast.Tuple]:
27
+ )-> tuple[ast.Name, LedgerOfImports, list[ast.AnnAssign], ast.Tuple, list[ast.Name], list[ast.arg], ast.Subscript, ast.Assign, list[ast.keyword]]:
27
28
  """
28
29
  Parameters:
29
30
  logicalPathModule: gimme string cuz python is stoopid
@@ -37,63 +38,44 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: strDotStrCuzPyStoopid, da
37
38
  if not isinstance(dataclass, ast.ClassDef):
38
39
  raise ValueError(f"I could not find {dataclass_Identifier=} in {logicalPathModule=}.")
39
40
 
40
- list_astAnnAssign: list[ast.AnnAssign] = []
41
- listKeywordForDataclassInitialization: list[ast.keyword] = []
42
- list_astNameDataclassFragments: list[ast.Name] = []
43
- ledgerDataclassAndFragments = LedgerOfImports()
41
+ ledgerDataclassANDFragments = LedgerOfImports()
42
+ list_ast_argAnnotated4ArgumentsSpecification: list[ast.arg] = []
43
+ list_keyword4DataclassInitialization: list[ast.keyword] = []
44
+ listAnnAssign4DataclassUnpack: list[ast.AnnAssign] = []
45
+ listAnnotations: list[ast.expr] = []
46
+ listNameDataclassFragments4Parameters: list[ast.Name] = []
44
47
 
45
48
  addToLedgerPredicate = ifThis.isAnnAssignAndAnnotationIsName
46
- addToLedgerAction = Then.Z0Z_ledger(logicalPathModule, ledgerDataclassAndFragments)
49
+ addToLedgerAction = Then.Z0Z_ledger(logicalPathModule, ledgerDataclassANDFragments)
47
50
  addToLedger = NodeCollector(addToLedgerPredicate, [addToLedgerAction])
48
51
 
49
52
  exclusionPredicate = ifThis.is_keyword_IdentifierEqualsConstantValue('init', False)
50
- appendKeywordAction = Then.Z0Z_appendKeywordMirroredTo(listKeywordForDataclassInitialization)
51
- filteredAppendKeywordAction = executeActionUnlessDescendantMatches(exclusionPredicate, appendKeywordAction)
53
+ appendKeywordAction = Then.Z0Z_appendKeywordMirroredTo(list_keyword4DataclassInitialization)
54
+ filteredAppendKeywordAction = Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate, appendKeywordAction) # type: ignore
52
55
 
53
56
  collector = NodeCollector(
54
57
  ifThis.isAnnAssignAndTargetIsName,
55
- [Then.Z0Z_appendAnnAssignOfNameDOTnameTo(instance_Identifier, list_astAnnAssign)
56
- , Then.append_targetTo(list_astNameDataclassFragments)
58
+ [Then.Z0Z_appendAnnAssignOf_nameDOTnameTo(instance_Identifier, listAnnAssign4DataclassUnpack)
59
+ , Then.append_targetTo(listNameDataclassFragments4Parameters) # type: ignore
57
60
  , lambda node: addToLedger.visit(node)
58
61
  , filteredAppendKeywordAction
62
+ , lambda node: list_ast_argAnnotated4ArgumentsSpecification.append(Make.ast_arg(node.target.id, node.annotation)) # type: ignore
63
+ , lambda node: listAnnotations.append(node.annotation) # type: ignore
59
64
  ]
60
65
  )
61
66
 
62
67
  collector.visit(dataclass)
63
68
 
64
- ledgerDataclassAndFragments.addImportFromStr(logicalPathModule, dataclass_Identifier)
65
-
66
- astNameDataclass = Make.astName(dataclass_Identifier)
67
- astTupleForAssignTargetsToFragments: ast.Tuple = Make.astTuple(list_astNameDataclassFragments, ast.Store())
68
- return astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, listKeywordForDataclassInitialization, astTupleForAssignTargetsToFragments
69
-
70
- def makeDataclassConverter(dataclassIdentifierAsStr: str,
71
- logicalPathModuleDataclass: str,
72
- dataclassInstanceAsStr: str,
73
- dispatcherCallableAsStr: str,
74
- logicalPathModuleDispatcher: str,
75
- dataConverterCallableAsStr: str,
76
- ) -> IngredientsFunction:
77
-
78
- astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, list_astKeywordDataclassFragments, astTupleForAssignTargetsToFragments = shatter_dataclassesDOTdataclass(logicalPathModuleDataclass, dataclassIdentifierAsStr, dataclassInstanceAsStr)
79
-
80
- ingredientsFunction = IngredientsFunction(
81
- FunctionDef = Make.astFunctionDef(name=dataConverterCallableAsStr
82
- , argumentsSpecification=Make.astArgumentsSpecification(args=[Make.astArg(dataclassInstanceAsStr, astNameDataclass)])
83
- , body = cast(list[ast.stmt], list_astAnnAssign)
84
- , returns = astNameDataclass
85
- )
86
- , imports = ledgerDataclassAndFragments
87
- )
88
-
89
- callToDispatcher = Make.astAssign(listTargets=[astTupleForAssignTargetsToFragments]
90
- , value=Make.astCall(Make.astName(dispatcherCallableAsStr), args=list_astNameDataclassFragments))
91
- ingredientsFunction.FunctionDef.body.append(callToDispatcher)
92
- ingredientsFunction.imports.addImportFromStr(logicalPathModuleDispatcher, dispatcherCallableAsStr)
69
+ astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns = Make.astSubscript(Make.astName('tuple'), Make.astTuple(listAnnotations))
93
70
 
94
- ingredientsFunction.FunctionDef.body.append(Make.astReturn(Make.astCall(astNameDataclass, list_astKeywords=list_astKeywordDataclassFragments)))
71
+ ledgerDataclassANDFragments.addImportFromStr(logicalPathModule, dataclass_Identifier)
95
72
 
96
- return ingredientsFunction
73
+ astName_dataclassesDOTdataclass = Make.astName(dataclass_Identifier)
74
+ astTuple4AssignTargetsToFragments: ast.Tuple = Make.astTuple(listNameDataclassFragments4Parameters, ast.Store())
75
+ astAssignDataclassRepack = Make.astAssign(listTargets=[Make.astName(instance_Identifier)], value=Make.astCall(astName_dataclassesDOTdataclass, list_astKeywords=list_keyword4DataclassInitialization))
76
+ return (astName_dataclassesDOTdataclass, ledgerDataclassANDFragments, listAnnAssign4DataclassUnpack,
77
+ astTuple4AssignTargetsToFragments, listNameDataclassFragments4Parameters, list_ast_argAnnotated4ArgumentsSpecification,
78
+ astSubscriptPrimitiveTupleAnnotations4FunctionDef_returns, astAssignDataclassRepack, list_keyword4DataclassInitialization)
97
79
 
98
80
  @overload
99
81
  def makeStateJob(listDimensions: Sequence[int], *, writeJob: Literal[True], **keywordArguments: Any) -> Path: ...
@@ -116,11 +98,11 @@ def makeStateJob(listDimensions: Sequence[int], *, writeJob: bool = True, **keyw
116
98
  the path to the saved state file if writeJob is True.
117
99
  """
118
100
  mapShape = validateListDimensions(listDimensions)
119
- stateUniversal = outfitCountFolds(mapShape, **keywordArguments)
101
+ stateUniversal: ComputationState = outfitCountFolds(mapShape, **keywordArguments)
120
102
 
121
103
  moduleSource: ModuleType = getSourceAlgorithm()
122
104
  # TODO `countInitialize` is hardcoded
123
- stateUniversal: ComputationState = moduleSource.countInitialize(stateUniversal)
105
+ stateUniversal = moduleSource.countInitialize(stateUniversal)
124
106
 
125
107
  if not writeJob:
126
108
  return stateUniversal