mapFolding 0.6.0__py3-none-any.whl → 0.7.1__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 (54) hide show
  1. mapFolding/__init__.py +6 -104
  2. mapFolding/basecamp.py +12 -8
  3. mapFolding/beDRY.py +103 -286
  4. mapFolding/filesystem.py +95 -0
  5. mapFolding/noHomeYet.py +20 -0
  6. mapFolding/oeis.py +46 -39
  7. mapFolding/reference/flattened.py +377 -0
  8. mapFolding/reference/hunterNumba.py +132 -0
  9. mapFolding/reference/irvineJavaPort.py +120 -0
  10. mapFolding/reference/jax.py +208 -0
  11. mapFolding/reference/lunnan.py +153 -0
  12. mapFolding/reference/lunnanNumpy.py +123 -0
  13. mapFolding/reference/lunnanWhile.py +121 -0
  14. mapFolding/reference/rotatedEntryPoint.py +240 -0
  15. mapFolding/reference/total_countPlus1vsPlusN.py +211 -0
  16. mapFolding/someAssemblyRequired/Z0Z_workbench.py +33 -0
  17. mapFolding/someAssemblyRequired/__init__.py +16 -0
  18. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +21 -0
  19. mapFolding/someAssemblyRequired/ingredientsNumba.py +100 -0
  20. mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +7 -0
  21. mapFolding/someAssemblyRequired/synthesizeDataConverters.py +135 -0
  22. mapFolding/someAssemblyRequired/synthesizeNumba.py +91 -0
  23. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +417 -0
  24. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +91 -0
  25. mapFolding/someAssemblyRequired/transformationTools.py +425 -0
  26. mapFolding/someAssemblyRequired/whatWillBe.py +357 -0
  27. mapFolding/syntheticModules/__init__.py +0 -0
  28. mapFolding/syntheticModules/dataNamespaceFlattened.py +30 -0
  29. mapFolding/syntheticModules/multiprocessingCount_doTheNeedful.py +216 -0
  30. mapFolding/syntheticModules/numbaCount.py +90 -0
  31. mapFolding/syntheticModules/numbaCountExample.py +158 -0
  32. mapFolding/syntheticModules/numbaCountSequential.py +111 -0
  33. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +13 -0
  34. mapFolding/syntheticModules/numba_doTheNeedful.py +12 -0
  35. mapFolding/syntheticModules/numba_doTheNeedfulExample.py +13 -0
  36. mapFolding/theDao.py +216 -229
  37. mapFolding/theSSOT.py +269 -101
  38. {mapfolding-0.6.0.dist-info → mapfolding-0.7.1.dist-info}/METADATA +7 -6
  39. mapfolding-0.7.1.dist-info/RECORD +51 -0
  40. {mapfolding-0.6.0.dist-info → mapfolding-0.7.1.dist-info}/WHEEL +1 -1
  41. {mapfolding-0.6.0.dist-info → mapfolding-0.7.1.dist-info}/top_level.txt +1 -0
  42. tests/__init__.py +0 -0
  43. tests/conftest.py +278 -0
  44. tests/test_computations.py +53 -0
  45. tests/test_filesystem.py +52 -0
  46. tests/test_oeis.py +128 -0
  47. tests/test_other.py +84 -0
  48. tests/test_tasks.py +56 -0
  49. mapFolding/theConfiguration.py +0 -58
  50. mapFolding/theSSOTdatatypes.py +0 -155
  51. mapFolding/theWrongWay.py +0 -7
  52. mapfolding-0.6.0.dist-info/RECORD +0 -16
  53. {mapfolding-0.6.0.dist-info → mapfolding-0.7.1.dist-info}/LICENSE +0 -0
  54. {mapfolding-0.6.0.dist-info → mapfolding-0.7.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,425 @@
1
+ from collections.abc import Callable, Container, Sequence
2
+ from pathlib import Path
3
+ from typing import Any, cast, NamedTuple, TypeAlias, TYPE_CHECKING, TypeGuard, TypeVar
4
+ import ast
5
+ if TYPE_CHECKING:
6
+ from mapFolding.someAssemblyRequired.whatWillBe import LedgerOfImports
7
+ """
8
+ Semiotic notes:
9
+ In the `ast` package, some things that look and feel like a "name" are not `ast.Name` type. The following semiotics are a balance between technical precision and practical usage.
10
+
11
+ astName: always means `ast.Name`.
12
+ Name: uppercase, _should_ be interchangeable with astName, even in camelCase.
13
+ name: lowercase, never means `ast.Name`. In camelCase, I _should_ avoid using it in such a way that it could be confused with "Name", uppercase.
14
+ _Identifier: very strongly correlates with the private `ast._Identifier`, which is a TypeAlias for `str`.
15
+ identifier: lowercase, a general term that includes the above and other Python identifiers.
16
+ Identifier: uppercase, without the leading underscore should only appear in camelCase and means "identifier", lowercase.
17
+ namespace: lowercase, in dotted-names, such as `pathlib.Path` or `collections.abc`, "namespace" is the part before the dot.
18
+ Namespace: uppercase, should only appear in camelCase and means "namespace", lowercase.
19
+ """
20
+ # TODO consider semiotic usefulness of "namespace" or variations such as "namespaceName", "namespacePath", and "namespace_Identifier"
21
+
22
+ # TODO learn whether libcst can help
23
+
24
+ astParameter = TypeVar('astParameter', bound=Any)
25
+ ast_Identifier: TypeAlias = str
26
+ strDotStrCuzPyStoopid: TypeAlias = str
27
+ strORlist_ast_type_paramORintORNone: TypeAlias = Any
28
+ list_ast_type_paramORintORNone: TypeAlias = Any
29
+ strORintORNone: TypeAlias = Any
30
+ Z0Z_thisCannotBeTheBestWay: TypeAlias = list[ast.Name] | list[ast.Attribute] | list[ast.Subscript] | list[ast.Name | ast.Attribute] | list[ast.Name | ast.Subscript] | list[ast.Attribute | ast.Subscript] | list[ast.Name | ast.Attribute | ast.Subscript]
31
+
32
+ # NOTE: the new "Recipe" concept will allow me to remove this
33
+ class YouOughtaKnow(NamedTuple):
34
+ callableSynthesized: str
35
+ pathFilenameForMe: Path
36
+ astForCompetentProgrammers: ast.ImportFrom
37
+
38
+ # listAsNode
39
+
40
+ class NodeCollector(ast.NodeVisitor):
41
+ # A node visitor that collects data via one or more actions when a predicate is met.
42
+ def __init__(self, findPredicate: Callable[[ast.AST], bool], actions: list[Callable[[ast.AST], None]]) -> None:
43
+ self.findPredicate = findPredicate
44
+ self.actions = actions
45
+
46
+ def visit(self, node: ast.AST) -> None:
47
+ if self.findPredicate(node):
48
+ for action in self.actions:
49
+ action(node)
50
+ self.generic_visit(node)
51
+
52
+ class NodeReplacer(ast.NodeTransformer):
53
+ """
54
+ A node transformer that replaces or removes AST nodes based on a condition.
55
+ This transformer traverses an AST and for each node checks a predicate. If the predicate
56
+ returns True, the transformer uses the replacement builder to obtain a new node. Returning
57
+ None from the replacement builder indicates that the node should be removed.
58
+
59
+ Attributes:
60
+ findMe: A function that finds all locations that match a one or more conditions.
61
+ doThis: A function that does work at each location, such as make a new node, collect information or delete the node.
62
+
63
+ Methods:
64
+ visit(node: ast.AST) -> Optional[ast.AST]:
65
+ Visits each node in the AST, replacing or removing it based on the predicate.
66
+ """
67
+ def __init__(self
68
+ , findMe: Callable[[ast.AST], bool]
69
+ , doThis: Callable[[ast.AST], ast.AST | Sequence[ast.AST] | None]
70
+ ) -> None:
71
+ self.findMe = findMe
72
+ self.doThis = doThis
73
+
74
+ def visit(self, node: ast.AST) -> ast.AST | Sequence[ast.AST] | None:
75
+ if self.findMe(node):
76
+ return self.doThis(node)
77
+ return super().visit(node)
78
+
79
+ def descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callable[[ast.AST], bool]) -> bool:
80
+ """ Return True if any descendant of the node (or the node itself) matches the predicateFunction. """
81
+ matchFound = False
82
+
83
+ class DescendantFinder(ast.NodeVisitor):
84
+ def generic_visit(self, node: ast.AST) -> None:
85
+ nonlocal matchFound
86
+ if predicateFunction(node):
87
+ matchFound = True
88
+ else:
89
+ super().generic_visit(node)
90
+
91
+ DescendantFinder().visit(node)
92
+ return matchFound
93
+
94
+ def executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.AST], bool], actionFunction: Callable[[ast.AST], None]) -> Callable[[ast.AST], None]:
95
+ """
96
+ Return a new action that will execute actionFunction only if no descendant (or the node itself)
97
+ matches exclusionPredicate.
98
+ """
99
+ def wrappedAction(node: ast.AST) -> None:
100
+ if not descendantContainsMatchingNode(node, exclusionPredicate):
101
+ actionFunction(node)
102
+ return wrappedAction
103
+
104
+ class ifThis:
105
+ @staticmethod
106
+ def anyOf(*somePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
107
+ return lambda nodeTarget: any(predicate(nodeTarget) for predicate in somePredicates)
108
+
109
+ @staticmethod
110
+ def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], bool]:
111
+ return lambda node: node in container
112
+
113
+ # TODO is this only useable if namespace is not `None`? Yes, but use "" for namespace if necessary.
114
+ @staticmethod
115
+ def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
116
+ return lambda nodeFocus: ifThis.CallReallyIs(namespace, identifier)(nodeFocus) and 1 == sum(1 for descendant in ast.walk(nodeFocus) if ifThis.CallReallyIs(namespace, identifier)(descendant))
117
+
118
+ @staticmethod
119
+ def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], bool]:
120
+ return lambda nodeSubject: (ifThis.isCall(nodeSubject) and ifThis.isName(nodeSubject.func) and ifThis.ast_IdentifierIsIn(container)(nodeSubject.func.id) and ifThis.CallDoesNotCallItself("", nodeSubject.func.id)(nodeSubject))
121
+
122
+ @staticmethod
123
+ def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
124
+ return ifThis.anyOf(ifThis.isCall_Identifier(identifier), ifThis.isCallNamespace_Identifier(namespace, identifier))
125
+
126
+ @staticmethod
127
+ def is_keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
128
+ return isinstance(node, ast.keyword)
129
+
130
+ @staticmethod
131
+ def is_keywordAndValueIsConstant(nodeCheck: ast.AST) -> TypeGuard[ast.keyword]:
132
+ return ifThis.is_keyword(nodeCheck) and ifThis.isConstant(nodeCheck.value)
133
+
134
+ @staticmethod
135
+ def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
136
+ return lambda nodeInstant: ifThis.is_keyword(nodeInstant) and nodeInstant.arg == identifier
137
+ @staticmethod
138
+ def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], bool]:
139
+ return lambda astNode: (ifThis.is_keyword_Identifier(identifier)(astNode) and ifThis.is_keywordAndValueIsConstant(astNode) and ifThis.isConstantEquals(ConstantValue)(astNode.value))
140
+
141
+ @staticmethod
142
+ def isAnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
143
+ return isinstance(node, ast.AnnAssign)
144
+
145
+ @staticmethod
146
+ def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
147
+ return ifThis.isAnnAssign(node) and ifThis.isName(node.annotation)
148
+
149
+ @staticmethod
150
+ def isAnnAssignAndTargetIsName(whatNode: ast.AST) -> TypeGuard[ast.AnnAssign]:
151
+ return ifThis.isAnnAssign(whatNode) and ifThis.isName(whatNode.target)
152
+
153
+ @staticmethod
154
+ def isAnnAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
155
+ return lambda nodeStop: ifThis.isAnnAssign(nodeStop) and ifThis.NameReallyIs(identifier)(nodeStop.target)
156
+
157
+ @staticmethod
158
+ def isAnyAssignmentTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
159
+ return ifThis.anyOf(ifThis.isAssignOnlyTo(identifier), ifThis.isAnnAssignTo(identifier), ifThis.isAugAssignTo(identifier))
160
+
161
+ @staticmethod
162
+ def isAssign(node: ast.AST) -> TypeGuard[ast.Assign]:
163
+ return isinstance(node, ast.Assign)
164
+
165
+ @staticmethod
166
+ def isAssignOnlyTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
167
+ return lambda aNode: ifThis.isAssign(aNode) and ifThis.NameReallyIs(identifier)(aNode.targets[0])
168
+
169
+ @staticmethod
170
+ def isAttribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
171
+ return isinstance(node, ast.Attribute)
172
+
173
+ @staticmethod
174
+ def isAugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
175
+ return isinstance(node, ast.AugAssign)
176
+
177
+ @staticmethod
178
+ def isAugAssignTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
179
+ return lambda nodeQuestion: ifThis.isAugAssign(nodeQuestion) and ifThis.NameReallyIs(identifier)(nodeQuestion.target)
180
+
181
+ @staticmethod
182
+ def isCall(node: ast.AST) -> TypeGuard[ast.Call]:
183
+ return isinstance(node, ast.Call)
184
+
185
+ @staticmethod
186
+ def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
187
+ return lambda ImaNode: ifThis.isCall(ImaNode) and ifThis.isName_Identifier(identifier)(ImaNode.func)
188
+
189
+ # TODO what happens if `None` is passed as the namespace?
190
+ @staticmethod
191
+ def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
192
+ return lambda node: ifThis.isCall(node) and ifThis.isNameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
193
+
194
+ @staticmethod
195
+ def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
196
+ return ifThis.isCall(node) and ifThis.isName(node.func)
197
+
198
+ @staticmethod
199
+ def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
200
+ return isinstance(node, ast.ClassDef)
201
+
202
+ @staticmethod
203
+ def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
204
+ return lambda node: ifThis.isClassDef(node) and node.name == identifier
205
+
206
+ @staticmethod
207
+ def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
208
+ return isinstance(node, ast.Constant)
209
+
210
+ @staticmethod
211
+ def isConstantEquals(value: Any) -> Callable[[ast.AST], bool]:
212
+ return lambda node: ifThis.isConstant(node) and node.value == value
213
+
214
+ @staticmethod
215
+ def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
216
+ return isinstance(node, ast.FunctionDef)
217
+
218
+ @staticmethod
219
+ def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
220
+ return lambda node: ifThis.isFunctionDef(node) and node.name == identifier
221
+
222
+ @staticmethod
223
+ def isImport(node: ast.AST) -> TypeGuard[ast.Import]:
224
+ return isinstance(node, ast.Import)
225
+
226
+ @staticmethod
227
+ def isName(node: ast.AST) -> TypeGuard[ast.Name]:
228
+ return isinstance(node, ast.Name)
229
+
230
+ @staticmethod
231
+ def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
232
+ return lambda node: ifThis.isName(node) and node.id == identifier
233
+
234
+ @staticmethod
235
+ def isNameDOTname(node: ast.AST) -> TypeGuard[ast.Attribute]:
236
+ return ifThis.isAttribute(node) and ifThis.isName(node.value)
237
+
238
+ @staticmethod
239
+ def isNameDOTnameNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
240
+ return lambda node: ifThis.isNameDOTname(node) and ifThis.isName_Identifier(namespace)(node.value) and node.attr == identifier
241
+
242
+ @staticmethod
243
+ def isSubscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
244
+ return isinstance(node, ast.Subscript)
245
+
246
+ @staticmethod
247
+ def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
248
+ return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value)
249
+
250
+ @staticmethod
251
+ def isSubscript_Identifier_Identifier(identifier: ast_Identifier, sliceIdentifier: ast_Identifier) -> Callable[[ast.AST], bool]:
252
+ return lambda node: ifThis.isSubscript(node) and ifThis.isName_Identifier(identifier)(node.value) and ifThis.isName_Identifier(sliceIdentifier)(node.slice) # auto-generated
253
+
254
+ @staticmethod
255
+ def NameReallyIs(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
256
+ return ifThis.anyOf(ifThis.isName_Identifier(identifier), ifThis.isSubscript_Identifier(identifier))
257
+
258
+ class Make:
259
+ @staticmethod
260
+ def copy_astCallKeywords(astCall: ast.Call) -> dict[str, Any]:
261
+ """Extract keyword parameters from a decorator AST node."""
262
+ dictionaryKeywords: dict[str, Any] = {}
263
+ for keywordItem in astCall.keywords:
264
+ if isinstance(keywordItem.value, ast.Constant) and keywordItem.arg is not None:
265
+ dictionaryKeywords[keywordItem.arg] = keywordItem.value.value
266
+ return dictionaryKeywords
267
+
268
+ @staticmethod
269
+ def astAlias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
270
+ return ast.alias(name=name, asname=asname)
271
+
272
+ @staticmethod
273
+ def astAnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign:
274
+ """ `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."""
275
+ return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
276
+
277
+ @staticmethod
278
+ def astAssign(listTargets: Any, value: ast.expr, **keywordArguments: strORintORNone) -> ast.Assign:
279
+ """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
280
+ return ast.Assign(targets=listTargets, value=value, **keywordArguments)
281
+
282
+ @staticmethod
283
+ def astArg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: strORintORNone) -> ast.arg:
284
+ """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
285
+ return ast.arg(identifier, annotation, **keywordArguments)
286
+
287
+ @staticmethod
288
+ 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:
289
+ return ast.arguments(posonlyargs=posonlyargs, args=args, vararg=vararg, kwonlyargs=kwonlyargs, kw_defaults=kw_defaults, kwarg=kwarg, defaults=defaults)
290
+
291
+ @staticmethod
292
+ def astCall(caller: ast.Name | ast.Attribute, args: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
293
+ return ast.Call(func=caller, args=list(args) if args else [], keywords=list(list_astKeywords) if list_astKeywords else [])
294
+
295
+ @staticmethod
296
+ 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:
297
+ """keywordArguments: type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
298
+ return ast.ClassDef(name=name, bases=listBases, keywords=list_keyword, body=body, decorator_list=decorator_list, **keywordArguments)
299
+
300
+ @staticmethod
301
+ 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:
302
+ """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"""
303
+ return ast.FunctionDef(name=name, args=argumentsSpecification, body=body, decorator_list=decorator_list, returns=returns, **keywordArguments)
304
+
305
+ @staticmethod
306
+ def astImport(moduleName: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
307
+ return ast.Import(names=[Make.astAlias(moduleName, asname)], **keywordArguments)
308
+
309
+ @staticmethod
310
+ def astImportFrom(moduleName: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
311
+ return ast.ImportFrom(module=moduleName, names=list_astAlias, level=0, **keywordArguments)
312
+
313
+ @staticmethod
314
+ def astKeyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
315
+ return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
316
+
317
+ @staticmethod
318
+ def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
319
+ return ast.Module(body, type_ignores)
320
+
321
+ @staticmethod
322
+ def astName(identifier: ast_Identifier) -> ast.Name:
323
+ return ast.Name(id=identifier, ctx=ast.Load())
324
+
325
+ @staticmethod
326
+ def itDOTname(nameChain: ast.Name | ast.Attribute, dotName: str) -> ast.Attribute:
327
+ return ast.Attribute(value=nameChain, attr=dotName, ctx=ast.Load())
328
+
329
+ @staticmethod
330
+ def nameDOTname(identifier: ast_Identifier, *dotName: str) -> ast.Name | ast.Attribute:
331
+ nameDOTname: ast.Name | ast.Attribute = Make.astName(identifier)
332
+ if not dotName:
333
+ return nameDOTname
334
+ for suffix in dotName:
335
+ nameDOTname = Make.itDOTname(nameDOTname, suffix)
336
+ return nameDOTname
337
+
338
+ @staticmethod
339
+ def astReturn(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
340
+ return ast.Return(value=value, **keywordArguments)
341
+
342
+ @staticmethod
343
+ def astTuple(elements: Sequence[ast.expr], context: ast.expr_context | None = None, **keywordArguments: int) -> ast.Tuple:
344
+ """context: Load/Store/Del"""
345
+ context = context or ast.Load()
346
+ return ast.Tuple(elts=list(elements), ctx=context, **keywordArguments)
347
+
348
+ class Then:
349
+ @staticmethod
350
+ def insertThisAbove(astStatement: ast.stmt) -> Callable[[ast.stmt], Sequence[ast.stmt]]:
351
+ return lambda aboveMe: [astStatement, aboveMe]
352
+ @staticmethod
353
+ def insertThisBelow(astStatement: ast.stmt) -> Callable[[ast.stmt], Sequence[ast.stmt]]:
354
+ return lambda belowMe: [belowMe, astStatement]
355
+ @staticmethod
356
+ def replaceWith(astStatement: ast.stmt) -> Callable[[ast.stmt], ast.stmt]:
357
+ return lambda replaceMe: astStatement
358
+ @staticmethod
359
+ def removeThis(node: ast.AST) -> None:
360
+ return None
361
+ from mapFolding.someAssemblyRequired.whatWillBe import LedgerOfImports
362
+ @staticmethod
363
+ def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AST], None]:
364
+ return lambda node: ledger.addImportFromStr(logicalPath, cast(ast.Name, cast(ast.AnnAssign, node).annotation).id)
365
+ @staticmethod
366
+ def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AST], None]:
367
+ return lambda node: list_keyword.append(Make.astKeyword(cast(ast.Name, cast(ast.AnnAssign, node).target).id, cast(ast.Name, cast(ast.AnnAssign, node).target)))
368
+ @staticmethod
369
+ def append_targetTo(listName: list[ast.Name]) -> Callable[[ast.AST], None]:
370
+ return lambda node: listName.append(cast(ast.Name, cast(ast.AnnAssign, node).target))
371
+ @staticmethod
372
+ def appendTo(listAST: Sequence[ast.AST]) -> Callable[[ast.AST], None]:
373
+ return lambda node: list(listAST).append(node)
374
+ @staticmethod
375
+ def Z0Z_appendAnnAssignOfNameDOTnameTo(identifier: ast_Identifier, listNameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AST], None]:
376
+ return lambda node: listNameDOTname.append(Make.astAnnAssign(cast(ast.AnnAssign, node).target, cast(ast.AnnAssign, node).annotation, Make.nameDOTname(identifier, cast(ast.Name, cast(ast.AnnAssign, node).target).id)))
377
+
378
+ class FunctionInliner(ast.NodeTransformer):
379
+ def __init__(self, dictionaryFunctions: dict[str, ast.FunctionDef]) -> None:
380
+ self.dictionaryFunctions: dict[str, ast.FunctionDef] = dictionaryFunctions
381
+
382
+ def inlineFunctionBody(self, callableTargetName: str) -> ast.FunctionDef:
383
+ inlineDefinition: ast.FunctionDef = self.dictionaryFunctions[callableTargetName]
384
+ # Process nested calls within the inlined function
385
+ for astNode in ast.walk(inlineDefinition):
386
+ self.visit(astNode)
387
+ return inlineDefinition
388
+
389
+ def visit_Call(self, node: ast.Call):
390
+ astCall = self.generic_visit(node)
391
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryFunctions)(astCall):
392
+ inlineDefinition: ast.FunctionDef = self.inlineFunctionBody(cast(ast.Name, cast(ast.Call, astCall).func).id)
393
+
394
+ if (inlineDefinition and inlineDefinition.body):
395
+ statementTerminating: ast.stmt = inlineDefinition.body[-1]
396
+
397
+ if (isinstance(statementTerminating, ast.Return)
398
+ and statementTerminating.value is not None):
399
+ return self.visit(statementTerminating.value)
400
+ elif isinstance(statementTerminating, ast.Expr):
401
+ return self.visit(statementTerminating.value)
402
+ else:
403
+ return ast.Constant(value=None)
404
+ return astCall
405
+
406
+ def visit_Expr(self, node: ast.Expr):
407
+ if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryFunctions)(node.value):
408
+ inlineDefinition: ast.FunctionDef = self.inlineFunctionBody(cast(ast.Name, cast(ast.Call, node.value).func).id)
409
+ return [self.visit(stmt) for stmt in inlineDefinition.body]
410
+ return self.generic_visit(node)
411
+ # TODO When resolving the ledger of imports, remove self-referential imports
412
+
413
+ def extractClassDef(identifier: ast_Identifier, module: ast.Module) -> ast.ClassDef | None:
414
+ sherpa: list[ast.ClassDef] = []
415
+ extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
416
+ extractor.visit(module)
417
+ astClassDef = sherpa[0] if sherpa else None
418
+ return astClassDef
419
+
420
+ def extractFunctionDef(identifier: ast_Identifier, module: ast.Module) -> ast.FunctionDef | None:
421
+ sherpa: list[ast.FunctionDef] = []
422
+ extractor = NodeCollector(ifThis.isFunctionDef_Identifier(identifier), [Then.appendTo(sherpa)])
423
+ extractor.visit(module)
424
+ astClassDef = sherpa[0] if sherpa else None
425
+ return astClassDef