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
@@ -2,17 +2,22 @@
2
2
  AST Node Predicate and Access Utilities for Pattern Matching and Traversal
3
3
 
4
4
  This module provides utilities for accessing and matching AST nodes in a consistent way.
5
- It contains two primary classes:
5
+ It contains three primary classes:
6
6
 
7
7
  1. DOT: Provides consistent accessor methods for AST node attributes across different
8
8
  node types, simplifying the access to node properties.
9
9
 
10
- 2. ifThis: Contains predicate functions for matching AST nodes based on various criteria,
10
+ 2. be: Offers type-guard functions that verify AST node types, enabling safe type
11
+ narrowing for static type checking and improving code safety.
12
+
13
+ 3. ifThis: Contains predicate functions for matching AST nodes based on various criteria,
11
14
  enabling precise targeting of nodes for analysis or transformation.
12
15
 
13
16
  These utilities form the foundation of the pattern-matching component in the AST
14
17
  manipulation framework, working in conjunction with the NodeChanger and NodeTourist
15
- classes to enable precise and targeted code transformations.
18
+ classes to enable precise and targeted code transformations. Together, they implement
19
+ a declarative approach to AST manipulation that separates node identification (ifThis),
20
+ type verification (be), and data access (DOT).
16
21
  """
17
22
 
18
23
  from collections.abc import Callable
@@ -26,6 +31,7 @@ from mapFolding.someAssemblyRequired import (
26
31
  astClassHasDOTvalue_expr,
27
32
  astClassOptionallyHasDOTnameNotName,
28
33
  astClassHasDOTvalue_exprNone,
34
+ ImaCallToName,
29
35
  )
30
36
  from typing import Any, overload, TypeGuard
31
37
  import ast
@@ -65,9 +71,17 @@ class DOT:
65
71
  @staticmethod
66
72
  def attr(node: ast.Attribute) -> ast_Identifier:
67
73
  return node.attr
74
+
75
+ @staticmethod
76
+ @overload
77
+ def func(node: ImaCallToName) -> ast.Name:...
78
+ @staticmethod
79
+ @overload
80
+ def func(node: ast.Call) -> ast.expr:...
68
81
  @staticmethod
69
- def func(node: ast.Call) -> ast.expr:
82
+ def func(node: ast.Call | ImaCallToName) -> ast.expr | ast.Name:
70
83
  return node.func
84
+
71
85
  @staticmethod
72
86
  def id(node: ast.Name) -> ast_Identifier:
73
87
  return node.id
@@ -111,6 +125,75 @@ class DOT:
111
125
  def value(node: astClassHasDOTvalue) -> Any | ast.expr | bool | None:
112
126
  return node.value
113
127
 
128
+ class be:
129
+ """
130
+ Provide type-guard functions for safely verifying AST node types during manipulation.
131
+
132
+ The be class contains static methods that perform runtime type verification of AST nodes,
133
+ returning TypeGuard results that enable static type checkers to narrow node types in
134
+ conditional branches. These type-guards:
135
+
136
+ 1. Improve code safety by preventing operations on incompatible node types
137
+ 2. Enable IDE tooling to provide better autocompletion and error detection
138
+ 3. Document expected node types in a way that's enforced by the type system
139
+ 4. Support pattern-matching workflows where node types must be verified before access
140
+
141
+ When used with conditional statements, these type-guards allow for precise,
142
+ type-safe manipulation of AST nodes while maintaining full static type checking
143
+ capabilities, even in complex transformation scenarios.
144
+ """
145
+ @staticmethod
146
+ def AnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
147
+ return isinstance(node, ast.AnnAssign)
148
+
149
+ @staticmethod
150
+ def arg(node: ast.AST) -> TypeGuard[ast.arg]:
151
+ return isinstance(node, ast.arg)
152
+
153
+ @staticmethod
154
+ def Assign(node: ast.AST) -> TypeGuard[ast.Assign]:
155
+ return isinstance(node, ast.Assign)
156
+
157
+ @staticmethod
158
+ def Attribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
159
+ return isinstance(node, ast.Attribute)
160
+
161
+ @staticmethod
162
+ def AugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
163
+ return isinstance(node, ast.AugAssign)
164
+
165
+ @staticmethod
166
+ def Call(node: ast.AST) -> TypeGuard[ast.Call]:
167
+ return isinstance(node, ast.Call)
168
+
169
+ @staticmethod
170
+ def ClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
171
+ return isinstance(node, ast.ClassDef)
172
+
173
+ @staticmethod
174
+ def FunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
175
+ return isinstance(node, ast.FunctionDef)
176
+
177
+ @staticmethod
178
+ def keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
179
+ return isinstance(node, ast.keyword)
180
+
181
+ @staticmethod
182
+ def Name(node: ast.AST) -> TypeGuard[ast.Name]:
183
+ return isinstance(node, ast.Name)
184
+
185
+ @staticmethod
186
+ def Return(node: ast.AST) -> TypeGuard[ast.Return]:
187
+ return isinstance(node, ast.Return)
188
+
189
+ @staticmethod
190
+ def Starred(node: ast.AST) -> TypeGuard[ast.Starred]:
191
+ return isinstance(node, ast.Starred)
192
+
193
+ @staticmethod
194
+ def Subscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
195
+ return isinstance(node, ast.Subscript)
196
+
114
197
  class ifThis:
115
198
  """
116
199
  Provide predicate functions for matching and filtering AST nodes based on various criteria.
@@ -135,41 +218,41 @@ class ifThis:
135
218
  @staticmethod
136
219
  def is_arg_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg] | bool]:
137
220
  """see also `isArgument_Identifier`"""
138
- return lambda node: isinstance(node, ast.arg) and ifThis._Identifier(identifier)(DOT.arg(node))
221
+ return lambda node: be.arg(node) and ifThis._Identifier(identifier)(DOT.arg(node))
139
222
  @staticmethod
140
223
  def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
141
224
  """see also `isArgument_Identifier`"""
142
- return lambda node: isinstance(node, ast.keyword) and ifThis._Identifier(identifier)(DOT.arg(node))
225
+ return lambda node: be.keyword(node) and ifThis._Identifier(identifier)(DOT.arg(node))
143
226
 
144
227
  @staticmethod
145
228
  def isAnnAssign_targetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
146
229
  def workhorse(node: ast.AST) -> TypeGuard[ast.AnnAssign] | bool:
147
- return isinstance(node, ast.AnnAssign) and targetPredicate(DOT.target(node))
230
+ return be.AnnAssign(node) and targetPredicate(DOT.target(node))
148
231
  return workhorse
149
232
 
150
233
  @staticmethod
151
234
  def isArgument_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg | ast.keyword] | bool]:
152
- return lambda node: (isinstance(node, ast.arg) or isinstance(node, ast.keyword)) and ifThis._Identifier(identifier)(DOT.arg(node))
235
+ return lambda node: (be.arg(node) or be.keyword(node)) and ifThis._Identifier(identifier)(DOT.arg(node))
153
236
 
154
237
  @staticmethod
155
238
  def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
156
239
  """node is Assign and node.targets[0] matches `targets0Predicate`."""
157
- return lambda node: isinstance(node, ast.Assign) and targets0Predicate(node.targets[0])
240
+ return lambda node: be.Assign(node) and targets0Predicate(node.targets[0])
158
241
  @staticmethod
159
242
  def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
160
243
  """node is ast.Assign and node.value matches `valuePredicate`. """
161
- return lambda node: isinstance(node, ast.Assign) and valuePredicate(DOT.value(node))
244
+ return lambda node: be.Assign(node) and valuePredicate(DOT.value(node))
162
245
 
163
246
  @staticmethod
164
247
  def isAttribute_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
165
248
  """node is `ast.Attribute` and the top-level `ast.Name` is `identifier`"""
166
249
  def workhorse(node: ast.AST) -> TypeGuard[ast.Attribute]:
167
- return isinstance(node, ast.Attribute) and ifThis._nested_Identifier(identifier)(DOT.value(node))
250
+ return be.Attribute(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
168
251
  return workhorse
169
252
  @staticmethod
170
253
  def isAttributeName(node: ast.AST) -> TypeGuard[ast.Attribute]:
171
254
  """ Displayed as Name.attribute."""
172
- return isinstance(node, ast.Attribute) and isinstance(DOT.value(node), ast.Name)
255
+ return be.Attribute(node) and be.Name(DOT.value(node))
173
256
  @staticmethod
174
257
  def isAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
175
258
  return lambda node: ifThis.isAttributeName(node) and ifThis.isName_Identifier(namespace)(DOT.value(node)) and ifThis._Identifier(identifier)(DOT.attr(node))
@@ -177,42 +260,47 @@ class ifThis:
177
260
  @staticmethod
178
261
  def isAugAssign_targetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AugAssign] | bool]:
179
262
  def workhorse(node: ast.AST) -> TypeGuard[ast.AugAssign] | bool:
180
- return isinstance(node, ast.AugAssign) and targetPredicate(DOT.target(node))
263
+ return be.AugAssign(node) and targetPredicate(DOT.target(node))
181
264
  return workhorse
182
265
 
183
266
  @staticmethod
184
- def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
185
- return lambda node: isinstance(node, ast.Call) and ifThis.isName_Identifier(identifier)(DOT.func(node))
267
+ def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ImaCallToName] | bool]:
268
+ def workhorse(node: ast.AST) -> TypeGuard[ImaCallToName] | bool:
269
+ return ifThis.isCallToName(node) and ifThis._Identifier(identifier)(DOT.id(DOT.func(node)))
270
+ return workhorse
271
+
186
272
  @staticmethod
187
273
  def isCallAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
188
- return lambda node: isinstance(node, ast.Call) and ifThis.isAttributeNamespace_Identifier(namespace, identifier)(DOT.func(node))
274
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Call] | bool:
275
+ return be.Call(node) and ifThis.isAttributeNamespace_Identifier(namespace, identifier)(DOT.func(node))
276
+ return workhorse
189
277
  @staticmethod
190
- def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
191
- return isinstance(node, ast.Call) and isinstance(DOT.func(node), ast.Name)
278
+ def isCallToName(node: ast.AST) -> TypeGuard[ImaCallToName]:
279
+ return be.Call(node) and be.Name(DOT.func(node))
192
280
 
193
281
  @staticmethod
194
282
  def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
195
- return lambda node: isinstance(node, ast.ClassDef) and ifThis._Identifier(identifier)(DOT.name(node))
283
+ return lambda node: be.ClassDef(node) and ifThis._Identifier(identifier)(DOT.name(node))
196
284
 
197
285
  @staticmethod
198
286
  def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef] | bool]:
199
- return lambda node: isinstance(node, ast.FunctionDef) and ifThis._Identifier(identifier)(DOT.name(node))
287
+ return lambda node: be.FunctionDef(node) and ifThis._Identifier(identifier)(DOT.name(node))
200
288
 
201
289
  @staticmethod
202
290
  def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name] | bool]:
203
- return lambda node: isinstance(node, ast.Name) and ifThis._Identifier(identifier)(DOT.id(node))
291
+ return lambda node: be.Name(node) and ifThis._Identifier(identifier)(DOT.id(node))
204
292
 
205
293
  @staticmethod
206
294
  def isStarred_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Starred] | bool]:
207
295
  """node is `ast.Starred` and the top-level `ast.Name` is `identifier`"""
208
296
  def workhorse(node: ast.AST) -> TypeGuard[ast.Starred]:
209
- return isinstance(node, ast.Starred) and ifThis._nested_Identifier(identifier)(DOT.value(node))
297
+ return be.Starred(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
210
298
  return workhorse
211
299
  @staticmethod
212
300
  def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
213
301
  """node is `ast.Subscript` and the top-level `ast.Name` is `identifier`"""
214
302
  def workhorse(node: ast.AST) -> TypeGuard[ast.Subscript]:
215
- return isinstance(node, ast.Subscript) and ifThis._nested_Identifier(identifier)(DOT.value(node))
303
+ return be.Subscript(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
216
304
  return workhorse
217
305
 
218
306
  @staticmethod
@@ -29,7 +29,6 @@ from Z0Z_tools import updateExtendPolishDictionaryLists
29
29
  import ast
30
30
  import dataclasses
31
31
 
32
- # Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
33
32
  class LedgerOfImports:
34
33
  """
35
34
  Track and manage import statements for programmatically generated code.
@@ -58,23 +57,22 @@ class LedgerOfImports:
58
57
  self.walkThis(startWith)
59
58
 
60
59
  def addAst(self, astImport____: ast.Import | ast.ImportFrom) -> None:
61
- assert isinstance(astImport____, (ast.Import, ast.ImportFrom)), f"I received {type(astImport____) = }, but I can only accept {ast.Import} and {ast.ImportFrom}."
62
- if isinstance(astImport____, ast.Import):
63
- for alias in astImport____.names:
64
- self.listImport.append(alias.name)
65
- elif isinstance(astImport____, ast.ImportFrom): # type: ignore
66
- # TODO fix the mess created by `None` means '.'. I need a `str_nameDOTname` to replace '.'
67
- if astImport____.module is None:
68
- astImport____.module = '.'
69
- for alias in astImport____.names:
70
- self.dictionaryImportFrom[astImport____.module].append((alias.name, alias.asname))
60
+ match astImport____:
61
+ case ast.Import():
62
+ for alias in astImport____.names:
63
+ self.listImport.append(alias.name)
64
+ case ast.ImportFrom():
65
+ # TODO fix the mess created by `None` means '.'. I need a `str_nameDOTname` to replace '.'
66
+ if astImport____.module is None:
67
+ astImport____.module = '.'
68
+ for alias in astImport____.names:
69
+ self.dictionaryImportFrom[astImport____.module].append((alias.name, alias.asname))
70
+ case _:
71
+ raise ValueError(f"I received {type(astImport____) = }, but I can only accept {ast.Import} and {ast.ImportFrom}.")
71
72
 
72
73
  def addImport_asStr(self, moduleWithLogicalPath: str_nameDOTname) -> None:
73
74
  self.listImport.append(moduleWithLogicalPath)
74
75
 
75
- # def addImportFrom_asStr(self, moduleWithLogicalPath: str_nameDOTname, name: ast_Identifier, asname: ast_Identifier | None = None) -> None:
76
- # self.dictionaryImportFrom[moduleWithLogicalPath].append((name, asname))
77
-
78
76
  def addImportFrom_asStr(self, moduleWithLogicalPath: str_nameDOTname, name: ast_Identifier, asname: ast_Identifier | None = None) -> None:
79
77
  if moduleWithLogicalPath not in self.dictionaryImportFrom:
80
78
  self.dictionaryImportFrom[moduleWithLogicalPath] = []
@@ -85,15 +83,14 @@ class LedgerOfImports:
85
83
  self.removeImportFrom(moduleWithLogicalPath, None, None)
86
84
 
87
85
  def removeImportFrom(self, moduleWithLogicalPath: str_nameDOTname, name: ast_Identifier | None, asname: ast_Identifier | None = None) -> None:
88
- assert moduleWithLogicalPath is not None, SyntaxError(f"I received `{moduleWithLogicalPath = }`, but it must be the name of a module.")
86
+ """
87
+ name, asname Action
88
+ None, None : remove all matches for the module
89
+ ast_Identifier, ast_Identifier : remove exact matches
90
+ ast_Identifier, None : remove exact matches
91
+ None, ast_Identifier : remove all matches for asname and if entry_asname is None remove name == ast_Identifier
92
+ """
89
93
  if moduleWithLogicalPath in self.dictionaryImportFrom:
90
- """
91
- name, asname Meaning
92
- ast_Identifier, ast_Identifier : remove exact matches
93
- ast_Identifier, None : remove exact matches
94
- None, ast_Identifier : remove all matches for asname and if entry_asname is None remove name == ast_Identifier
95
- None, None : remove all matches for the module
96
- """
97
94
  if name is None and asname is None:
98
95
  # Remove all entries for the module
99
96
  self.dictionaryImportFrom.pop(moduleWithLogicalPath)
@@ -102,7 +99,6 @@ class LedgerOfImports:
102
99
  self.dictionaryImportFrom[moduleWithLogicalPath] = [(entry_name, entry_asname) for entry_name, entry_asname in self.dictionaryImportFrom[moduleWithLogicalPath]
103
100
  if not (entry_asname == asname) and not (entry_asname is None and entry_name == asname)]
104
101
  else:
105
- # Remove exact matches for the module
106
102
  self.dictionaryImportFrom[moduleWithLogicalPath] = [(entry_name, entry_asname) for entry_name, entry_asname in self.dictionaryImportFrom[moduleWithLogicalPath]
107
103
  if not (entry_name == name and entry_asname == asname)]
108
104
  if not self.dictionaryImportFrom[moduleWithLogicalPath]:
@@ -255,7 +251,6 @@ class IngredientsModule:
255
251
  def appendIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
256
252
  """Append one or more `IngredientsFunction`."""
257
253
  for allegedIngredientsFunction in ingredientsFunction:
258
- assert isinstance(allegedIngredientsFunction, IngredientsFunction), ValueError(f"I received `{type(allegedIngredientsFunction) = }`, but I can only accept `{IngredientsFunction}`.")
259
254
  self.listIngredientsFunctions.append(allegedIngredientsFunction)
260
255
 
261
256
  def removeImportFromModule(self, moduleWithLogicalPath: str_nameDOTname) -> None:
@@ -308,7 +303,7 @@ class IngredientsModule:
308
303
  @dataclasses.dataclass
309
304
  class RecipeSynthesizeFlow:
310
305
  """
311
- Configure the generation of optimized Numba-accelerated code modules.
306
+ Configure the generation of new modules, including Numba-accelerated code modules.
312
307
 
313
308
  RecipeSynthesizeFlow defines the complete blueprint for transforming an original
314
309
  Python algorithm into an optimized, accelerated implementation. It specifies:
@@ -358,7 +353,7 @@ class RecipeSynthesizeFlow:
358
353
  """ `logicalPathFlowRoot` likely corresponds to a physical filesystem directory."""
359
354
 
360
355
  # Module ================================
361
- moduleDispatcher: ast_Identifier = 'numbaCount_doTheNeedful'
356
+ moduleDispatcher: ast_Identifier = 'numbaCount'
362
357
  moduleInitialize: ast_Identifier = moduleDispatcher
363
358
  moduleParallel: ast_Identifier = moduleDispatcher
364
359
  moduleSequential: ast_Identifier = moduleDispatcher
@@ -376,6 +371,10 @@ class RecipeSynthesizeFlow:
376
371
  dataclassInstance: ast_Identifier = sourceDataclassInstance
377
372
  dataclassInstanceTaskDistribution: ast_Identifier = sourceDataclassInstanceTaskDistribution
378
373
 
374
+ removeDataclassDispatcher: bool = False
375
+ removeDataclassInitialize: bool = False
376
+ removeDataclassParallel: bool = True
377
+ removeDataclassSequential: bool = True
379
378
  # ========================================
380
379
  # Computed
381
380
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
@@ -432,10 +431,10 @@ class ShatteredDataclass:
432
431
  countingVariableName: ast.Name
433
432
  """AST name node representing the counting variable identifier."""
434
433
 
435
- field2AnnAssign: dict[ast_Identifier, ast.AnnAssign] = dataclasses.field(default_factory=dict)
434
+ field2AnnAssign: dict[ast_Identifier, ast.AnnAssign | ast.Assign] = dataclasses.field(default_factory=dict)
436
435
  """Maps field names to their corresponding AST call expressions."""
437
436
 
438
- Z0Z_field2AnnAssign: dict[ast_Identifier, tuple[ast.AnnAssign, str]] = dataclasses.field(default_factory=dict)
437
+ Z0Z_field2AnnAssign: dict[ast_Identifier, tuple[ast.AnnAssign | ast.Assign, str]] = dataclasses.field(default_factory=dict)
439
438
 
440
439
  fragments4AssignmentOrParameters: ast.Tuple = dummyTuple
441
440
  """AST tuple used as target for assignment to capture returned fragments."""
@@ -107,6 +107,7 @@ def importPathFilename2Callable(pathFilename: PathLike[Any] | PurePath, identifi
107
107
  Load a callable (function, class, etc.) from a Python file.
108
108
  This function imports a specified Python file as a module, extracts a callable object
109
109
  from it by name, and returns that callable.
110
+
110
111
  Parameters
111
112
  ----------
112
113
  pathFilename : Union[PathLike[Any], PurePath]
@@ -115,10 +116,12 @@ def importPathFilename2Callable(pathFilename: PathLike[Any] | PurePath, identifi
115
116
  Name of the callable to extract from the imported module.
116
117
  moduleIdentifier : Optional[str]
117
118
  Name to use for the imported module. If None, the filename stem is used.
119
+
118
120
  Returns
119
121
  -------
120
122
  Callable[..., Any]
121
123
  The callable object extracted from the imported module.
124
+
122
125
  Raises
123
126
  ------
124
127
  ImportError
@@ -18,16 +18,26 @@ This creates extremely fast, specialized implementations that can be run directl
18
18
  as Python scripts or further compiled into standalone executables.
19
19
  """
20
20
 
21
- from mapFolding.toolboxFilesystem import getPathFilenameFoldsTotal
22
- from mapFolding.someAssemblyRequired import ast_Identifier, ifThis, Make, NodeChanger, Then, IngredientsFunction, IngredientsModule, LedgerOfImports
23
- from mapFolding.someAssemblyRequired.toolboxNumba import RecipeJob, SpicesJobNumba, decorateCallableWithNumba
24
- from mapFolding.someAssemblyRequired.transformationTools import extractFunctionDef, write_astModule
25
- from mapFolding.someAssemblyRequired.transformationTools import makeInitializedComputationState
26
- from mapFolding.theSSOT import The, raiseIfNoneGitHubIssueNumber3
27
- from mapFolding.oeis import getFoldsTotalKnown
28
- from typing import cast
29
- from Z0Z_tools import autoDecodingRLE
21
+ from mapFolding import getPathFilenameFoldsTotal, raiseIfNoneGitHubIssueNumber3, The
22
+ from mapFolding.someAssemblyRequired import (
23
+ ast_Identifier,
24
+ be,
25
+ ifThis,
26
+ IngredientsFunction,
27
+ IngredientsModule,
28
+ LedgerOfImports,
29
+ Make,
30
+ NodeChanger,
31
+ NodeTourist,
32
+ str_nameDOTname,
33
+ Then,
34
+ )
35
+ from mapFolding.someAssemblyRequired.RecipeJob import RecipeJob
36
+ from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
37
+ from mapFolding.someAssemblyRequired.transformationTools import dictionaryEstimates, extractFunctionDef, write_astModule, makeInitializedComputationState
30
38
  from pathlib import PurePosixPath
39
+ from typing import cast, NamedTuple
40
+ from Z0Z_tools import autoDecodingRLE
31
41
  import ast
32
42
  """Synthesize one file to compute `foldsTotal` of `mapShape`."""
33
43
 
@@ -89,6 +99,10 @@ if __name__ == '__main__':
89
99
  countWithProgressBar = NodeChanger(findThis, doThat)
90
100
  countWithProgressBar.visit(ingredientsFunction.astFunctionDef)
91
101
 
102
+ removeReturnStatement = NodeChanger(be.Return, Then.removeIt)
103
+ removeReturnStatement.visit(ingredientsFunction.astFunctionDef)
104
+ ingredientsFunction.astFunctionDef.returns = Make.Constant(value=None)
105
+
92
106
  ingredientsModule.appendLauncher(ast.parse(linesLaunch))
93
107
 
94
108
  return ingredientsModule, ingredientsFunction
@@ -119,9 +133,13 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
119
133
  """
120
134
  ingredientsFunction.imports.update(job.shatteredDataclass.ledger)
121
135
 
122
- list_IdentifiersNotUsed = list_IdentifiersNotUsedHARDCODED
123
-
124
136
  list_argCuzMyBrainRefusesToThink = ingredientsFunction.astFunctionDef.args.args + ingredientsFunction.astFunctionDef.args.posonlyargs + ingredientsFunction.astFunctionDef.args.kwonlyargs
137
+ list_arg_arg: list[ast_Identifier] = [ast_arg.arg for ast_arg in list_argCuzMyBrainRefusesToThink]
138
+ listName: list[ast.Name] = []
139
+ NodeTourist(be.Name, Then.appendTo(listName)).visit(ingredientsFunction.astFunctionDef)
140
+ list_Identifiers: list[ast_Identifier] = [astName.id for astName in listName]
141
+ list_IdentifiersNotUsed: list[ast_Identifier] = list(set(list_arg_arg) - set(list_Identifiers))
142
+
125
143
  for ast_arg in list_argCuzMyBrainRefusesToThink:
126
144
  if ast_arg.arg in job.shatteredDataclass.field2AnnAssign:
127
145
  if ast_arg.arg in list_IdentifiersNotUsed:
@@ -132,7 +150,7 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
132
150
  case 'scalar':
133
151
  ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
134
152
  case 'array':
135
- dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], addSpaces=True)
153
+ dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], True)
136
154
  dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
137
155
  ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
138
156
  case _:
@@ -181,11 +199,6 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
181
199
  if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
182
200
  ingredientsCount: IngredientsFunction = IngredientsFunction(astFunctionDef, LedgerOfImports())
183
201
 
184
- # Change the return so you can dynamically determine which variables are not used
185
- removeReturnStatement = NodeChanger(lambda node: isinstance(node, ast.Return), Then.removeIt) # type: ignore
186
- removeReturnStatement.visit(ingredientsCount.astFunctionDef)
187
- ingredientsCount.astFunctionDef.returns = Make.Constant(value=None)
188
-
189
202
  # Remove `foldGroups` and any other unused statements, so you can dynamically determine which variables are not used
190
203
  findThis = ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier('foldGroups'))
191
204
  doThat = Then.removeIt
@@ -199,46 +212,63 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
199
212
  doThat = Then.replaceWith(Make.Constant(int(job.state.__dict__[identifier])))
200
213
  NodeChanger(findThis, doThat).visit(ingredientsCount.astFunctionDef)
201
214
 
202
- # This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
203
215
  ingredientsModule = IngredientsModule()
216
+ # This launcher eliminates the use of one identifier, so run it now and you can dynamically determine which variables are not used
204
217
  if spices.useNumbaProgressBar:
205
218
  ingredientsModule, ingredientsCount = addLauncherNumbaProgress(ingredientsModule, ingredientsCount, job, spices)
206
219
  spices.parametersNumba['nogil'] = True
220
+ else:
221
+ linesLaunch: str = f"""
222
+ if __name__ == '__main__':
223
+ import time
224
+ timeStart = time.perf_counter()
225
+ foldsTotal = {job.countCallable}() * {job.state.leavesTotal}
226
+ print(time.perf_counter() - timeStart)
227
+ print('\\nmap {job.state.mapShape} =', foldsTotal)
228
+ writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
229
+ writeStream.write(str(foldsTotal))
230
+ writeStream.close()
231
+ """
232
+ # from mapFolding.oeis import getFoldsTotalKnown
233
+ # print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
234
+ ingredientsModule.appendLauncher(ast.parse(linesLaunch))
235
+ changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
236
+ changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
237
+ ingredientsCount.astFunctionDef.returns = job.shatteredDataclass.countingVariableAnnotation
207
238
 
208
239
  ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
209
240
 
210
- Z0Z_Identifier = 'DatatypeLeavesTotal'
211
- Z0Z_type = 'uint8'
212
- ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
213
- Z0Z_module = 'typing'
214
- Z0Z_annotation = 'TypeAlias'
215
- ingredientsModule.imports.addImportFrom_asStr(Z0Z_module, Z0Z_annotation)
216
- Z0Z_statement = Make.AnnAssign(Make.Name(Z0Z_Identifier, ast.Store()), Make.Name(Z0Z_annotation), Make.Name(Z0Z_type))
217
- ingredientsModule.appendPrologue(statement=Z0Z_statement)
218
-
219
- Z0Z_Identifier = 'DatatypeElephino'
220
- Z0Z_type = 'int16'
221
- ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
222
- Z0Z_module = 'typing'
223
- Z0Z_annotation = 'TypeAlias'
224
- ingredientsModule.imports.addImportFrom_asStr(Z0Z_module, Z0Z_annotation)
225
- Z0Z_statement = Make.AnnAssign(Make.Name(Z0Z_Identifier, ast.Store()), Make.Name(Z0Z_annotation), Make.Name(Z0Z_type))
226
- ingredientsModule.appendPrologue(statement=Z0Z_statement)
241
+ class DatatypeConfig(NamedTuple):
242
+ Z0Z_module: str_nameDOTname
243
+ fml: ast_Identifier
244
+ Z0Z_type_name: ast_Identifier
245
+ Z0Z_asname: ast_Identifier | None = None
246
+
247
+ listDatatypeConfigs = [
248
+ DatatypeConfig(fml='DatatypeLeavesTotal', Z0Z_module='numba', Z0Z_type_name='uint16'),
249
+ DatatypeConfig(fml='DatatypeElephino', Z0Z_module='numba', Z0Z_type_name='uint16'),
250
+ DatatypeConfig(fml='DatatypeFoldsTotal', Z0Z_module='numba', Z0Z_type_name='int64'),
251
+ ]
252
+
253
+ for datatypeConfig in listDatatypeConfigs:
254
+ ingredientsModule.imports.addImportFrom_asStr(datatypeConfig.Z0Z_module, datatypeConfig.Z0Z_type_name)
255
+ statement = Make.Assign(
256
+ [Make.Name(datatypeConfig.fml, ast.Store())],
257
+ Make.Name(datatypeConfig.Z0Z_type_name)
258
+ )
259
+ ingredientsModule.appendPrologue(statement=statement)
227
260
 
228
261
  ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
229
- Z0Z_module = 'numpy'
230
- Z0Z_asname = 'Array1DLeavesTotal'
231
- ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
232
- Z0Z_type_name = 'uint8'
233
- ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
234
- Z0Z_asname = 'Array1DElephino'
235
- ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
236
- Z0Z_type_name = 'int16'
237
- ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
238
- Z0Z_asname = 'Array3D'
239
- ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
240
- Z0Z_type_name = 'uint8'
241
- ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
262
+
263
+ listNumPyTypeConfigs = [
264
+ DatatypeConfig(fml='Array1DLeavesTotal', Z0Z_module='numpy', Z0Z_type_name='uint16', Z0Z_asname='Array1DLeavesTotal'),
265
+ DatatypeConfig(fml='Array1DElephino', Z0Z_module='numpy', Z0Z_type_name='uint16', Z0Z_asname='Array1DElephino'),
266
+ DatatypeConfig(fml='Array3D', Z0Z_module='numpy', Z0Z_type_name='uint16', Z0Z_asname='Array3D'),
267
+ ]
268
+
269
+ for typeConfig in listNumPyTypeConfigs:
270
+ ingredientsCount.imports.removeImportFrom(typeConfig.Z0Z_module, None, typeConfig.fml)
271
+ ingredientsCount.imports.addImportFrom_asStr(typeConfig.Z0Z_module, typeConfig.Z0Z_type_name, typeConfig.Z0Z_asname)
242
272
 
243
273
  ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
244
274
  # TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
@@ -269,11 +299,13 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
269
299
  """
270
300
 
271
301
  if __name__ == '__main__':
272
- mapShape = (2,4)
302
+ mapShape = (2,2,2,2,2,2,2,2)
273
303
  state = makeInitializedComputationState(mapShape)
274
- foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
304
+ # foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
305
+ foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
275
306
  pathModule = PurePosixPath(The.pathPackage, 'jobs')
276
307
  pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
277
308
  aJob = RecipeJob(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
278
- spices = SpicesJobNumba()
309
+ spices = SpicesJobNumba(useNumbaProgressBar=False, parametersNumba=parametersNumbaLight)
310
+ # spices = SpicesJobNumba()
279
311
  makeJobNumba(aJob, spices)