mapFolding 0.9.0__py3-none-any.whl → 0.9.2__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 (31) hide show
  1. mapFolding/__init__.py +49 -48
  2. mapFolding/basecamp.py +40 -35
  3. mapFolding/beDRY.py +75 -69
  4. mapFolding/oeis.py +74 -85
  5. mapFolding/reference/__init__.py +2 -2
  6. mapFolding/someAssemblyRequired/RecipeJob.py +103 -0
  7. mapFolding/someAssemblyRequired/__init__.py +31 -29
  8. mapFolding/someAssemblyRequired/_theTypes.py +9 -1
  9. mapFolding/someAssemblyRequired/_tool_Make.py +1 -2
  10. mapFolding/someAssemblyRequired/_tool_Then.py +16 -8
  11. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +111 -23
  12. mapFolding/someAssemblyRequired/_toolboxContainers.py +27 -28
  13. mapFolding/someAssemblyRequired/_toolboxPython.py +3 -0
  14. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +83 -51
  15. mapFolding/someAssemblyRequired/toolboxNumba.py +19 -220
  16. mapFolding/someAssemblyRequired/transformationTools.py +183 -12
  17. mapFolding/syntheticModules/{numbaCount_doTheNeedful.py → numbaCount.py} +13 -12
  18. mapFolding/theDao.py +37 -36
  19. mapFolding/theSSOT.py +29 -33
  20. mapFolding/toolboxFilesystem.py +29 -38
  21. {mapfolding-0.9.0.dist-info → mapfolding-0.9.2.dist-info}/METADATA +2 -1
  22. mapfolding-0.9.2.dist-info/RECORD +47 -0
  23. tests/test_computations.py +2 -1
  24. tests/test_other.py +0 -7
  25. mapfolding-0.9.0.dist-info/RECORD +0 -46
  26. /mapFolding/reference/{lunnanNumpy.py → lunnonNumpy.py} +0 -0
  27. /mapFolding/reference/{lunnanWhile.py → lunnonWhile.py} +0 -0
  28. {mapfolding-0.9.0.dist-info → mapfolding-0.9.2.dist-info}/WHEEL +0 -0
  29. {mapfolding-0.9.0.dist-info → mapfolding-0.9.2.dist-info}/entry_points.txt +0 -0
  30. {mapfolding-0.9.0.dist-info → mapfolding-0.9.2.dist-info}/licenses/LICENSE +0 -0
  31. {mapfolding-0.9.0.dist-info → mapfolding-0.9.2.dist-info}/top_level.txt +0 -0
@@ -16,13 +16,10 @@ performance improvements while preserving code semantics and correctness.
16
16
  """
17
17
 
18
18
  from collections.abc import Callable, Sequence
19
- from mapFolding.someAssemblyRequired import grab, IngredientsModule, LedgerOfImports, Make, NodeChanger, NodeTourist, RecipeSynthesizeFlow, Then, ast_Identifier, ifThis, parsePathFilename2astModule, str_nameDOTname, IngredientsFunction, ShatteredDataclass
20
- from mapFolding.someAssemblyRequired.transformationTools import inlineFunctionDef, Z0Z_lameFindReplace, astModuleToIngredientsFunction, shatter_dataclassesDOTdataclass, write_astModule
21
- from mapFolding.theSSOT import ComputationState, DatatypeFoldsTotal as TheDatatypeFoldsTotal, DatatypeElephino as TheDatatypeElephino, DatatypeLeavesTotal as TheDatatypeLeavesTotal
22
- from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, getPathRootJobDEFAULT
19
+ from mapFolding.someAssemblyRequired import ast_Identifier, be, IngredientsFunction, Make, NodeTourist, RecipeSynthesizeFlow, str_nameDOTname, Then
20
+ from mapFolding.someAssemblyRequired.transformationTools import makeNewFlow, write_astModule
23
21
  from numba.core.compiler import CompilerBase as numbaCompilerBase
24
- from pathlib import Path, PurePosixPath
25
- from typing import Any, cast, Final, TYPE_CHECKING, TypeAlias, TypeGuard
22
+ from typing import Any, cast, Final, TYPE_CHECKING, TypeGuard
26
23
  import ast
27
24
  import dataclasses
28
25
 
@@ -48,17 +45,17 @@ class ParametersNumba(TypedDict):
48
45
  debug: NotRequired[bool]
49
46
  error_model: str
50
47
  fastmath: bool
51
- forceinline: bool
48
+ forceinline: NotRequired[bool]
52
49
  forceobj: NotRequired[bool]
53
- inline: str
50
+ inline: NotRequired[str]
54
51
  locals: NotRequired[dict[str, Any]]
55
- looplift: bool
56
- no_cfunc_wrapper: bool
57
- no_cpython_wrapper: bool
52
+ looplift: NotRequired[bool]
53
+ no_cfunc_wrapper: NotRequired[bool]
54
+ no_cpython_wrapper: NotRequired[bool]
58
55
  no_rewrites: NotRequired[bool]
59
56
  nogil: NotRequired[bool]
60
- nopython: bool
61
- parallel: bool
57
+ nopython: NotRequired[bool]
58
+ parallel: NotRequired[bool]
62
59
  pipeline_class: NotRequired[type[numbaCompilerBase]]
63
60
  signature_or_function: NotRequired[Any | Callable[..., Any] | str | tuple[Any, ...]]
64
61
  target: NotRequired[str]
@@ -74,6 +71,7 @@ parametersNumbaSuperJit: Final[ParametersNumba] = { **parametersNumbaDefault, 'n
74
71
  parametersNumbaSuperJitParallel: Final[ParametersNumba] = { **parametersNumbaSuperJit, '_nrt': True, 'parallel': True, }
75
72
  """Speed, no helmet, concurrency, no talking to non-jitted functions."""
76
73
  parametersNumbaMinimum: Final[ParametersNumba] = { '_nrt': True, 'boundscheck': True, 'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': False, 'inline': 'always', 'looplift': False, 'no_cfunc_wrapper': False, 'no_cpython_wrapper': False, 'nopython': False, 'forceobj': True, 'parallel': False, }
74
+ parametersNumbaLight: Final[ParametersNumba] = {'cache': True, 'error_model': 'numpy', 'fastmath': True, 'forceinline': True}
77
75
 
78
76
  Z0Z_numbaDataTypeModule: str_nameDOTname = 'numba'
79
77
  Z0Z_decoratorCallable: ast_Identifier = 'jit'
@@ -121,10 +119,10 @@ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, paramete
121
119
  list_arg4signature_or_function: list[ast.expr] = []
122
120
  for parameter in ingredientsFunction.astFunctionDef.args.args:
123
121
  # For now, let Numba infer them.
122
+ signatureElement: ast.Subscript | ast.Name | None = makeSpecialSignatureForNumba(parameter)
123
+ if signatureElement:
124
+ list_arg4signature_or_function.append(signatureElement)
124
125
  continue
125
- # signatureElement: ast.Subscript | ast.Name | None = makeSpecialSignatureForNumba(parameter)
126
- # if signatureElement:
127
- # list_arg4signature_or_function.append(signatureElement)
128
126
 
129
127
  if ingredientsFunction.astFunctionDef.returns and isinstance(ingredientsFunction.astFunctionDef.returns, ast.Name):
130
128
  theReturn: ast.Name = ingredientsFunction.astFunctionDef.returns
@@ -153,111 +151,9 @@ def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, paramete
153
151
  class SpicesJobNumba:
154
152
  useNumbaProgressBar: bool = True
155
153
  numbaProgressBarIdentifier: ast_Identifier = 'ProgressBarGroupsOfFolds'
156
- parametersNumba = parametersNumbaDefault
154
+ parametersNumba: ParametersNumba = dataclasses.field(default_factory=ParametersNumba) # type: ignore
157
155
 
158
156
  # Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
159
- @dataclasses.dataclass
160
- class RecipeJob:
161
- state: ComputationState
162
- # TODO create function to calculate `foldsTotalEstimated`
163
- foldsTotalEstimated: int = 0
164
- shatteredDataclass: ShatteredDataclass = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType]
165
-
166
- # ========================================
167
- # Source
168
- source_astModule = parsePathFilename2astModule(theNumbaFlow.pathFilenameSequential)
169
- sourceCountCallable: ast_Identifier = theNumbaFlow.callableSequential
170
-
171
- sourceLogicalPathModuleDataclass: str_nameDOTname = theNumbaFlow.logicalPathModuleDataclass
172
- sourceDataclassIdentifier: ast_Identifier = theNumbaFlow.dataclassIdentifier
173
- sourceDataclassInstance: ast_Identifier = theNumbaFlow.dataclassInstance
174
-
175
- sourcePathPackage: PurePosixPath | None = theNumbaFlow.pathPackage
176
- sourcePackageIdentifier: ast_Identifier | None = theNumbaFlow.packageIdentifier
177
-
178
- # ========================================
179
- # Filesystem (names of physical objects)
180
- pathPackage: PurePosixPath | None = None
181
- pathModule: PurePosixPath | None = PurePosixPath(getPathRootJobDEFAULT())
182
- """ `pathModule` will override `pathPackage` and `logicalPathRoot`."""
183
- fileExtension: str = theNumbaFlow.fileExtension
184
- pathFilenameFoldsTotal: PurePosixPath = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType]
185
-
186
- # ========================================
187
- # Logical identifiers (as opposed to physical identifiers)
188
- packageIdentifier: ast_Identifier | None = None
189
- logicalPathRoot: str_nameDOTname | None = None
190
- """ `logicalPathRoot` likely corresponds to a physical filesystem directory."""
191
- moduleIdentifier: ast_Identifier = dataclasses.field(default=None, init=True) # pyright: ignore[reportAssignmentType]
192
- countCallable: ast_Identifier = sourceCountCallable
193
- dataclassIdentifier: ast_Identifier | None = sourceDataclassIdentifier
194
- dataclassInstance: ast_Identifier | None = sourceDataclassInstance
195
- logicalPathModuleDataclass: str_nameDOTname | None = sourceLogicalPathModuleDataclass
196
-
197
- # ========================================
198
- # Datatypes
199
- DatatypeFoldsTotal: TypeAlias = TheDatatypeFoldsTotal
200
- DatatypeElephino: TypeAlias = TheDatatypeElephino
201
- DatatypeLeavesTotal: TypeAlias = TheDatatypeLeavesTotal
202
-
203
- def _makePathFilename(self,
204
- pathRoot: PurePosixPath | None = None,
205
- logicalPathINFIX: str_nameDOTname | None = None,
206
- filenameStem: str | None = None,
207
- fileExtension: str | None = None,
208
- ) -> PurePosixPath:
209
- if pathRoot is None:
210
- pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
211
- if logicalPathINFIX:
212
- whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
213
- pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
214
- if filenameStem is None:
215
- filenameStem = self.moduleIdentifier
216
- if fileExtension is None:
217
- fileExtension = self.fileExtension
218
- filename: str = filenameStem + fileExtension
219
- return pathRoot.joinpath(filename)
220
-
221
- @property
222
- def pathFilenameModule(self) -> PurePosixPath:
223
- if self.pathModule is None:
224
- return self._makePathFilename()
225
- else:
226
- return self._makePathFilename(pathRoot=self.pathModule, logicalPathINFIX=None)
227
-
228
- def __post_init__(self):
229
- pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(self.state.mapShape))
230
-
231
- if self.moduleIdentifier is None: # pyright: ignore[reportUnnecessaryComparison]
232
- self.moduleIdentifier = pathFilenameFoldsTotal.stem
233
-
234
- if self.pathFilenameFoldsTotal is None: # pyright: ignore[reportUnnecessaryComparison]
235
- self.pathFilenameFoldsTotal = pathFilenameFoldsTotal
236
-
237
- if self.shatteredDataclass is None and self.logicalPathModuleDataclass and self.dataclassIdentifier and self.dataclassInstance: # pyright: ignore[reportUnnecessaryComparison]
238
- self.shatteredDataclass = shatter_dataclassesDOTdataclass(self.logicalPathModuleDataclass, self.dataclassIdentifier, self.dataclassInstance)
239
-
240
- # ========================================
241
- # Fields you probably don't need =================================
242
- # Dispatcher =================================
243
- sourceDispatcherCallable: ast_Identifier = theNumbaFlow.callableDispatcher
244
- dispatcherCallable: ast_Identifier = sourceDispatcherCallable
245
- # Parallel counting =================================
246
- sourceDataclassInstanceTaskDistribution: ast_Identifier = theNumbaFlow.dataclassInstanceTaskDistribution
247
- sourceConcurrencyManagerNamespace: ast_Identifier = theNumbaFlow.concurrencyManagerNamespace
248
- sourceConcurrencyManagerIdentifier: ast_Identifier = theNumbaFlow.concurrencyManagerIdentifier
249
- dataclassInstanceTaskDistribution: ast_Identifier = sourceDataclassInstanceTaskDistribution
250
- concurrencyManagerNamespace: ast_Identifier = sourceConcurrencyManagerNamespace
251
- concurrencyManagerIdentifier: ast_Identifier = sourceConcurrencyManagerIdentifier
252
-
253
- class be:
254
- @staticmethod
255
- def Call(node: ast.AST) -> TypeGuard[ast.Call]:
256
- return isinstance(node, ast.Call)
257
- @staticmethod
258
- def Return(node: ast.AST) -> TypeGuard[ast.Return]:
259
- return isinstance(node, ast.Return)
260
-
261
157
  def makeNumbaFlow(numbaFlow: RecipeSynthesizeFlow) -> None:
262
158
  """
263
159
  Transform standard Python algorithm code into optimized Numba implementations.
@@ -283,109 +179,12 @@ def makeNumbaFlow(numbaFlow: RecipeSynthesizeFlow) -> None:
283
179
  transformation process, including source and target locations,
284
180
  function and variable names, and output paths.
285
181
  """
286
- # TODO a tool to automatically remove unused variables from the ArgumentsSpecification (return, and returns) _might_ be nice.
287
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
288
-
289
- listAllIngredientsFunctions = [
290
- (ingredientsInitialize := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableInitialize)),
291
- (ingredientsParallel := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableParallel)),
292
- (ingredientsSequential := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableSequential)),
293
- (ingredientsDispatcher := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableDispatcher)),
294
- ]
295
-
296
- # Inline functions ========================================================
297
- # NOTE Replacements statements are based on the identifiers in the _source_, so operate on the source identifiers.
298
- ingredientsInitialize.astFunctionDef = inlineFunctionDef(numbaFlow.sourceCallableInitialize, numbaFlow.source_astModule)
299
- ingredientsParallel.astFunctionDef = inlineFunctionDef(numbaFlow.sourceCallableParallel, numbaFlow.source_astModule)
300
- ingredientsSequential.astFunctionDef = inlineFunctionDef(numbaFlow.sourceCallableSequential, numbaFlow.source_astModule)
301
-
302
- # assignRecipeIdentifiersToCallable. =============================
303
- # Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
304
- # How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
305
- # NOTE reminder: you are updating these `ast.Name` here (and not in a more general search) because this is a
306
- # narrow search for `ast.Call` so you won't accidentally replace unrelated `ast.Name`.
307
- listFindReplace = [(numbaFlow.sourceCallableDispatcher, numbaFlow.callableDispatcher),
308
- (numbaFlow.sourceCallableInitialize, numbaFlow.callableInitialize),
309
- (numbaFlow.sourceCallableParallel, numbaFlow.callableParallel),
310
- (numbaFlow.sourceCallableSequential, numbaFlow.callableSequential),]
311
- for ingredients in listAllIngredientsFunctions:
312
- for source_Identifier, recipe_Identifier in listFindReplace:
313
- updateCallName = NodeChanger(ifThis.isCall_Identifier(source_Identifier), grab.funcAttribute(Then.replaceWith(Make.Name(recipe_Identifier))))
314
- updateCallName.visit(ingredients.astFunctionDef)
315
-
316
- ingredientsDispatcher.astFunctionDef.name = numbaFlow.callableDispatcher
317
- ingredientsInitialize.astFunctionDef.name = numbaFlow.callableInitialize
318
- ingredientsParallel.astFunctionDef.name = numbaFlow.callableParallel
319
- ingredientsSequential.astFunctionDef.name = numbaFlow.callableSequential
320
-
321
- # Assign identifiers per the recipe. ==============================
322
- listFindReplace = [(numbaFlow.sourceDataclassInstance, numbaFlow.dataclassInstance),
323
- (numbaFlow.sourceDataclassInstanceTaskDistribution, numbaFlow.dataclassInstanceTaskDistribution),
324
- (numbaFlow.sourceConcurrencyManagerNamespace, numbaFlow.concurrencyManagerNamespace),]
325
- for ingredients in listAllIngredientsFunctions:
326
- for source_Identifier, recipe_Identifier in listFindReplace:
327
- updateName = NodeChanger(ifThis.isName_Identifier(source_Identifier) , grab.idAttribute(Then.replaceWith(recipe_Identifier)))
328
- update_arg = NodeChanger(ifThis.isArgument_Identifier(source_Identifier), grab.argAttribute(Then.replaceWith(recipe_Identifier)))
329
- updateName.visit(ingredients.astFunctionDef)
330
- update_arg.visit(ingredients.astFunctionDef)
331
-
332
- updateConcurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(numbaFlow.sourceConcurrencyManagerNamespace, numbaFlow.sourceConcurrencyManagerIdentifier)
333
- , grab.funcAttribute(Then.replaceWith(Make.Attribute(Make.Name(numbaFlow.concurrencyManagerNamespace), numbaFlow.concurrencyManagerIdentifier))))
334
- updateConcurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
335
-
336
- # shatter Dataclass =======================================================
337
- instance_Identifier = numbaFlow.dataclassInstance
338
- getTheOtherRecord_damn = numbaFlow.dataclassInstanceTaskDistribution
339
- shatteredDataclass = shatter_dataclassesDOTdataclass(numbaFlow.logicalPathModuleDataclass, numbaFlow.sourceDataclassIdentifier, instance_Identifier)
340
- ingredientsDispatcher.imports.update(shatteredDataclass.ledger)
341
-
342
- # How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
343
- # Change callable parameters and Call to the callable at the same time ====
344
- # sequentialCallable =========================================================
345
- ingredientsSequential.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
346
- astCallSequentialCallable = Make.Call(Make.Name(numbaFlow.callableSequential), shatteredDataclass.listName4Parameters)
347
- changeReturnSequentialCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.fragments4AssignmentOrParameters)))
348
- ingredientsSequential.astFunctionDef.returns = shatteredDataclass.signatureReturnAnnotation
349
- replaceAssignSequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(numbaFlow.callableSequential)), Then.replaceWith(Make.Assign(listTargets=[shatteredDataclass.fragments4AssignmentOrParameters], value=astCallSequentialCallable)))
350
-
351
- unpack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(numbaFlow.callableSequential)), Then.insertThisAbove(shatteredDataclass.listUnpack))
352
- repack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(numbaFlow.callableSequential)), Then.insertThisBelow([shatteredDataclass.repack]))
353
-
354
- changeReturnSequentialCallable.visit(ingredientsSequential.astFunctionDef)
355
- replaceAssignSequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
356
- unpack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
357
- repack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
358
-
359
- ingredientsSequential.astFunctionDef = Z0Z_lameFindReplace(ingredientsSequential.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
360
-
361
- # parallelCallable =========================================================
362
- ingredientsParallel.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
363
- replaceCall2concurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(numbaFlow.concurrencyManagerNamespace, numbaFlow.concurrencyManagerIdentifier), Then.replaceWith(Make.Call(Make.Attribute(Make.Name(numbaFlow.concurrencyManagerNamespace), numbaFlow.concurrencyManagerIdentifier), listArguments=[Make.Name(numbaFlow.callableParallel)] + shatteredDataclass.listName4Parameters)))
364
-
365
- # NOTE I am dissatisfied with this logic for many reasons, including that it requires separate NodeCollector and NodeReplacer instances.
366
- astCallConcurrencyResult: list[ast.Call] = []
367
- get_astCallConcurrencyResult = NodeTourist(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), getIt(astCallConcurrencyResult))
368
- get_astCallConcurrencyResult.visit(ingredientsDispatcher.astFunctionDef)
369
- replaceAssignParallelCallable = NodeChanger(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), grab.valueAttribute(Then.replaceWith(astCallConcurrencyResult[0])))
370
- replaceAssignParallelCallable.visit(ingredientsDispatcher.astFunctionDef)
371
- changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.countingVariableName)))
372
- ingredientsParallel.astFunctionDef.returns = shatteredDataclass.countingVariableAnnotation
373
-
374
- unpack4parallelCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCallAttributeNamespace_Identifier(numbaFlow.concurrencyManagerNamespace, numbaFlow.concurrencyManagerIdentifier)), Then.insertThisAbove(shatteredDataclass.listUnpack))
375
-
376
- unpack4parallelCallable.visit(ingredientsDispatcher.astFunctionDef)
377
- replaceCall2concurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
378
- changeReturnParallelCallable.visit(ingredientsParallel.astFunctionDef)
379
-
380
- ingredientsParallel.astFunctionDef = Z0Z_lameFindReplace(ingredientsParallel.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
381
182
 
382
- # numba decorators =========================================
383
- ingredientsParallel = decorateCallableWithNumba(ingredientsParallel)
384
- ingredientsSequential = decorateCallableWithNumba(ingredientsSequential)
183
+ ingredientsModuleNumbaUnified = makeNewFlow(numbaFlow)
385
184
 
386
- # Module-level transformations ===========================================================
387
- ingredientsModuleNumbaUnified = IngredientsModule(ingredientsFunction=listAllIngredientsFunctions, imports=LedgerOfImports(numbaFlow.source_astModule))
388
- ingredientsModuleNumbaUnified.removeImportFromModule('numpy')
185
+ # numba decorators =========================================
186
+ ingredientsModuleNumbaUnified.listIngredientsFunctions[1] = decorateCallableWithNumba(ingredientsModuleNumbaUnified.listIngredientsFunctions[1])
187
+ ingredientsModuleNumbaUnified.listIngredientsFunctions[2] = decorateCallableWithNumba(ingredientsModuleNumbaUnified.listIngredientsFunctions[2])
389
188
 
390
189
  write_astModule(ingredientsModuleNumbaUnified, numbaFlow.pathFilenameDispatcher, numbaFlow.packageIdentifier)
391
190
 
@@ -25,7 +25,9 @@ from mapFolding.beDRY import outfitCountFolds
25
25
  from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal, writeStringToHere
26
26
  from mapFolding.someAssemblyRequired import (
27
27
  ast_Identifier,
28
+ be,
28
29
  DOT,
30
+ grab,
29
31
  ifThis,
30
32
  importLogicalPath2Callable,
31
33
  IngredientsFunction,
@@ -35,6 +37,7 @@ from mapFolding.someAssemblyRequired import (
35
37
  NodeChanger,
36
38
  NodeTourist,
37
39
  parseLogicalPath2astModule,
40
+ RecipeSynthesizeFlow,
38
41
  ShatteredDataclass,
39
42
  str_nameDOTname,
40
43
  Then,
@@ -47,6 +50,7 @@ from typing import Any, Literal, overload
47
50
  import ast
48
51
  import dataclasses
49
52
  import pickle
53
+ import python_minifier
50
54
 
51
55
  def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
52
56
  """
@@ -97,7 +101,7 @@ def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.Funct
97
101
  identifier: The name of the function to find.
98
102
 
99
103
  Returns:
100
- The matching function definition AST node, or None if not found.
104
+ astFunctionDef: The matching function definition AST node, or None if not found.
101
105
  """
102
106
  return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
103
107
 
@@ -115,7 +119,7 @@ def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.Fu
115
119
  A dictionary mapping function identifiers to their AST function definition nodes.
116
120
  """
117
121
  dictionaryIdentifier2FunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
118
- NodeTourist(lambda node: isinstance(node, ast.FunctionDef), Then.updateKeyValueIn(DOT.name, Then.extractIt, dictionaryIdentifier2FunctionDef)).visit(module) # type: ignore
122
+ NodeTourist(be.FunctionDef, Then.updateKeyValueIn(DOT.name, Then.extractIt, dictionaryIdentifier2FunctionDef)).visit(module)
119
123
  return dictionaryIdentifier2FunctionDef
120
124
 
121
125
  def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) -> ast.FunctionDef:
@@ -144,7 +148,7 @@ def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) ->
144
148
  raise ValueError(f"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }") from ERRORmessage
145
149
 
146
150
  listIdentifiersCalledFunctions: list[ast_Identifier] = []
147
- findIdentifiersToInline = NodeTourist(ifThis.isCallToName, lambda node: Then.appendTo(listIdentifiersCalledFunctions)(DOT.id(DOT.func(node)))) # type: ignore
151
+ findIdentifiersToInline = NodeTourist(findThis = ifThis.isCallToName, doThat = grab.funcDOTidAttribute(Then.appendTo(listIdentifiersCalledFunctions)))
148
152
  findIdentifiersToInline.visit(FunctionDefToInline)
149
153
 
150
154
  dictionary4Inlining: dict[ast_Identifier, ast.FunctionDef] = {}
@@ -165,7 +169,8 @@ def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) ->
165
169
  if NodeTourist(ifThis.matchesMeButNotAnyDescendant(ifThis.isCall_Identifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
166
170
  FunctionDefTarget = dictionaryFunctionDef[identifier]
167
171
  if len(FunctionDefTarget.body) == 1:
168
- inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
172
+ replacement = NodeTourist(be.Return, Then.extractIt(DOT.value)).captureLastMatch(FunctionDefTarget)
173
+ inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(replacement))
169
174
  for astFunctionDef in dictionary4Inlining.values():
170
175
  inliner.visit(astFunctionDef)
171
176
  else:
@@ -175,7 +180,8 @@ def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) ->
175
180
 
176
181
  for identifier, FunctionDefTarget in dictionary4Inlining.items():
177
182
  if len(FunctionDefTarget.body) == 1:
178
- inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(FunctionDefTarget.body[0].value)) # type: ignore
183
+ replacement = NodeTourist(be.Return, Then.extractIt(DOT.value)).captureLastMatch(FunctionDefTarget)
184
+ inliner = NodeChanger(ifThis.isCall_Identifier(identifier), Then.replaceWith(replacement))
179
185
  inliner.visit(FunctionDefToInline)
180
186
  else:
181
187
  inliner = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(identifier)),Then.replaceWith(FunctionDefTarget.body[0:-1]))
@@ -263,8 +269,8 @@ class DeReConstructField2ast:
263
269
  ast_nameDOTname: ast.Attribute = dataclasses.field(init=False)
264
270
  astAnnotation: ast.expr = dataclasses.field(init=False)
265
271
  ast_argAnnotated: ast.arg = dataclasses.field(init=False)
266
- astAnnAssignConstructor: ast.AnnAssign = dataclasses.field(init=False)
267
- Z0Z_hack: tuple[ast.AnnAssign, str] = dataclasses.field(init=False)
272
+ astAnnAssignConstructor: ast.AnnAssign|ast.Assign = dataclasses.field(init=False)
273
+ Z0Z_hack: tuple[ast.AnnAssign|ast.Assign, str] = dataclasses.field(init=False)
268
274
 
269
275
  def __post_init__(self, dataclassesDOTdataclassLogicalPathModule: str_nameDOTname, dataclassClassDef: ast.ClassDef, dataclassesDOTdataclassInstance_Identifier: ast_Identifier, field: dataclasses.Field[Any]) -> None:
270
276
  self.compare = field.compare
@@ -282,23 +288,47 @@ class DeReConstructField2ast:
282
288
  self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
283
289
  self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstance_Identifier), self.name)
284
290
 
285
- sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), Then.extractIt(DOT.annotation)).captureLastMatch(dataclassClassDef) # type: ignore
291
+ sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), Then.extractIt(DOT.annotation)).captureLastMatch(dataclassClassDef)
286
292
  if sherpa is None: raise raiseIfNoneGitHubIssueNumber3
287
293
  else: self.astAnnotation = sherpa
288
294
 
289
295
  self.ast_argAnnotated = Make.arg(self.name, self.astAnnotation)
296
+ """
297
+ from ast import Module, Expr, Subscript, Name, Tuple, Load
298
+ Subscript(
299
+ value=Name(id='ndarray', ctx=Load()),
300
+ slice=Tuple(
301
+ elts=[
302
+ Subscript(
303
+ value=Name(id='tuple', ctx=Load()),
304
+ slice=Name(id='int', ctx=Load()),
305
+ ctx=Load()),
306
+ Subscript(
307
+ value=Name(id='dtype', ctx=Load()),
308
+ slice=Name(id='NumPyLeavesTotal', ctx=Load()),
309
+ ctx=Load())],
310
+ ctx=Load()),
311
+ ctx=Load()
312
+ )
290
313
 
314
+ """
291
315
  dtype = self.metadata.get('dtype', None)
292
316
  if dtype:
293
317
  moduleWithLogicalPath: str_nameDOTname = 'numpy'
294
- annotation = 'ndarray'
295
- self.ledger.addImportFrom_asStr(moduleWithLogicalPath, annotation)
318
+ annotationType = 'ndarray'
319
+ self.ledger.addImportFrom_asStr(moduleWithLogicalPath, annotationType)
320
+ self.ledger.addImportFrom_asStr(moduleWithLogicalPath, 'dtype')
321
+ axesSubscript = Make.Subscript(Make.Name('tuple'), Make.Name('uint8'))
322
+ dtype_asnameName: ast.Name = self.astAnnotation # type: ignore
323
+ if dtype_asnameName.id == 'Array3D':
324
+ axesSubscript = Make.Subscript(Make.Name('tuple'), Make.Tuple([Make.Name('uint8'), Make.Name('uint8'), Make.Name('uint8')]))
325
+ ast_expr = Make.Subscript(Make.Name(annotationType), Make.Tuple([axesSubscript, Make.Subscript(Make.Name('dtype'), dtype_asnameName)]))
296
326
  constructor = 'array'
297
327
  self.ledger.addImportFrom_asStr(moduleWithLogicalPath, constructor)
298
328
  dtypeIdentifier: ast_Identifier = dtype.__name__
299
- dtype_asnameName: ast.Name = self.astAnnotation # type: ignore
300
329
  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)]))
330
+ self.astAnnAssignConstructor = Make.AnnAssign(self.astName, ast_expr, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', dtype_asnameName)]))
331
+ self.astAnnAssignConstructor = Make.Assign([self.astName], Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', dtype_asnameName)]))
302
332
  self.Z0Z_hack = (self.astAnnAssignConstructor, 'array')
303
333
  elif isinstance(self.astAnnotation, ast.Name):
304
334
  self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(self.astAnnotation, [Make.Constant(-1)]))
@@ -413,9 +443,150 @@ def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any]
413
443
  if packageName:
414
444
  autoflake_additional_imports.append(packageName)
415
445
  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)
446
+ # pythonSource = python_minifier.minify(pythonSource, remove_annotations=False, hoist_literals=False)
416
447
  writeStringToHere(pythonSource, pathFilename)
417
448
 
418
449
  # END of acceptable classes and functions ======================================================
450
+ def removeUnusedParameters(ingredientsFunction: IngredientsFunction) -> IngredientsFunction:
451
+ list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
452
+ list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
453
+ listName: list[ast.Name] = []
454
+ fauxFunctionDef = deepcopy(ingredientsFunction.astFunctionDef)
455
+ NodeChanger(be.Return, Then.removeIt).visit(fauxFunctionDef)
456
+ NodeTourist(be.Name, Then.appendTo(listName)).visit(fauxFunctionDef)
457
+ list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
458
+ list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
459
+ for arg_Identifier in list_IdentifiersNotUsed:
460
+ remove_arg = NodeChanger(ifThis.is_arg_Identifier(arg_Identifier), Then.removeIt)
461
+ remove_arg.visit(ingredientsFunction.astFunctionDef)
462
+ return ingredientsFunction
463
+
464
+ def makeNewFlow(recipeFlow: RecipeSynthesizeFlow) -> IngredientsModule:
465
+ # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
466
+ listAllIngredientsFunctions = [
467
+ (ingredientsInitialize := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableInitialize)),
468
+ (ingredientsParallel := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableParallel)),
469
+ (ingredientsSequential := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableSequential)),
470
+ (ingredientsDispatcher := astModuleToIngredientsFunction(recipeFlow.source_astModule, recipeFlow.sourceCallableDispatcher)),
471
+ ]
472
+
473
+ # Inline functions ========================================================
474
+ # NOTE Replacements statements are based on the identifiers in the _source_, so operate on the source identifiers.
475
+ ingredientsInitialize.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableInitialize, recipeFlow.source_astModule)
476
+ ingredientsParallel.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableParallel, recipeFlow.source_astModule)
477
+ ingredientsSequential.astFunctionDef = inlineFunctionDef(recipeFlow.sourceCallableSequential, recipeFlow.source_astModule)
478
+
479
+ # assignRecipeIdentifiersToCallable. =============================
480
+ # Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
481
+ # How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
482
+ # NOTE reminder: you are updating these `ast.Name` here (and not in a more general search) because this is a
483
+ # narrow search for `ast.Call` so you won't accidentally replace unrelated `ast.Name`.
484
+ listFindReplace = [(recipeFlow.sourceCallableDispatcher, recipeFlow.callableDispatcher),
485
+ (recipeFlow.sourceCallableInitialize, recipeFlow.callableInitialize),
486
+ (recipeFlow.sourceCallableParallel, recipeFlow.callableParallel),
487
+ (recipeFlow.sourceCallableSequential, recipeFlow.callableSequential),]
488
+ for ingredients in listAllIngredientsFunctions:
489
+ for source_Identifier, recipe_Identifier in listFindReplace:
490
+ updateCallName = NodeChanger(ifThis.isCall_Identifier(source_Identifier), grab.funcAttribute(Then.replaceWith(Make.Name(recipe_Identifier))))
491
+ updateCallName.visit(ingredients.astFunctionDef)
492
+
493
+ ingredientsDispatcher.astFunctionDef.name = recipeFlow.callableDispatcher
494
+ ingredientsInitialize.astFunctionDef.name = recipeFlow.callableInitialize
495
+ ingredientsParallel.astFunctionDef.name = recipeFlow.callableParallel
496
+ ingredientsSequential.astFunctionDef.name = recipeFlow.callableSequential
497
+
498
+ # Assign identifiers per the recipe. ==============================
499
+ listFindReplace = [(recipeFlow.sourceDataclassInstance, recipeFlow.dataclassInstance),
500
+ (recipeFlow.sourceDataclassInstanceTaskDistribution, recipeFlow.dataclassInstanceTaskDistribution),
501
+ (recipeFlow.sourceConcurrencyManagerNamespace, recipeFlow.concurrencyManagerNamespace),]
502
+ for ingredients in listAllIngredientsFunctions:
503
+ for source_Identifier, recipe_Identifier in listFindReplace:
504
+ updateName = NodeChanger(ifThis.isName_Identifier(source_Identifier) , grab.idAttribute(Then.replaceWith(recipe_Identifier)))
505
+ update_arg = NodeChanger(ifThis.isArgument_Identifier(source_Identifier), grab.argAttribute(Then.replaceWith(recipe_Identifier))) # type: ignore
506
+ updateName.visit(ingredients.astFunctionDef)
507
+ update_arg.visit(ingredients.astFunctionDef)
508
+
509
+ updateConcurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(recipeFlow.sourceConcurrencyManagerNamespace, recipeFlow.sourceConcurrencyManagerIdentifier)
510
+ , grab.funcAttribute(Then.replaceWith(Make.Attribute(Make.Name(recipeFlow.concurrencyManagerNamespace), recipeFlow.concurrencyManagerIdentifier))))
511
+ updateConcurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
512
+
513
+ # shatter Dataclass =======================================================
514
+ instance_Identifier = recipeFlow.dataclassInstance
515
+ getTheOtherRecord_damn = recipeFlow.dataclassInstanceTaskDistribution
516
+ shatteredDataclass = shatter_dataclassesDOTdataclass(recipeFlow.logicalPathModuleDataclass, recipeFlow.sourceDataclassIdentifier, instance_Identifier)
517
+ ingredientsDispatcher.imports.update(shatteredDataclass.ledger)
518
+
519
+ # How can I use dataclass settings as the SSOT for specific actions? https://github.com/hunterhogan/mapFolding/issues/16
520
+ # Change callable parameters and Call to the callable at the same time ====
521
+ # sequentialCallable =========================================================
522
+ if recipeFlow.removeDataclassSequential:
523
+ ingredientsSequential = removeDataclassFromFunction(ingredientsSequential, shatteredDataclass)
524
+ ingredientsDispatcher = unpackDataclassCallFunctionRepackDataclass(ingredientsDispatcher, recipeFlow.callableSequential, shatteredDataclass)
525
+
526
+ if recipeFlow.removeDataclassInitialize:
527
+ ingredientsInitialize = removeDataclassFromFunction(ingredientsInitialize, shatteredDataclass)
528
+ ingredientsDispatcher = unpackDataclassCallFunctionRepackDataclass(ingredientsDispatcher, recipeFlow.callableInitialize, shatteredDataclass)
529
+
530
+ # parallelCallable =========================================================
531
+ if recipeFlow.removeDataclassParallel:
532
+ ingredientsParallel.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
533
+
534
+ ingredientsParallel.astFunctionDef = Z0Z_lameFindReplace(ingredientsParallel.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
535
+
536
+ ingredientsParallel = removeUnusedParameters(ingredientsParallel)
537
+
538
+ list_argCuzMyBrainRefusesToThink = ingredientsParallel.astFunctionDef.args.args + ingredientsParallel.astFunctionDef.args.posonlyargs + ingredientsParallel.astFunctionDef.args.kwonlyargs
539
+ list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
540
+
541
+ listParameters = [parameter for parameter in shatteredDataclass.listName4Parameters if parameter.id in list_arg_arg]
542
+
543
+ 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)] + listParameters)))
544
+
545
+ # NOTE I am dissatisfied with this logic for many reasons, including that it requires separate NodeCollector and NodeReplacer instances.
546
+ astCallConcurrencyResult: list[ast.Call] = []
547
+ get_astCallConcurrencyResult = NodeTourist(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), getIt(astCallConcurrencyResult))
548
+ get_astCallConcurrencyResult.visit(ingredientsDispatcher.astFunctionDef)
549
+ replaceAssignParallelCallable = NodeChanger(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), grab.valueAttribute(Then.replaceWith(astCallConcurrencyResult[0])))
550
+ replaceAssignParallelCallable.visit(ingredientsDispatcher.astFunctionDef)
551
+ changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.countingVariableName)))
552
+ ingredientsParallel.astFunctionDef.returns = shatteredDataclass.countingVariableAnnotation
553
+
554
+ unpack4parallelCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCallAttributeNamespace_Identifier(recipeFlow.concurrencyManagerNamespace, recipeFlow.concurrencyManagerIdentifier)), Then.insertThisAbove(shatteredDataclass.listUnpack))
555
+
556
+ unpack4parallelCallable.visit(ingredientsDispatcher.astFunctionDef)
557
+ replaceCall2concurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
558
+ changeReturnParallelCallable.visit(ingredientsParallel.astFunctionDef)
559
+
560
+ # Module-level transformations ===========================================================
561
+ ingredientsModuleNumbaUnified = IngredientsModule(ingredientsFunction=listAllIngredientsFunctions, imports=LedgerOfImports(recipeFlow.source_astModule))
562
+ ingredientsModuleNumbaUnified.removeImportFromModule('numpy')
563
+
564
+ return ingredientsModuleNumbaUnified
565
+
566
+ def removeDataclassFromFunction(ingredientsTarget: IngredientsFunction, shatteredDataclass: ShatteredDataclass) -> IngredientsFunction:
567
+ ingredientsTarget.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
568
+ ingredientsTarget.astFunctionDef.returns = shatteredDataclass.signatureReturnAnnotation
569
+ changeReturnCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.fragments4AssignmentOrParameters)))
570
+ changeReturnCallable.visit(ingredientsTarget.astFunctionDef)
571
+ ingredientsTarget.astFunctionDef = Z0Z_lameFindReplace(ingredientsTarget.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
572
+ return ingredientsTarget
573
+
574
+ def unpackDataclassCallFunctionRepackDataclass(ingredientsCaller: IngredientsFunction, targetCallableIdentifier: ast_Identifier, shatteredDataclass: ShatteredDataclass) -> IngredientsFunction:
575
+ astCallTargetCallable = Make.Call(Make.Name(targetCallableIdentifier), shatteredDataclass.listName4Parameters)
576
+ replaceAssignTargetCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(targetCallableIdentifier)), Then.replaceWith(Make.Assign(listTargets=[shatteredDataclass.fragments4AssignmentOrParameters], value=astCallTargetCallable)))
577
+ unpack4targetCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(targetCallableIdentifier)), Then.insertThisAbove(shatteredDataclass.listUnpack))
578
+ repack4targetCallable = NodeChanger(ifThis.isAssignAndValueIs(ifThis.isCall_Identifier(targetCallableIdentifier)), Then.insertThisBelow([shatteredDataclass.repack]))
579
+ replaceAssignTargetCallable.visit(ingredientsCaller.astFunctionDef)
580
+ unpack4targetCallable.visit(ingredientsCaller.astFunctionDef)
581
+ repack4targetCallable.visit(ingredientsCaller.astFunctionDef)
582
+ return ingredientsCaller
583
+
584
+ def getIt(astCallConcurrencyResult: list[ast.Call]) -> Callable[[ast.AST], ast.AST]:
585
+ def workhorse(node: ast.AST) -> ast.AST:
586
+ NodeTourist(be.Call, Then.appendTo(astCallConcurrencyResult)).visit(node)
587
+ return node
588
+ return workhorse
589
+
419
590
  dictionaryEstimates: dict[tuple[int, ...], int] = {
420
591
  (2,2,2,2,2,2,2,2): 798148657152000,
421
592
  (2,21): 776374224866624,