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.
Files changed (33) hide show
  1. mapFolding/__init__.py +66 -18
  2. mapFolding/basecamp.py +32 -17
  3. mapFolding/beDRY.py +3 -3
  4. mapFolding/oeis.py +121 -25
  5. mapFolding/someAssemblyRequired/__init__.py +48 -27
  6. mapFolding/someAssemblyRequired/_theTypes.py +11 -15
  7. mapFolding/someAssemblyRequired/_tool_Make.py +40 -12
  8. mapFolding/someAssemblyRequired/_tool_Then.py +59 -25
  9. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +151 -276
  10. mapFolding/someAssemblyRequired/_toolboxContainers.py +185 -51
  11. mapFolding/someAssemblyRequired/_toolboxPython.py +165 -44
  12. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +141 -20
  13. mapFolding/someAssemblyRequired/toolboxNumba.py +93 -52
  14. mapFolding/someAssemblyRequired/transformationTools.py +228 -138
  15. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
  16. mapFolding/theSSOT.py +147 -55
  17. mapFolding/toolboxFilesystem.py +1 -1
  18. mapfolding-0.9.0.dist-info/METADATA +177 -0
  19. mapfolding-0.9.0.dist-info/RECORD +46 -0
  20. tests/__init__.py +44 -0
  21. tests/conftest.py +75 -7
  22. tests/test_computations.py +90 -9
  23. tests/test_filesystem.py +32 -33
  24. tests/test_other.py +0 -1
  25. tests/test_tasks.py +2 -2
  26. mapFolding/noHomeYet.py +0 -32
  27. mapFolding/someAssemblyRequired/newInliner.py +0 -22
  28. mapfolding-0.8.5.dist-info/METADATA +0 -190
  29. mapfolding-0.8.5.dist-info/RECORD +0 -48
  30. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/WHEEL +0 -0
  31. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/entry_points.txt +0 -0
  32. {mapfolding-0.8.5.dist-info → mapfolding-0.9.0.dist-info}/licenses/LICENSE +0 -0
  33. {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 transforming Python code through abstract syntax tree (AST) manipulation.
3
-
4
- This module provides a comprehensive set of utilities for programmatically analyzing,
5
- transforming, and generating Python code through AST manipulation. It implements
6
- a highly flexible framework that enables:
7
-
8
- 1. Precise identification of code patterns through composable predicates
9
- 2. Targeted modification of code structures while preserving semantics
10
- 3. Code generation with proper syntax and import management
11
- 4. Analysis of code dependencies and relationships
12
- 5. Clean transformation of one algorithmic implementation to another
13
-
14
- The utilities are organized into several key components:
15
- - Predicate factories (ifThis): Create composable functions for matching AST patterns
16
- - Node transformers: Modify AST structures in targeted ways
17
- - Code generation helpers (Make): Create well-formed AST nodes programmatically
18
- - Import tracking: Maintain proper imports during code transformation
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
- TypeCertified,
48
- 又,
41
+ 个,
49
42
  )
50
- from mapFolding.theSSOT import ComputationState, The, raiseIfNoneGitHubIssueNumber3
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
- return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
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
- return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
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
- def makeDictionaryFunctionDef(module: ast.AST) -> dict[ast_Identifier, ast.FunctionDef]:
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(be.FunctionDef, Then.updateKeyValueIn(DOT.name, Then.getIt, dictionaryIdentifier2FunctionDef)).visit(module)
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 makeDictionary4InliningFunction(identifierToInline: ast_Identifier, dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef], FunctionDefToInline: ast.FunctionDef | None = None) -> dict[str, ast.FunctionDef]:
121
+ def inlineFunctionDef(identifierToInline: ast_Identifier, module: ast.Module) -> ast.FunctionDef:
75
122
  """
76
- Creates a dictionary of function definitions required for inlining a target function.
77
- This function analyzes a target function and recursively collects all function definitions
78
- that are called within it (and any functions called by those functions), preparing them for inlining.
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
- identifierToInline : ast_Identifier
82
- The identifier of the function to be inlined.
83
- dictionaryFunctionDef : dict[ast_Identifier, ast.FunctionDef]
84
- A dictionary mapping function identifiers to their AST function definitions.
85
- FunctionDefToInline : ast.FunctionDef | None, optional
86
- The AST function definition to inline. If None, it will be retrieved from dictionaryFunctionDef using identifierToInline.
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
- dict[str, ast.FunctionDef]
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
- if FunctionDefToInline is None:
101
- try:
102
- FunctionDefToInline = dictionaryFunctionDef[identifierToInline]
103
- except KeyError as ERRORmessage:
104
- raise ValueError(f"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }") from ERRORmessage
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)))) # pyright: ignore[reportArgumentType]
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
- dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
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 in dictionaryFunctionDef:
128
- dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
129
-
130
- return dictionary4Inlining
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: ImaAnnotationType = dataclasses.field(init=False)
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)), 又.annotation(Then.getIt)).captureLastMatch(dataclassClassDef)
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.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', Make.Name(dtype.__name__))]))
224
- self.ledger.addImportFrom_asStr('numpy', constructor)
225
- self.ledger.addImportFrom_asStr('numpy', dtype.__name__)
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 be.Name(self.astAnnotation):
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 be.Subscript(self.astAnnotation):
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 be.Name(self.astAnnotation):
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: gimme string cuz python is stoopid
244
- dataclass_Identifier: The identifier of the dataclass to be dismantled.
245
- instance_Identifier: In the synthesized module/function/scope, the identifier that will be used for the instance.
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=False, remove_duplicate_keys = False, remove_unused_variables = False)
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): 362794844160000,
298
- (2,21): 1493028892051200,
299
- (3,15): 9842024675968800,
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): 129950723279272000,
424
+ (8,8): 791274195985524900,
302
425
  }
303
426
 
304
427
  # END of marginal classes and functions ======================================================
305
- def Z0Z_lameFindReplace(astTree, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]):
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: