mapFolding 0.8.0__py3-none-any.whl → 0.8.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 (40) hide show
  1. mapFolding/__init__.py +33 -4
  2. mapFolding/basecamp.py +16 -2
  3. mapFolding/beDRY.py +40 -32
  4. mapFolding/filesystem.py +124 -90
  5. mapFolding/noHomeYet.py +12 -0
  6. mapFolding/oeis.py +18 -3
  7. mapFolding/reference/__init__.py +38 -0
  8. mapFolding/reference/flattened.py +66 -47
  9. mapFolding/reference/hunterNumba.py +28 -4
  10. mapFolding/reference/irvineJavaPort.py +13 -1
  11. mapFolding/reference/{jax.py → jaxCount.py} +46 -27
  12. mapFolding/reference/lunnanNumpy.py +19 -5
  13. mapFolding/reference/lunnanWhile.py +19 -7
  14. mapFolding/reference/rotatedEntryPoint.py +20 -3
  15. mapFolding/reference/total_countPlus1vsPlusN.py +226 -203
  16. mapFolding/someAssemblyRequired/__init__.py +29 -0
  17. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +32 -14
  18. mapFolding/someAssemblyRequired/ingredientsNumba.py +22 -1
  19. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +193 -0
  20. mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +3 -4
  21. mapFolding/someAssemblyRequired/transformDataStructures.py +168 -0
  22. mapFolding/someAssemblyRequired/transformationTools.py +233 -225
  23. mapFolding/theDao.py +19 -5
  24. mapFolding/theSSOT.py +89 -122
  25. mapfolding-0.8.2.dist-info/METADATA +187 -0
  26. mapfolding-0.8.2.dist-info/RECORD +39 -0
  27. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/WHEEL +1 -1
  28. tests/conftest.py +43 -33
  29. tests/test_computations.py +7 -7
  30. tests/test_other.py +2 -2
  31. mapFolding/reference/lunnan.py +0 -153
  32. mapFolding/someAssemblyRequired/Z0Z_workbench.py +0 -350
  33. mapFolding/someAssemblyRequired/synthesizeDataConverters.py +0 -117
  34. mapFolding/syntheticModules/numbaCountHistoricalExample.py +0 -158
  35. mapFolding/syntheticModules/numba_doTheNeedfulHistoricalExample.py +0 -13
  36. mapfolding-0.8.0.dist-info/METADATA +0 -157
  37. mapfolding-0.8.0.dist-info/RECORD +0 -41
  38. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/entry_points.txt +0 -0
  39. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info/licenses}/LICENSE +0 -0
  40. {mapfolding-0.8.0.dist-info → mapfolding-0.8.2.dist-info}/top_level.txt +0 -0
@@ -1,40 +1,46 @@
1
- """As of 2025-03-15
2
- Tools for transforming Python code from one format to another.
3
-
4
- Scope:
5
- - What is necessary to transform the baseline algorithm into optimized formats used by the official package.
6
-
7
- Aspirations:
8
- - Each tool is abstracted or generic enough to be used beyond the scope of the official package.
9
- - Each tool is designed to be used in a modular fashion, allowing for the creation of new tools by combining existing tools.
10
- - If a tool has a default setting, the setting shall be the setting used by the official package.
11
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.
24
+ """
25
+ from autoflake import fix_code as autoflake_fix_code
12
26
  from collections import defaultdict
13
- from collections.abc import Callable, Container, Generator, Sequence
14
- from contextlib import contextmanager
27
+ from collections.abc import Callable, Container, Sequence
28
+ from copy import deepcopy
29
+ from importlib import import_module as importlib_import_module
15
30
  from inspect import getsource as inspect_getsource
31
+ from mapFolding.filesystem import writeStringToHere
16
32
  from mapFolding.theSSOT import (
17
- getSourceAlgorithm,
18
- theDataclassIdentifier,
19
- theDataclassInstance,
20
- theDataclassInstanceTaskDistribution,
21
- theDispatcherCallable,
22
- theFileExtension,
33
+ raiseIfNoneGitHubIssueNumber3,
34
+ The,
23
35
  theFormatStrModuleForCallableSynthetic,
24
36
  theFormatStrModuleSynthetic,
25
- theLogicalPathModuleDataclass,
26
37
  theLogicalPathModuleDispatcherSynthetic,
27
38
  theModuleDispatcherSynthetic,
28
- theModuleOfSyntheticModules,
29
- thePackageName,
30
- thePathPackage,
31
- theSourceInitializeCallable,
32
- theSourceParallelCallable,
33
- theSourceSequentialCallable,
34
39
  )
35
- from pathlib import PurePosixPath
40
+ from os import PathLike
41
+ from pathlib import Path, PurePath, PurePosixPath
36
42
  from types import ModuleType
37
- from typing import Any, Generic, cast, TypeAlias, TypeGuard, TypeVar
43
+ from typing import Any, cast, Generic, TypeAlias, TypeGuard, TypeVar
38
44
  from Z0Z_tools import updateExtendPolishDictionaryLists
39
45
  import ast
40
46
  import dataclasses
@@ -56,24 +62,15 @@ Namespace: uppercase, should only appear in camelCase and means "namespace", low
56
62
 
57
63
  # Would `LibCST` be better than `ast` in some cases? https://github.com/hunterhogan/mapFolding/issues/7
58
64
 
59
- nodeType = TypeVar('nodeType', bound=ast.AST)
60
- ast_Identifier: TypeAlias = str
61
65
  ast_expr_Slice: TypeAlias = ast.expr
62
- strDotStrCuzPyStoopid: TypeAlias = str
63
- strORlist_ast_type_paramORintORNone: TypeAlias = Any
64
- list_ast_type_paramORintORNone: TypeAlias = Any
65
- strORintORNone: TypeAlias = Any
66
+ ast_Identifier: TypeAlias = str
66
67
  astClassHasAttributeDOTname: TypeAlias = ast.FunctionDef | ast.ClassDef | ast.AsyncFunctionDef
67
68
  astMosDef = TypeVar('astMosDef', bound=astClassHasAttributeDOTname)
68
-
69
- @contextmanager
70
- def listAsNode(ImaList: list[ast.stmt]) -> Generator[ast.Module, None, None]:
71
- """Convert a list of AST nodes into a module for use in node walking tools."""
72
- ImaModule = ast.Module(ImaList)
73
- try:
74
- yield ImaModule
75
- finally:
76
- ImaList = ImaModule.body
69
+ list_ast_type_paramORintORNone: TypeAlias = Any
70
+ nodeType = TypeVar('nodeType', bound=ast.AST)
71
+ strDotStrCuzPyStoopid: TypeAlias = str
72
+ strORintORNone: TypeAlias = Any
73
+ strORlist_ast_type_paramORintORNone: TypeAlias = Any
77
74
 
78
75
  class NodeCollector(Generic[nodeType], ast.NodeVisitor):
79
76
  """A node visitor that collects data via one or more actions when a predicate is met."""
@@ -87,255 +84,189 @@ class NodeCollector(Generic[nodeType], ast.NodeVisitor):
87
84
  action(cast(nodeType, node))
88
85
  self.generic_visit(node)
89
86
 
90
- ImaViolationOfTheLiskovSubstitutionPrinciple: TypeAlias = ast.AST
91
87
  class NodeReplacer(Generic[nodeType], ast.NodeTransformer):
92
88
  """A node transformer that replaces or removes AST nodes based on a condition."""
93
89
  def __init__(self, findThis: Callable[[ast.AST], TypeGuard[nodeType] | bool], doThat: Callable[[nodeType], ast.AST | Sequence[ast.AST] | None]) -> None:
94
90
  self.findThis = findThis
95
91
  self.doThat = doThat
96
92
 
97
- def visit(self, node: ImaViolationOfTheLiskovSubstitutionPrinciple) -> ast.AST | Sequence[ast.AST] | None:
93
+ def visit(self, node: ast.AST) -> ast.AST | Sequence[ast.AST] | None:
98
94
  if self.findThis(node):
99
95
  return self.doThat(cast(nodeType, node))
100
96
  return super().visit(node)
101
97
 
102
98
  class ifThis:
103
99
  @staticmethod
104
- def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], TypeGuard[ast_Identifier]]:
105
- return lambda node: node in container # type: ignore
106
-
100
+ def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]:
101
+ return lambda node: node in container
107
102
  @staticmethod
108
- def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call]]:
103
+ def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
109
104
  """If `namespace` is not applicable to your case, then call with `namespace=""`."""
110
- return lambda node: ifThis.matchesMeButNotAnyDescendant(ifThis.CallReallyIs(namespace, identifier))(node) # type: ignore
111
-
105
+ return lambda node: ifThis.matchesMeButNotAnyDescendant(ifThis.CallReallyIs(namespace, identifier))(node)
112
106
  @staticmethod
113
- def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], TypeGuard[ast.Call]]:
114
- return lambda node: ifThis.isCall(node) and ifThis.isName(node.func) and ifThis.ast_IdentifierIsIn(container)(node.func.id) and ifThis.CallDoesNotCallItself("", node.func.id)(node) # type: ignore
115
-
107
+ def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
108
+ return lambda node: ifThis.isCall(node) and ifThis.isName(node.func) and ifThis.ast_IdentifierIsIn(container)(node.func.id) and ifThis.CallDoesNotCallItself("", node.func.id)(node)
116
109
  @staticmethod
117
- def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call]]:
118
- return ifThis.isAnyOf(ifThis.isCall_Identifier(identifier), ifThis.isCallNamespace_Identifier(namespace, identifier)) # type: ignore
119
-
110
+ def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
111
+ return ifThis.isAnyOf(ifThis.isCall_Identifier(identifier), ifThis.isCallNamespace_Identifier(namespace, identifier))
120
112
  @staticmethod
121
113
  def is_keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
122
114
  return isinstance(node, ast.keyword)
123
-
124
115
  @staticmethod
125
116
  def is_keywordAndValueIsConstant(node: ast.AST) -> TypeGuard[ast.keyword]:
126
117
  return ifThis.is_keyword(node) and ifThis.isConstant(node.value)
127
-
128
118
  @staticmethod
129
- def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword]]:
130
- return lambda node: ifThis.is_keyword(node) and node.arg == identifier # type: ignore
131
-
119
+ def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
120
+ def workhorse(node: ast.AST) -> TypeGuard[ast.keyword] | bool:
121
+ return ifThis.is_keyword(node) and node.arg == identifier
122
+ return workhorse
132
123
  @staticmethod
133
- def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], TypeGuard[ast.keyword]]:
134
- return lambda node: (ifThis.is_keyword_Identifier(identifier)(node) and ifThis.is_keywordAndValueIsConstant(node) and ifThis.isConstantEquals(ConstantValue)(node.value)) # type: ignore
135
-
124
+ def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
125
+ return lambda node: ifThis.is_keyword_Identifier(identifier)(node) and ifThis.is_keywordAndValueIsConstant(node) and ifThis.isConstantEquals(ConstantValue)(node.value)
136
126
  @staticmethod
137
127
  def isAllOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
138
128
  return lambda node: all(predicate(node) for predicate in thesePredicates)
139
-
140
129
  @staticmethod
141
130
  def isAnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
142
131
  return isinstance(node, ast.AnnAssign)
143
-
144
132
  @staticmethod
145
133
  def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
146
134
  return ifThis.isAnnAssign(node) and ifThis.isName(node.annotation)
147
-
148
135
  @staticmethod
149
136
  def isAnnAssignAndTargetIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
150
137
  return ifThis.isAnnAssign(node) and ifThis.isName(node.target)
151
-
152
138
  @staticmethod
153
- def isAnnAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign]]:
154
- return lambda node: ifThis.isAnnAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target) # type: ignore
155
-
139
+ def isAnnAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
140
+ return lambda node: ifThis.isAnnAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target)
156
141
  @staticmethod
157
142
  def isAnyAssignmentTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
158
143
  return ifThis.isAnyOf(ifThis.isAssignOnlyTo(identifier), ifThis.isAnnAssignTo(identifier), ifThis.isAugAssignTo(identifier))
159
-
160
144
  @staticmethod
161
145
  def isAnyCompare(node: ast.AST) -> TypeGuard[ast.Compare] | TypeGuard[ast.BoolOp]:
162
146
  return ifThis.isCompare(node) or ifThis.isBoolOp(node)
163
-
164
147
  @staticmethod
165
148
  def isAnyOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
166
149
  return lambda node: any(predicate(node) for predicate in thesePredicates)
167
-
168
150
  @staticmethod
169
151
  def isAssign(node: ast.AST) -> TypeGuard[ast.Assign]:
170
152
  return isinstance(node, ast.Assign)
171
-
172
153
  @staticmethod
173
- def isAssignAndValueIsCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
174
- return lambda node: ifThis.isAssign(node) and ifThis.isCall_Identifier(identifier)(node.value) # type: ignore
175
-
176
- # @staticmethod
177
- # def isAssignAndValueIsCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
178
- # return lambda node: ifThis.isAssign(node) and ifThis.isCallNamespace_Identifier(namespace, identifier)(node.value)
179
-
154
+ def isAssignAndValueIsCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
155
+ return lambda node: ifThis.isAssign(node) and ifThis.isCall_Identifier(identifier)(node.value)
180
156
  @staticmethod
181
- def isAssignAndValueIsCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
157
+ def isAssignAndValueIsCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
182
158
  return ifThis.isAssignAndValueIs(ifThis.isCallNamespace_Identifier(namespace, identifier))
183
-
184
159
  @staticmethod
185
- def isAssignOnlyTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
186
- return lambda node: ifThis.isAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.targets[0]) # type: ignore
187
-
160
+ def isAssignOnlyTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
161
+ return lambda node: ifThis.isAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.targets[0])
188
162
  @staticmethod
189
- def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
163
+ def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
190
164
  """node is Assign and node.targets[0] matches `targets0Predicate`."""
191
- return lambda node: ifThis.isAssign(node) and targets0Predicate(node.targets[0]) # type: ignore
192
-
165
+ return lambda node: ifThis.isAssign(node) and targets0Predicate(node.targets[0])
193
166
  @staticmethod
194
- def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign]]:
195
- """node is Assign and node.value matches `valuePredicate`.
196
-
167
+ def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
168
+ """node is ast.Assign and node.value matches `valuePredicate`.
197
169
  Parameters:
198
170
  valuePredicate: Function that evaluates the value of the assignment
199
-
200
171
  Returns:
201
- A predicate that matches assignments with values meeting the criteria
172
+ predicate: matches assignments with values meeting the criteria
202
173
  """
203
- return lambda node: ifThis.isAssign(node) and valuePredicate(node.value) # type: ignore
204
-
174
+ return lambda node: ifThis.isAssign(node) and valuePredicate(node.value)
205
175
  @staticmethod
206
176
  def isAttribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
207
177
  return isinstance(node, ast.Attribute)
208
-
209
178
  @staticmethod
210
179
  def isAugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
211
180
  return isinstance(node, ast.AugAssign)
212
-
213
181
  @staticmethod
214
- def isAugAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AugAssign]]:
215
- return lambda node: ifThis.isAugAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target) # type: ignore
216
-
182
+ def isAugAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.AugAssign] | bool]:
183
+ return lambda node: ifThis.isAugAssign(node) and ifThis.NameReallyIs_Identifier(identifier)(node.target)
217
184
  @staticmethod
218
185
  def isBoolOp(node: ast.AST) -> TypeGuard[ast.BoolOp]:
219
186
  return isinstance(node, ast.BoolOp)
220
-
221
187
  @staticmethod
222
188
  def isCall(node: ast.AST) -> TypeGuard[ast.Call]:
223
189
  return isinstance(node, ast.Call)
224
-
225
190
  @staticmethod
226
- def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call]]:
227
- return lambda node: ifThis.isCall(node) and ifThis.isName_Identifier(identifier)(node.func) # type: ignore
228
-
191
+ def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
192
+ return lambda node: ifThis.isCall(node) and ifThis.isName_Identifier(identifier)(node.func)
229
193
  @staticmethod
230
- def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call]]:
231
- return lambda node: ifThis.isCall(node) and ifThis.is_nameDOTnameNamespace_Identifier(namespace, identifier)(node.func) # type: ignore
232
-
194
+ def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
195
+ return lambda node: ifThis.isCall(node) and ifThis.is_nameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
233
196
  @staticmethod
234
197
  def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
235
198
  return ifThis.isCall(node) and ifThis.isName(node.func)
236
-
237
199
  @staticmethod
238
200
  def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
239
201
  return isinstance(node, ast.ClassDef)
240
-
241
202
  @staticmethod
242
- def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef]]:
243
- return lambda node: ifThis.isClassDef(node) and node.name == identifier # type: ignore
244
-
203
+ def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
204
+ return lambda node: ifThis.isClassDef(node) and node.name == identifier
245
205
  @staticmethod
246
206
  def isCompare(node: ast.AST) -> TypeGuard[ast.Compare]:
247
207
  return isinstance(node, ast.Compare)
248
-
249
208
  @staticmethod
250
209
  def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
251
210
  return isinstance(node, ast.Constant)
252
-
253
211
  @staticmethod
254
- def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant]]:
255
- return lambda node: ifThis.isConstant(node) and node.value == value # type: ignore
256
-
212
+ def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
213
+ return lambda node: ifThis.isConstant(node) and node.value == value
257
214
  @staticmethod
258
215
  def isExpr(node: ast.AST) -> TypeGuard[ast.Expr]:
259
216
  return isinstance(node, ast.Expr)
260
-
261
217
  @staticmethod
262
218
  def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
263
219
  return isinstance(node, ast.FunctionDef)
264
-
265
220
  @staticmethod
266
- def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef]]:
267
- return lambda node: ifThis.isFunctionDef(node) and node.name == identifier # type: ignore
268
-
221
+ def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef] | bool]:
222
+ return lambda node: ifThis.isFunctionDef(node) and node.name == identifier
269
223
  @staticmethod
270
224
  def isImport(node: ast.AST) -> TypeGuard[ast.Import]:
271
225
  return isinstance(node, ast.Import)
272
-
273
- # A "name" is roughly ast._Identifier
274
-
275
- # A Name can be a "name"
276
- # ast.Name()
277
-
278
- # These classes can be a "name": I will call the group 'ClassQuizas'
279
- # The technical mechanism is `type[ast.ClassQuizas.value] == ast.Name`
280
- # or, and this is the annoying part,
281
- # `type[ast.ClassQuizas.value] == ast.ClassQuizas and type[ast.ClassQuizas.value.value] == ast.ClassQuizas and ... == ast.Name`
282
- # ast.Attribute()
283
- # ast.Subscript()
284
- # ast.Starred()
285
-
286
226
  @staticmethod
287
227
  def isName(node: ast.AST) -> TypeGuard[ast.Name]:
228
+ """TODO
229
+ ast.Name()
230
+ ast.Attribute()
231
+ ast.Subscript()
232
+ ast.Starred()
233
+ """
288
234
  return isinstance(node, ast.Name)
289
-
290
235
  @staticmethod
291
- def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name]]:
292
- return lambda node: ifThis.isName(node) and node.id == identifier # type: ignore
293
-
236
+ def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name] | bool]:
237
+ return lambda node: ifThis.isName(node) and node.id == identifier
294
238
  @staticmethod
295
239
  def is_nameDOTname(node: ast.AST) -> TypeGuard[ast.Attribute]:
296
240
  return ifThis.isAttribute(node) and ifThis.isName(node.value)
297
-
298
241
  @staticmethod
299
- def is_nameDOTnameNamespace(namespace: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute]]:
300
- return lambda node: (ifThis.is_nameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value)) # type: ignore
301
-
242
+ def is_nameDOTnameNamespace(namespace: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
243
+ return lambda node: ifThis.is_nameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value)
302
244
  @staticmethod
303
- def is_nameDOTnameNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute]]:
304
- return lambda node: (ifThis.is_nameDOTnameNamespace(namespace)(node) and node.attr == identifier) # type: ignore
305
-
306
- @staticmethod
307
- def isAnyNestedName(node: ast.AST):
308
- return ifThis.isAnyOf()
309
-
245
+ def is_nameDOTnameNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
246
+ return lambda node: ifThis.is_nameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value) and node.attr == identifier
310
247
  @staticmethod
311
248
  def NameReallyIs_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
312
249
  # The following logic is incomplete.
313
250
  return ifThis.isAnyOf(ifThis.isName_Identifier(identifier), ifThis.isSubscriptIsName_Identifier(identifier))
314
-
315
251
  @staticmethod
316
252
  def isReturn(node: ast.AST) -> TypeGuard[ast.Return]:
317
253
  return isinstance(node, ast.Return)
318
-
319
254
  @staticmethod
320
255
  def isReturnAnyCompare(node: ast.AST) -> TypeGuard[ast.Return]:
321
256
  return ifThis.isReturn(node) and node.value is not None and ifThis.isAnyCompare(node.value)
322
-
323
257
  @staticmethod
324
258
  def isReturnUnaryOp(node: ast.AST) -> TypeGuard[ast.Return]:
325
259
  return ifThis.isReturn(node) and node.value is not None and ifThis.isUnaryOp(node.value)
326
-
327
260
  @staticmethod
328
261
  def isSubscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
329
262
  return isinstance(node, ast.Subscript)
330
-
331
263
  @staticmethod
332
264
  def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript]]:
333
265
  """node is `ast.Subscript` and the top-level `ast.Name` is `identifier`
334
266
  Parameters:
335
267
  identifier: The identifier to look for in the value chain
336
-
337
268
  Returns:
338
- A predicate function that checks if a node matches the criteria
269
+ predicate: function that checks if a node matches the criteria
339
270
  """
340
271
  def predicate(node: ast.AST) -> TypeGuard[ast.Subscript]:
341
272
  if not ifThis.isSubscript(node):
@@ -349,26 +280,21 @@ class ifThis:
349
280
  return False
350
281
  return checkNodeDOTvalue(node.value)
351
282
  return predicate
352
-
353
283
  @staticmethod
354
- def isSubscriptIsName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript]]:
355
- return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) # type: ignore
356
-
284
+ def isSubscriptIsName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
285
+ return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value)
357
286
  @staticmethod
358
- def isSubscript_Identifier_Identifier(identifier: ast_Identifier, sliceIdentifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript]]:
359
- return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) and ifThis.isName_Identifier(sliceIdentifier)(node.slice) # type: ignore
360
-
287
+ def isSubscript_Identifier_Identifier(identifier: ast_Identifier, sliceIdentifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
288
+ return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) and ifThis.isName_Identifier(sliceIdentifier)(node.slice)
361
289
  @staticmethod
362
290
  def isUnaryOp(node: ast.AST) -> TypeGuard[ast.UnaryOp]:
363
291
  return isinstance(node, ast.UnaryOp)
364
-
365
- # Does this work?
292
+ # TODO Does this work?
366
293
  @staticmethod
367
294
  def matchesAtLeast1Descendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
368
295
  """Create a predicate that returns True if any descendant of the node matches the given predicate."""
369
296
  return lambda node: not ifThis.matchesNoDescendant(predicate)(node)
370
-
371
- # Does this work?
297
+ # TODO Does this work?
372
298
  @staticmethod
373
299
  def matchesMeAndMyDescendantsExactlyNTimes(predicate: Callable[[ast.AST], bool], nTimes: int) -> Callable[[ast.AST], bool]:
374
300
  """Create a predicate that returns True if exactly 'count' nodes in the tree match the predicate."""
@@ -376,12 +302,10 @@ class ifThis:
376
302
  matches = sum(1 for descendant in ast.walk(node) if predicate(descendant))
377
303
  return matches == nTimes
378
304
  return countMatchingNodes
379
-
380
305
  @staticmethod
381
306
  def matchesMeButNotAnyDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
382
307
  """Create a predicate that returns True if the node matches but none of its descendants match the predicate."""
383
308
  return lambda node: predicate(node) and ifThis.matchesNoDescendant(predicate)(node)
384
-
385
309
  @staticmethod
386
310
  def matchesNoDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
387
311
  """Create a predicate that returns True if no descendant of the node matches the given predicate."""
@@ -391,13 +315,11 @@ class ifThis:
391
315
  return False
392
316
  return True
393
317
  return checkNoMatchingDescendant
394
-
395
318
  @staticmethod
396
- def OnlyReturnAnyCompare(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
319
+ def onlyReturnAnyCompare(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
397
320
  return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnAnyCompare(astFunctionDef.body[0])
398
-
399
321
  @staticmethod
400
- def OnlyReturnUnaryOp(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
322
+ def onlyReturnUnaryOp(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
401
323
  return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnUnaryOp(astFunctionDef.body[0])
402
324
 
403
325
  class Make:
@@ -405,29 +327,23 @@ class Make:
405
327
  def ast_arg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: strORintORNone) -> ast.arg:
406
328
  """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
407
329
  return ast.arg(identifier, annotation, **keywordArguments)
408
-
409
330
  @staticmethod
410
331
  def ast_keyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
411
332
  return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
412
-
413
333
  @staticmethod
414
334
  def astAlias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
415
335
  return ast.alias(name, asname)
416
-
417
336
  @staticmethod
418
337
  def astAnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign:
419
338
  """`simple: int`: uses a clever int-from-boolean to assign the correct value to the `simple` attribute. So, don't add it as a parameter."""
420
339
  return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
421
-
422
340
  @staticmethod
423
341
  def astAssign(listTargets: Any, value: ast.expr, **keywordArguments: strORintORNone) -> ast.Assign:
424
342
  """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
425
343
  return ast.Assign(targets=listTargets, value=value, **keywordArguments)
426
-
427
344
  @staticmethod
428
345
  def astArgumentsSpecification(posonlyargs: list[ast.arg]=[], args: list[ast.arg]=[], vararg: ast.arg|None=None, kwonlyargs: list[ast.arg]=[], kw_defaults: list[ast.expr|None]=[None], kwarg: ast.arg|None=None, defaults: list[ast.expr]=[]) -> ast.arguments:
429
346
  return ast.arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
430
-
431
347
  @staticmethod
432
348
  def astAttribute(value: ast.expr, attribute: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Attribute:
433
349
  """
@@ -436,47 +352,37 @@ class Make:
436
352
  attribute: the `str` after the dot
437
353
  context (ast.Load()): Load/Store/Del"""
438
354
  return ast.Attribute(value, attribute, context, **keywordArguments)
439
-
440
355
  @staticmethod
441
356
  def astCall(caller: ast.Name | ast.Attribute, listArguments: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
442
357
  return ast.Call(func=caller, args=list(listArguments) if listArguments else [], keywords=list(list_astKeywords) if list_astKeywords else [])
443
-
444
358
  @staticmethod
445
359
  def astClassDef(name: ast_Identifier, listBases: list[ast.expr]=[], list_keyword: list[ast.keyword]=[], body: list[ast.stmt]=[], decorator_list: list[ast.expr]=[], **keywordArguments: list_ast_type_paramORintORNone) -> ast.ClassDef:
446
360
  """keywordArguments: type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
447
361
  return ast.ClassDef(name=name, bases=listBases, keywords=list_keyword, body=body, decorator_list=decorator_list, **keywordArguments)
448
-
449
362
  @staticmethod
450
363
  def astConstant(value: Any, **keywordArguments: strORintORNone) -> ast.Constant:
451
364
  """value: str|int|float|bool|None|bytes|bytearray|memoryview|complex|list|tuple|dict|set, or any other type that can be represented as a constant in Python.
452
365
  keywordArguments: kind:str, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
453
366
  return ast.Constant(value, **keywordArguments)
454
-
455
367
  @staticmethod
456
368
  def astFunctionDef(name: ast_Identifier, argumentsSpecification: ast.arguments=ast.arguments(), body: list[ast.stmt]=[], decorator_list: list[ast.expr]=[], returns: ast.expr|None=None, **keywordArguments: strORlist_ast_type_paramORintORNone) -> ast.FunctionDef:
457
369
  """keywordArguments: type_comment:str|None, type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
458
370
  return ast.FunctionDef(name=name, args=argumentsSpecification, body=body, decorator_list=decorator_list, returns=returns, **keywordArguments)
459
-
460
371
  @staticmethod
461
372
  def astImport(moduleName: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
462
373
  return ast.Import(names=[Make.astAlias(moduleName, asname)], **keywordArguments)
463
-
464
374
  @staticmethod
465
375
  def astImportFrom(moduleName: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
466
376
  return ast.ImportFrom(module=moduleName, names=list_astAlias, level=0, **keywordArguments)
467
-
468
377
  @staticmethod
469
378
  def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
470
379
  return ast.Module(body, type_ignores)
471
-
472
380
  @staticmethod
473
381
  def astName(identifier: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Name:
474
382
  return ast.Name(identifier, context, **keywordArguments)
475
-
476
383
  @staticmethod
477
384
  def itDOTname(nameChain: ast.Name | ast.Attribute, dotName: str) -> ast.Attribute:
478
385
  return ast.Attribute(value=nameChain, attr=dotName, ctx=ast.Load())
479
-
480
386
  @staticmethod
481
387
  # TODO rewrite with all parameters
482
388
  def nameDOTname(identifier: ast_Identifier, *dotName: str) -> ast.Name | ast.Attribute:
@@ -486,20 +392,15 @@ class Make:
486
392
  for suffix in dotName:
487
393
  nameDOTname = Make.itDOTname(nameDOTname, suffix)
488
394
  return nameDOTname
489
-
490
395
  @staticmethod
491
396
  def astReturn(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
492
397
  return ast.Return(value, **keywordArguments)
493
-
494
398
  @staticmethod
495
399
  def astSubscript(value: ast.expr, slice: ast_expr_Slice, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Subscript:
496
400
  return ast.Subscript(value, slice, ctx=context, **keywordArguments)
497
-
498
401
  @staticmethod
499
402
  def astTuple(elements: Sequence[ast.expr], context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Tuple:
500
- # def astTuple(elements: Sequence[ast.expr], context: ast.expr_context | None = None, **keywordArguments: int) -> ast.Tuple:
501
403
  """context: Load/Store/Del"""
502
- # context = context or ast.Load()
503
404
  return ast.Tuple(elts=list(elements), ctx=context, **keywordArguments)
504
405
 
505
406
  class LedgerOfImports:
@@ -513,8 +414,7 @@ class LedgerOfImports:
513
414
  self.walkThis(startWith)
514
415
 
515
416
  def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
516
- if not isinstance(astImport_, (ast.Import, ast.ImportFrom)):
517
- raise ValueError(f"Expected ast.Import or ast.ImportFrom, got {type(astImport_)}")
417
+ assert isinstance(astImport_, (ast.Import, ast.ImportFrom)), f"Expected ast.Import or ast.ImportFrom, got {type(astImport_)}"
518
418
  if isinstance(astImport_, ast.Import):
519
419
  for alias in astImport_.names:
520
420
  self.listImport.append(alias.name)
@@ -594,8 +494,6 @@ class Then:
594
494
  def Z0Z_appendAnnAssignOf_nameDOTnameTo(identifier: ast_Identifier, list_nameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AnnAssign], None]:
595
495
  return lambda node: list_nameDOTname.append(Make.astAnnAssign(node.target, node.annotation, Make.nameDOTname(identifier, node.target.id))) # type: ignore
596
496
 
597
- # TODO When resolving the ledger of imports, remove self-referential imports
598
-
599
497
  @dataclasses.dataclass
600
498
  class IngredientsFunction:
601
499
  """Everything necessary to integrate a function into a module should be here."""
@@ -606,11 +504,6 @@ class IngredientsFunction:
606
504
  class IngredientsModule:
607
505
  """Everything necessary to create one _logical_ `ast.Module` should be here.
608
506
  Extrinsic qualities should _probably_ be handled externally."""
609
- # If an `ast.Module` had a logical name that would be reasonable, but Python is firmly opposed
610
- # to a reasonable namespace, therefore, Hunter, you were silly to add a `name` field to this
611
- # dataclass for building an `ast.Module`.
612
- # name: ast_Identifier
613
- # Hey, genius, note that this is dataclasses.InitVar
614
507
  ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
615
508
 
616
509
  # init var with an existing module? method to deconstruct an existing module?
@@ -659,25 +552,27 @@ class RecipeSynthesizeFlow:
659
552
  """Settings for synthesizing flow."""
660
553
  # ========================================
661
554
  # Source
662
- sourceAlgorithm: ModuleType = getSourceAlgorithm()
555
+ sourceAlgorithm: ModuleType = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
663
556
  sourcePython: str = inspect_getsource(sourceAlgorithm)
664
557
  source_astModule: ast.Module = ast.parse(sourcePython)
665
558
 
666
559
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
667
- sourceDispatcherCallable: str = theDispatcherCallable
668
- sourceInitializeCallable: str = theSourceInitializeCallable
669
- sourceParallelCallable: str = theSourceParallelCallable
670
- sourceSequentialCallable: str = theSourceSequentialCallable
671
-
672
- sourceDataclassIdentifier: str = theDataclassIdentifier
673
- sourceDataclassInstance: str = theDataclassInstance
674
- sourceDataclassInstanceTaskDistribution: str = theDataclassInstanceTaskDistribution
675
- sourcePathModuleDataclass: str = theLogicalPathModuleDataclass
676
-
560
+ sourceDispatcherCallable: str = The.dispatcherCallable
561
+ sourceInitializeCallable: str = The.sourceInitializeCallable
562
+ sourceParallelCallable: str = The.sourceParallelCallable
563
+ sourceSequentialCallable: str = The.sourceSequentialCallable
564
+
565
+ sourceDataclassIdentifier: str = The.dataclassIdentifier
566
+ sourceDataclassInstance: str = The.dataclassInstance
567
+ sourceDataclassInstanceTaskDistribution: str = The.dataclassInstanceTaskDistribution
568
+ sourcePathModuleDataclass: str = The.logicalPathModuleDataclass
569
+
570
+ sourceConcurrencyManagerNamespace = The.sourceConcurrencyManagerNamespace
571
+ sourceConcurrencyManagerIdentifier = The.sourceConcurrencyManagerIdentifier
677
572
  # ========================================
678
573
  # Filesystem
679
- pathPackage: PurePosixPath = PurePosixPath(thePathPackage)
680
- fileExtension: str = theFileExtension
574
+ pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
575
+ fileExtension: str = The.fileExtension
681
576
 
682
577
  # ========================================
683
578
  # Logical identifiers
@@ -686,11 +581,11 @@ class RecipeSynthesizeFlow:
686
581
  formatStrModuleForCallableSynthetic: str = theFormatStrModuleForCallableSynthetic
687
582
 
688
583
  # Package
689
- packageName: ast_Identifier = thePackageName
584
+ packageName: ast_Identifier | None = The.packageName
690
585
 
691
586
  # Module
692
587
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
693
- Z0Z_flowLogicalPathRoot: str = theModuleOfSyntheticModules
588
+ Z0Z_flowLogicalPathRoot: str | None = The.moduleOfSyntheticModules
694
589
  moduleDispatcher: str = theModuleDispatcherSynthetic
695
590
  logicalPathModuleDataclass: str = sourcePathModuleDataclass
696
591
  # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
@@ -708,6 +603,26 @@ class RecipeSynthesizeFlow:
708
603
  # Variable
709
604
  dataclassInstance: str = sourceDataclassInstance
710
605
 
606
+ def _makePathFilename(self, filenameStem: str,
607
+ pathRoot: PurePosixPath | None = None,
608
+ logicalPathINFIX: strDotStrCuzPyStoopid | None = None,
609
+ fileExtension: str | None = None,
610
+ ) -> PurePosixPath:
611
+ """filenameStem: (hint: the name of the logical module)"""
612
+ if pathRoot is None:
613
+ pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
614
+ if logicalPathINFIX:
615
+ whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
616
+ pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
617
+ if fileExtension is None:
618
+ fileExtension = self.fileExtension
619
+ filename: str = filenameStem + fileExtension
620
+ return pathRoot.joinpath(filename)
621
+
622
+ @property
623
+ def pathFilenameDispatcher(self) -> PurePosixPath:
624
+ return self._makePathFilename(filenameStem=self.moduleDispatcher, logicalPathINFIX=self.Z0Z_flowLogicalPathRoot)
625
+
711
626
  def extractClassDef(identifier: ast_Identifier, module: ast.Module) -> ast.ClassDef | None:
712
627
  sherpa: list[ast.ClassDef] = []
713
628
  extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
@@ -732,9 +647,9 @@ def makeDictionaryReplacementStatements(module: ast.Module) -> dict[ast_Identifi
732
647
  dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
733
648
  dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
734
649
  for name, astFunctionDef in dictionaryFunctionDef.items():
735
- if ifThis.OnlyReturnAnyCompare(astFunctionDef):
650
+ if ifThis.onlyReturnAnyCompare(astFunctionDef):
736
651
  dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
737
- elif ifThis.OnlyReturnUnaryOp(astFunctionDef):
652
+ elif ifThis.onlyReturnUnaryOp(astFunctionDef):
738
653
  dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
739
654
  else:
740
655
  dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
@@ -761,3 +676,96 @@ def Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.A
761
676
  if not Z0Z_descendantContainsMatchingNode(node, exclusionPredicate):
762
677
  actionFunction(node)
763
678
  return wrappedAction
679
+
680
+ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
681
+ class FunctionInliner(ast.NodeTransformer):
682
+ def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
683
+ self.dictionaryReplacementStatements = dictionaryReplacementStatements
684
+
685
+ def generic_visit(self, node: ast.AST) -> ast.AST:
686
+ """Visit all nodes and replace them if necessary."""
687
+ return super().generic_visit(node)
688
+
689
+ def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
690
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
691
+ return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
692
+ return node
693
+
694
+ def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
695
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
696
+ return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
697
+ return node
698
+
699
+ def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
700
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
701
+ replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore[attr-defined]
702
+ if not isinstance(replacement, list):
703
+ return replacement
704
+ return node
705
+
706
+ keepGoing = True
707
+ ImaInlineFunction = deepcopy(astFunctionDef)
708
+ while keepGoing:
709
+ ImaInlineFunction = deepcopy(astFunctionDef)
710
+ FunctionInliner(deepcopy(dictionaryReplacementStatements)).visit(ImaInlineFunction)
711
+ if ast.unparse(ImaInlineFunction) == ast.unparse(astFunctionDef):
712
+ keepGoing = False
713
+ else:
714
+ astFunctionDef = deepcopy(ImaInlineFunction)
715
+ ast.fix_missing_locations(astFunctionDef)
716
+ return ImaInlineFunction
717
+
718
+ def Z0Z_replaceMatchingASTnodes(astTree: ast.AST, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> ast.AST:
719
+ class TargetedNodeReplacer(ast.NodeTransformer):
720
+ def __init__(self, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> None:
721
+ self.mappingFindReplaceNodes = mappingFindReplaceNodes
722
+
723
+ def visit(self, node: ast.AST) -> ast.AST:
724
+ for nodeFind, nodeReplace in self.mappingFindReplaceNodes.items():
725
+ if self.nodesMatchStructurally(node, nodeFind):
726
+ return nodeReplace
727
+ return self.generic_visit(node)
728
+
729
+ def nodesMatchStructurally(self, nodeSubject: ast.AST | list[Any] | Any, nodePattern: ast.AST | list[Any] | Any) -> bool:
730
+ if nodeSubject is None or nodePattern is None:
731
+ return nodeSubject is None and nodePattern is None
732
+
733
+ if type(nodeSubject) != type(nodePattern):
734
+ return False
735
+
736
+ if isinstance(nodeSubject, ast.AST):
737
+ for field, fieldValueSubject in ast.iter_fields(nodeSubject):
738
+ if field in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'ctx'):
739
+ continue
740
+ attrPattern = getattr(nodePattern, field, None)
741
+ if not self.nodesMatchStructurally(fieldValueSubject, attrPattern):
742
+ return False
743
+ return True
744
+
745
+ if isinstance(nodeSubject, list) and isinstance(nodePattern, list):
746
+ nodeSubjectList: list[Any] = nodeSubject
747
+ nodePatternList: list[Any] = nodePattern
748
+ return len(nodeSubjectList) == len(nodePatternList) and all(
749
+ self.nodesMatchStructurally(elementSubject, elementPattern)
750
+ for elementSubject, elementPattern in zip(nodeSubjectList, nodePatternList)
751
+ )
752
+
753
+ return nodeSubject == nodePattern
754
+
755
+ astTreeCurrent, astTreePrevious = None, astTree
756
+ while astTreeCurrent is None or ast.unparse(astTreeCurrent) != ast.unparse(astTreePrevious):
757
+ astTreePrevious = astTreeCurrent if astTreeCurrent else astTree
758
+ astTreeCurrent = TargetedNodeReplacer(mappingFindReplaceNodes).visit(astTreePrevious)
759
+
760
+ return astTreeCurrent
761
+
762
+ def write_astModule(ingredients: IngredientsModule, pathFilename: str | PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
763
+ astModule = ingredients.export()
764
+ ast.fix_missing_locations(astModule)
765
+ pythonSource: str = ast.unparse(astModule)
766
+ if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
767
+ autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleNames()
768
+ if packageName:
769
+ autoflake_additional_imports.append(packageName)
770
+ 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)
771
+ writeStringToHere(pythonSource, pathFilename)