mapFolding 0.8.3__py3-none-any.whl → 0.8.4__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 (30) hide show
  1. mapFolding/__init__.py +2 -2
  2. mapFolding/basecamp.py +11 -5
  3. mapFolding/filesystem.py +134 -109
  4. mapFolding/oeis.py +1 -1
  5. mapFolding/someAssemblyRequired/__init__.py +37 -18
  6. mapFolding/someAssemblyRequired/_theTypes.py +35 -0
  7. mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
  8. mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
  9. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
  10. mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
  11. mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
  12. mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
  13. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +114 -169
  14. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
  15. mapFolding/someAssemblyRequired/transformDataStructures.py +167 -100
  16. mapFolding/someAssemblyRequired/transformationTools.py +63 -685
  17. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
  18. mapFolding/theDao.py +13 -11
  19. mapFolding/theSSOT.py +69 -112
  20. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/METADATA +2 -1
  21. mapfolding-0.8.4.dist-info/RECORD +49 -0
  22. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/WHEEL +1 -1
  23. tests/conftest.py +34 -29
  24. tests/test_computations.py +40 -31
  25. tests/test_filesystem.py +3 -3
  26. mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
  27. mapfolding-0.8.3.dist-info/RECORD +0 -43
  28. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/entry_points.txt +0 -0
  29. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/licenses/LICENSE +0 -0
  30. {mapfolding-0.8.3.dist-info → mapfolding-0.8.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,326 @@
1
+ from collections.abc import Callable, Container
2
+ from mapFolding.someAssemblyRequired import ast_expr_Slice, ast_Identifier, astClassHasDOTnameNotName, astClassHasDOTtarget, astClassHasDOTvalue, ImaAnnotationType, typeCertified
3
+ from typing import Any, overload, TypeGuard
4
+ import ast
5
+
6
+ Ima_targetType = ast.AST
7
+
8
+ class 又:
9
+ @staticmethod
10
+ @overload
11
+ def annotation(predicate: Callable[[ImaAnnotationType], ast.AST | ast_Identifier]) -> Callable[[ast.AnnAssign | ast.arg], ast.AST | ast_Identifier]:...
12
+ @staticmethod
13
+ @overload
14
+ def annotation(predicate: Callable[[ImaAnnotationType], TypeGuard[ImaAnnotationType] | bool]) -> Callable[[ast.AnnAssign | ast.arg], TypeGuard[ast.AnnAssign] | TypeGuard[ast.arg] | bool]:...
15
+ @staticmethod
16
+ def annotation(predicate: Callable[[ImaAnnotationType], TypeGuard[ImaAnnotationType] | ast.AST | ast_Identifier | bool]) -> Callable[[ast.AnnAssign | ast.arg], TypeGuard[ast.AnnAssign] | TypeGuard[ast.arg] | ast.AST | ast_Identifier | bool]:
17
+ @overload
18
+ def workhorse(node: ast.AnnAssign | ast.arg) -> ast.AST | ast_Identifier:...
19
+ @overload
20
+ def workhorse(node: ast.AnnAssign | ast.arg) -> TypeGuard[ast.AnnAssign] | TypeGuard[ast.arg] | bool:...
21
+ def workhorse(node: ast.AnnAssign | ast.arg) -> TypeGuard[ast.AnnAssign] | TypeGuard[ast.arg] | ast.AST | ast_Identifier | bool:
22
+ ImaAnnotation = node.annotation
23
+ if ImaAnnotation is None: return False
24
+ assert be.Attribute(ImaAnnotation) or be.Constant(ImaAnnotation) or be.Name(ImaAnnotation) or be.Subscript(ImaAnnotation)
25
+ # assert be.Annotation(ImaAnnotation)
26
+ return predicate(ImaAnnotation)
27
+ return workhorse
28
+ @staticmethod
29
+ @overload
30
+ def arg(predicate: Callable[[ast_Identifier], ast.AST | ast_Identifier]) -> Callable[[ast.arg | ast.keyword], ast.AST | ast_Identifier]:...
31
+ @staticmethod
32
+ @overload
33
+ def arg(predicate: Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]) -> Callable[[ast.arg | ast.keyword], TypeGuard[ast.arg] | TypeGuard[ast.keyword] | bool]:...
34
+ @staticmethod
35
+ def arg(predicate: Callable[[ast_Identifier], TypeGuard[ast_Identifier] | ast.AST | ast_Identifier | bool]) -> Callable[[ast.arg | ast.keyword], TypeGuard[ast.arg] | TypeGuard[ast.keyword] | ast.AST | ast_Identifier | bool]:
36
+ @overload
37
+ def workhorse(node: ast.arg | ast.keyword) -> ast.AST | ast_Identifier:...
38
+ @overload
39
+ def workhorse(node: ast.arg | ast.keyword) -> TypeGuard[ast.arg] | TypeGuard[ast.keyword] | bool:...
40
+ def workhorse(node: ast.arg | ast.keyword) -> TypeGuard[ast.arg] | TypeGuard[ast.keyword] | ast.AST | ast_Identifier | bool:
41
+ Ima_arg = node.arg
42
+ if Ima_arg is None: return False
43
+ return predicate(Ima_arg)
44
+ return workhorse
45
+ @staticmethod
46
+ def asname(predicate: Callable[[ast_Identifier | None], TypeGuard[ast_Identifier] | bool]) -> Callable[[ast.alias], TypeGuard[ast.alias] | bool]:
47
+ return lambda node: predicate(node.asname)
48
+ @staticmethod
49
+ def attr(predicate: Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]) -> Callable[[ast.Attribute], TypeGuard[ast.Attribute] | bool]:
50
+ return lambda node: predicate(node.attr)
51
+ @staticmethod
52
+ def func(predicate: Callable[[ast.AST], TypeGuard[ast.AST] | bool]) -> Callable[[ast.Call], TypeGuard[ast.Call] | bool]:
53
+ return lambda node: predicate(node.func)
54
+ @staticmethod
55
+ def id(predicate: Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]) -> Callable[[ast.Name], TypeGuard[ast.Name] | bool]:
56
+ return lambda node: predicate(node.id)
57
+ @staticmethod
58
+ def module(predicate: Callable[[ast_Identifier | None], TypeGuard[ast_Identifier] | bool]) -> Callable[[ast.ImportFrom], TypeGuard[ast.ImportFrom] | bool]:
59
+ return lambda node: predicate(node.module)
60
+ @staticmethod
61
+ def name(predicate: Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]) -> Callable[[astClassHasDOTnameNotName], TypeGuard[astClassHasDOTnameNotName] | bool]:
62
+ return lambda node: predicate(node.name)
63
+ @staticmethod
64
+ def slice(predicate: Callable[[ast_expr_Slice], TypeGuard[ast_expr_Slice] | bool]) -> Callable[[ast.Subscript], TypeGuard[ast.Subscript] | bool]:
65
+ return lambda node: predicate(node.slice)
66
+ @staticmethod
67
+ def target(predicate: Callable[[ast.AST], TypeGuard[ast.AST] | bool]) -> Callable[[astClassHasDOTtarget], TypeGuard[astClassHasDOTtarget] | bool]:
68
+ return lambda node: predicate(node.target)
69
+ @staticmethod
70
+ def value(predicate: Callable[[ast.AST], TypeGuard[ast.AST] | bool]) -> Callable[[astClassHasDOTvalue], TypeGuard[astClassHasDOTvalue] | bool]:
71
+ def workhorse(node: astClassHasDOTvalue) -> TypeGuard[astClassHasDOTvalue] | bool:
72
+ ImaValue = node.value
73
+ if ImaValue is None: return False
74
+ return predicate(ImaValue)
75
+ return workhorse
76
+
77
+ class be:
78
+ @staticmethod
79
+ def _typeCertified(antecedent: type[typeCertified]) -> Callable[[Any | None], TypeGuard[typeCertified]]:
80
+ def workhorse(node: Any | None) -> TypeGuard[typeCertified]:
81
+ return isinstance(node, antecedent)
82
+ return workhorse
83
+ @staticmethod
84
+ def AnnAssign(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.AnnAssign)(node)
85
+ # 'TypeVar "typeCertified" appears only once in generic function signature. Use "object" instead Pylance(reportInvalidTypeVarUse)"' HOW THE FUCK IS THAT INVALID WHEN IT IS WORKING PERFECTLY TO PASS THE TYPE INFORMATION--IN YOUR FUCKING STATIC TYPE CHECKER, PYLANCE!!!! Fuck you, and fuck your pretentious language.
86
+ @staticmethod
87
+ def arg(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.arg)(node)
88
+
89
+ # @staticmethod
90
+ # def Annotation(node: ast.AST) -> TypeGuard[object] | bool:
91
+ # if be.Attribute(node):
92
+ # return be.Attribute(node)
93
+ # elif be.Constant(node):
94
+ # return be.Constant(node)
95
+ # elif be.Name(node):
96
+ # return be.Name(node)
97
+ # elif be.Subscript(node):
98
+ # return be.Subscript(node)
99
+ # else:
100
+ # return False
101
+ # return be.Attribute(node) or be.Constant(node) or be.Name(node) or be.Subscript(node)
102
+
103
+ @staticmethod
104
+ def Assign(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Assign)(node)
105
+ @staticmethod
106
+ def Attribute(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Attribute)(node)
107
+ @staticmethod
108
+ def AugAssign(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.AugAssign)(node)
109
+ @staticmethod
110
+ def BoolOp(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.BoolOp)(node)
111
+ @staticmethod
112
+ def Call(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Call)(node)
113
+ @staticmethod
114
+ def ClassDef(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.ClassDef)(node)
115
+ @staticmethod
116
+ def Compare(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Compare)(node)
117
+ @staticmethod
118
+ def Constant(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Constant)(node)
119
+ @staticmethod
120
+ def Expr(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Expr)(node)
121
+ @staticmethod
122
+ def FunctionDef(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.FunctionDef)(node)
123
+ @staticmethod
124
+ def Import(node: ast.AST) -> TypeGuard[ast.Import]: return be._typeCertified(ast.Import)(node)
125
+ @staticmethod
126
+ def ImportFrom(node: ast.AST) -> TypeGuard[ast.ImportFrom]: return be._typeCertified(ast.ImportFrom)(node)
127
+ @staticmethod
128
+ def keyword(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.keyword)(node)
129
+ @staticmethod
130
+ def Module(node: ast.AST) -> TypeGuard[typeCertified]: return be._typeCertified(ast.Module)(node)
131
+ @staticmethod
132
+ def Name(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Name)(node)
133
+ @staticmethod
134
+ def Return(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Return)(node)
135
+ @staticmethod
136
+ def Starred(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Starred)(node)
137
+ @staticmethod
138
+ def Subscript(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.Subscript)(node)
139
+ @staticmethod
140
+ def UnaryOp(node: ast.AST) -> TypeGuard[object]: return be._typeCertified(ast.UnaryOp)(node)
141
+
142
+ class ifThis:
143
+ @staticmethod
144
+ def equals(this: Any) -> Callable[[Any], TypeGuard[Any] | bool]:
145
+ return lambda node: node == this
146
+ @staticmethod
147
+ def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[object] | bool]:
148
+ """node is Assign and node.targets[0] matches `targets0Predicate`."""
149
+ return lambda node: be.Assign(node) and targets0Predicate(node.targets[0])
150
+ @staticmethod
151
+ def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[object] | bool]:
152
+ """node is ast.Assign and node.value matches `valuePredicate`.
153
+ Parameters:
154
+ valuePredicate: Function that evaluates the value of the assignment
155
+ Returns:
156
+ predicate: matches assignments with values meeting the criteria
157
+ """
158
+ return lambda node: be.Assign(node) and 又.value(valuePredicate)(node)
159
+ @staticmethod
160
+ def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
161
+ return lambda node: be.FunctionDef(node) and 又.name(ifThis._Identifier(identifier))(node)
162
+ @staticmethod
163
+ def isArgument_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
164
+ return lambda node: (be.arg(node) or be.keyword(node)) and 又.arg(ifThis._Identifier(identifier))(node)
165
+ @staticmethod
166
+ def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
167
+ """see also `isArgument_Identifier`"""
168
+ return lambda node: be.keyword(node) and 又.arg(ifThis._Identifier(identifier))(node)
169
+ @staticmethod
170
+ def is_arg_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
171
+ """see also `isArgument_Identifier`"""
172
+ return lambda node: be.arg(node) and 又.arg(ifThis._Identifier(identifier))(node)
173
+ @staticmethod
174
+ def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
175
+ return lambda node: be.ClassDef(node) and 又.name(ifThis._Identifier(identifier))(node)
176
+ @staticmethod
177
+ def isAssignAndValueIsCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
178
+ return lambda node: be.Assign(node) and 又.value(ifThis.isCall_Identifier(identifier))(node)
179
+ @staticmethod
180
+ def isAssignAndValueIsCallAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
181
+ return ifThis.isAssignAndValueIs(ifThis.isCallAttributeNamespace_Identifier(namespace, identifier))
182
+ @staticmethod
183
+ def is_keywordAndValueIsConstant(node: ast.AST) -> TypeGuard[object]:
184
+ return be.keyword(node) and 又.value(be.Constant)(node)
185
+ @staticmethod
186
+ def is_keyword_IdentifierEqualsConstantValue(identifier: ast_Identifier, ConstantValue: Any) -> Callable[[ast.AST], TypeGuard[object] | bool]:
187
+ return lambda node: ifThis.is_keyword_Identifier(identifier)(node) and ifThis.is_keywordAndValueIsConstant(node) and 又.value(ifThis.isConstantEquals(ConstantValue))(node)
188
+ """
189
+ Argument of type "typeCertified@isAnnAssign_targetIs" cannot be assigned to parameter of type "astClassHasDOTtarget"
190
+ Type "typeCertified@isAnnAssign_targetIs" is not assignable to type "astClassHasDOTtarget"
191
+ "object*" is not assignable to "AnnAssign"
192
+ "object*" is not assignable to "AsyncFor"
193
+ "object*" is not assignable to "AugAssign"
194
+ "object*" is not assignable to "comprehension"
195
+ "object*" is not assignable to "For"
196
+ "object*" is not assignable to "NamedExpr"
197
+ """
198
+ @staticmethod
199
+ def isAnnAssign_targetIs(targetPredicate: Callable[[Ima_targetType], TypeGuard[Ima_targetType] | bool]) -> Callable[[ast.AST], TypeGuard[object] | bool]:
200
+ def workhorse(node: ast.AST) -> TypeGuard[object] | bool:
201
+ return be.AnnAssign(node) and 又.target(targetPredicate)(node)
202
+ return workhorse
203
+ @staticmethod
204
+ def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[object] | bool:
205
+ return be.AnnAssign(node) and 又.annotation(be.Name)(node)
206
+ @staticmethod
207
+ def isAugAssign_targetIs(targetPredicate: Callable[[Ima_targetType], TypeGuard[Ima_targetType] | bool]) -> Callable[[ast.AST], TypeGuard[typeCertified] | bool]:
208
+ def workhorse(node: ast.AST) -> TypeGuard[typeCertified] | bool:
209
+ return be.AugAssign(node) and 又.target(targetPredicate)(node)
210
+ return workhorse
211
+
212
+ @staticmethod
213
+ def isAnyCompare(node: ast.AST) -> TypeGuard[object]:
214
+ return be.Compare(node) or be.BoolOp(node)
215
+ @staticmethod
216
+ def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[object] | bool]:
217
+ return lambda node: be.Constant(node) and 又.value(ifThis.equals(value))(node)
218
+ @staticmethod
219
+ def isReturnAnyCompare(node: ast.AST) -> TypeGuard[object] | bool:
220
+ return be.Return(node) and 又.value(ifThis.isAnyCompare)(node)
221
+ @staticmethod
222
+ def isReturnUnaryOp(node: ast.AST) -> TypeGuard[object] | bool:
223
+ return be.Return(node) and 又.value(be.UnaryOp)(node)
224
+
225
+ # ================================================================
226
+ # Nested identifier
227
+ @staticmethod
228
+ def _nestedJunction_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
229
+ def workhorse(node: ast.AST) -> TypeGuard[object] | bool:
230
+ return ifThis.isName_Identifier(identifier)(node) or ifThis.isAttribute_Identifier(identifier)(node) or ifThis.isSubscript_Identifier(identifier)(node) or ifThis.isStarred_Identifier(identifier)(node)
231
+ return workhorse
232
+ @staticmethod
233
+ def isAttribute_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
234
+ """node is `ast.Attribute` and the top-level `ast.Name` is `identifier`"""
235
+ def workhorse(node: ast.AST) -> TypeGuard[object]:
236
+ return be.Attribute(node) and 又.value(ifThis._nestedJunction_Identifier(identifier))(node)
237
+ return workhorse
238
+ @staticmethod
239
+ def isStarred_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
240
+ """node is `ast.Starred` and the top-level `ast.Name` is `identifier`"""
241
+ def workhorse(node: ast.AST) -> TypeGuard[object]:
242
+ return be.Starred(node) and 又.value(ifThis._nestedJunction_Identifier(identifier))(node)
243
+ return workhorse
244
+ @staticmethod
245
+ def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
246
+ """node is `ast.Subscript` and the top-level `ast.Name` is `identifier`"""
247
+ def workhorse(node: ast.AST) -> TypeGuard[object]:
248
+ return be.Subscript(node) and 又.value(ifThis._nestedJunction_Identifier(identifier))(node)
249
+ return workhorse
250
+ # ================================================================
251
+
252
+ @staticmethod
253
+ def Z0Z_unparseIs(astAST: ast.AST) -> Callable[[ast.AST], bool]:
254
+ def workhorse(node: ast.AST) -> bool: return ast.unparse(node) == ast.unparse(astAST)
255
+ return workhorse
256
+
257
+ # ================================================================
258
+ # NOT used
259
+ # TODO Does this work?
260
+ @staticmethod
261
+ def Z0Z_matchesAtLeast1Descendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
262
+ """Create a predicate that returns True if any descendant of the node matches the given predicate."""
263
+ return lambda node: not ifThis.matchesNoDescendant(predicate)(node)
264
+ # ================================================================
265
+ # MORE function inlining
266
+ @staticmethod
267
+ def onlyReturnAnyCompare(astFunctionDef: ast.AST) -> TypeGuard[object]:
268
+ return be.FunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnAnyCompare(astFunctionDef.body[0])
269
+ # For function inlining
270
+ @staticmethod
271
+ def onlyReturnUnaryOp(astFunctionDef: ast.AST) -> TypeGuard[object]:
272
+ return be.FunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnUnaryOp(astFunctionDef.body[0])
273
+ # ================================================================
274
+ # These are used by other functions
275
+ @staticmethod
276
+ def isCallAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
277
+ return lambda node: be.Call(node) and 又.func(ifThis.isAttributeNamespace_Identifier(namespace, identifier))(node)
278
+ @staticmethod
279
+ def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
280
+ return lambda node: be.Name(node) and 又.id(ifThis._Identifier(identifier))(node)
281
+ @staticmethod
282
+ def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
283
+ return lambda node: be.Call(node) and 又.func(ifThis.isName_Identifier(identifier))(node)
284
+ # ================================================================
285
+ @staticmethod
286
+ def matchesMeButNotAnyDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
287
+ """Create a predicate that returns True if the node matches but none of its descendants match the predicate."""
288
+ return lambda node: predicate(node) and ifThis.matchesNoDescendant(predicate)(node)
289
+ @staticmethod
290
+ def matchesNoDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
291
+ """Create a predicate that returns True if no descendant of the node matches the given predicate."""
292
+ def workhorse(node: ast.AST) -> bool:
293
+ for descendant in ast.walk(node):
294
+ if descendant is not node and predicate(descendant):
295
+ return False
296
+ return True
297
+ return workhorse
298
+
299
+ @staticmethod
300
+ def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
301
+ """If `namespace` is not applicable to your case, then call with `namespace=""`."""
302
+ return lambda node: ifThis.matchesMeButNotAnyDescendant(ifThis.CallReallyIs(namespace, identifier))(node)
303
+ @staticmethod
304
+ def CallReallyIs(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
305
+ return ifThis.isCall_Identifier(identifier) or ifThis.isCallAttributeNamespace_Identifier(namespace, identifier)
306
+ @staticmethod
307
+ def isAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[object] | bool]:
308
+ return lambda node: ifThis.isAttributeName(node) and 又.value(ifThis.isName_Identifier(namespace))(node) and 又.attr(ifThis._Identifier(identifier))(node)
309
+ @staticmethod
310
+ def _Identifier(identifier: ast_Identifier) -> Callable[[ast_Identifier | None], TypeGuard[ast_Identifier] | bool]:
311
+ return lambda node: node == identifier
312
+ @staticmethod
313
+ def isAttributeName(node: ast.AST) -> TypeGuard[object]:
314
+ """ Displayed as Name.attribute."""
315
+ return be.Attribute(node) and 又.value(be.Name)(node)
316
+
317
+ @staticmethod
318
+ def isCallToName(node: ast.AST) -> TypeGuard[object]:
319
+ return be.Call(node) and 又.func(be.Name)(node)
320
+ @staticmethod
321
+ def ast_IdentifierIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]:
322
+ return lambda node: node in container
323
+ # This bullshit is for the crappy function inliner I made.
324
+ @staticmethod
325
+ def CallDoesNotCallItselfAndNameDOTidIsIn(container: Container[ast_Identifier]) -> Callable[[ast.AST], TypeGuard[object] | bool]:
326
+ return lambda node: ifThis.isCallToName(node) and 又.func(又.id(ifThis.ast_IdentifierIn(container)))(node) and ifThis.CallDoesNotCallItself("", node.func.id)(node)
@@ -0,0 +1,306 @@
1
+ """
2
+ Container classes for AST transformations and code synthesis.
3
+
4
+ This module provides container classes used in the AST transformation process
5
+ and code synthesis workflows. It acts as a dependency boundary to prevent
6
+ circular imports while providing reusable data structures.
7
+ """
8
+ from collections import defaultdict
9
+ from collections.abc import Sequence
10
+ from mapFolding.someAssemblyRequired import ast_Identifier, be, ifThis, Make, parseLogicalPath2astModule, str_nameDOTname
11
+ from mapFolding.theSSOT import callableDispatcherHARDCODED, raiseIfNoneGitHubIssueNumber3, The
12
+ from pathlib import Path, PurePosixPath
13
+ from Z0Z_tools import updateExtendPolishDictionaryLists
14
+ import ast
15
+ import dataclasses
16
+
17
+ class LedgerOfImports:
18
+ # TODO When resolving the ledger of imports, remove self-referential imports
19
+ # TODO TypeIgnore :/
20
+
21
+ def __init__(self, startWith: ast.AST | None = None) -> None:
22
+ self.dictionaryImportFrom: dict[str_nameDOTname, list[tuple[ast_Identifier, ast_Identifier | None]]] = defaultdict(list)
23
+ self.listImport: list[str_nameDOTname] = []
24
+ if startWith:
25
+ self.walkThis(startWith)
26
+
27
+ def addAst(self, astImport____: ast.Import | ast.ImportFrom) -> None:
28
+ assert isinstance(astImport____, (ast.Import, ast.ImportFrom)), f"I received {type(astImport____) = }, but I can only accept {ast.Import} and {ast.ImportFrom}."
29
+ if be.Import(astImport____):
30
+ for alias in astImport____.names:
31
+ self.listImport.append(alias.name)
32
+ elif be.ImportFrom(astImport____):
33
+ # TODO fix the mess created by `None` means '.'. I need a `str_nameDOTname` to replace '.'
34
+ if astImport____.module is None:
35
+ astImport____.module = '.'
36
+ for alias in astImport____.names:
37
+ self.dictionaryImportFrom[astImport____.module].append((alias.name, alias.asname))
38
+
39
+ def addImport_asStr(self, moduleIdentifier: str_nameDOTname) -> None:
40
+ self.listImport.append(moduleIdentifier)
41
+
42
+ def addImportFrom_asStr(self, moduleIdentifier: ast_Identifier, name: ast_Identifier, asname: ast_Identifier | None = None) -> None:
43
+ self.dictionaryImportFrom[moduleIdentifier].append((name, asname))
44
+
45
+ def exportListModuleIdentifiers(self) -> list[ast_Identifier]:
46
+ listModuleIdentifiers: list[ast_Identifier] = list(self.dictionaryImportFrom.keys())
47
+ listModuleIdentifiers.extend(self.listImport)
48
+ return sorted(set(listModuleIdentifiers))
49
+
50
+ def makeList_ast(self) -> list[ast.ImportFrom | ast.Import]:
51
+ listImportFrom: list[ast.ImportFrom] = []
52
+ for moduleIdentifier, listOfNameTuples in sorted(self.dictionaryImportFrom.items()):
53
+ listOfNameTuples = sorted(list(set(listOfNameTuples)), key=lambda nameTuple: nameTuple[0])
54
+ list_alias: list[ast.alias] = []
55
+ for name, asname in listOfNameTuples:
56
+ list_alias.append(Make.alias(name, asname))
57
+ listImportFrom.append(Make.ImportFrom(moduleIdentifier, list_alias))
58
+ list_astImport: list[ast.Import] = [Make.Import(moduleIdentifier) for moduleIdentifier in sorted(set(self.listImport))]
59
+ return listImportFrom + list_astImport
60
+
61
+ def update(self, *fromLedger: 'LedgerOfImports') -> None:
62
+ """Update this ledger with imports from one or more other ledgers.
63
+ Parameters:
64
+ *fromLedger: One or more other `LedgerOfImports` objects from which to merge.
65
+ """
66
+ self.dictionaryImportFrom = updateExtendPolishDictionaryLists(self.dictionaryImportFrom, *(ledger.dictionaryImportFrom for ledger in fromLedger), destroyDuplicates=True, reorderLists=True)
67
+ for ledger in fromLedger:
68
+ self.listImport.extend(ledger.listImport)
69
+
70
+ def walkThis(self, walkThis: ast.AST) -> None:
71
+ for nodeBuffalo in ast.walk(walkThis):
72
+ if isinstance(nodeBuffalo, (ast.Import, ast.ImportFrom)):
73
+ self.addAst(nodeBuffalo)
74
+
75
+ @dataclasses.dataclass
76
+ class IngredientsFunction:
77
+ """Everything necessary to integrate a function into a module should be here.
78
+ Parameters:
79
+ astFunctionDef: hint `Make.astFunctionDef()`
80
+ """
81
+ astFunctionDef: ast.FunctionDef
82
+ imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
83
+ type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
84
+
85
+ @dataclasses.dataclass
86
+ class IngredientsModule:
87
+ """Everything necessary to create one _logical_ `ast.Module` should be here.
88
+ Extrinsic qualities should _probably_ be handled externally.
89
+
90
+ Parameters:
91
+ ingredientsFunction (None): One or more `IngredientsFunction` that will appended to `listIngredientsFunctions`.
92
+ """
93
+ ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
94
+
95
+ # init var with an existing module? method to deconstruct an existing module?
96
+
97
+ # `body` attribute of `ast.Module`
98
+ """NOTE
99
+ - Bare statements in `prologue` and `epilogue` are not 'protected' by `if __name__ == '__main__':` so they will be executed merely by loading the module.
100
+ - The dataclass has methods for modifying `prologue`, `epilogue`, and `launcher`.
101
+ - However, `prologue`, `epilogue`, and `launcher` are `ast.Module` (as opposed to `list[ast.stmt]`), so that you may use tools such as `ast.walk` and `ast.NodeVisitor` on the fields.
102
+ """
103
+ imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
104
+ """Modify this field using the methods in `LedgerOfImports`."""
105
+ prologue: ast.Module = Make.Module([],[])
106
+ """Statements after the imports and before the functions in listIngredientsFunctions."""
107
+ listIngredientsFunctions: list[IngredientsFunction] = dataclasses.field(default_factory=list)
108
+ epilogue: ast.Module = Make.Module([],[])
109
+ """Statements after the functions in listIngredientsFunctions and before `launcher`."""
110
+ launcher: ast.Module = Make.Module([],[])
111
+ """`if __name__ == '__main__':`"""
112
+
113
+ # `ast.TypeIgnore` statements to supplement those in other fields; `type_ignores` is a parameter for `ast.Module` constructor
114
+ supplemental_type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
115
+
116
+ def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
117
+ if ingredientsFunction is not None:
118
+ if isinstance(ingredientsFunction, IngredientsFunction):
119
+ self.appendIngredientsFunction(ingredientsFunction)
120
+ else:
121
+ self.appendIngredientsFunction(*ingredientsFunction)
122
+
123
+ def _append_astModule(self, self_astModule: ast.Module, astModule: ast.Module | None, statement: Sequence[ast.stmt] | ast.stmt | None, type_ignores: list[ast.TypeIgnore] | None) -> None:
124
+ """Append one or more statements to `prologue`."""
125
+ list_body: list[ast.stmt] = []
126
+ listTypeIgnore: list[ast.TypeIgnore] = []
127
+ if astModule is not None and be.Module(astModule):
128
+ list_body.extend(astModule.body)
129
+ listTypeIgnore.extend(astModule.type_ignores)
130
+ if type_ignores is not None:
131
+ listTypeIgnore.extend(type_ignores)
132
+ if statement is not None:
133
+ if isinstance(statement, Sequence):
134
+ list_body.extend(statement)
135
+ else:
136
+ list_body.append(statement)
137
+ self_astModule.body.extend(list_body)
138
+ self_astModule.type_ignores.extend(listTypeIgnore)
139
+ ast.fix_missing_locations(self_astModule)
140
+
141
+ def appendPrologue(self, astModule: ast.Module | None = None, statement: Sequence[ast.stmt] | ast.stmt | None = None, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
142
+ """Append one or more statements to `prologue`."""
143
+ self._append_astModule(self.prologue, astModule, statement, type_ignores)
144
+
145
+ def appendEpilogue(self, astModule: ast.Module | None = None, statement: Sequence[ast.stmt] | ast.stmt | None = None, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
146
+ """Append one or more statements to `epilogue`."""
147
+ self._append_astModule(self.epilogue, astModule, statement, type_ignores)
148
+
149
+ def appendLauncher(self, astModule: ast.Module | None = None, statement: Sequence[ast.stmt] | ast.stmt | None = None, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
150
+ """Append one or more statements to `launcher`."""
151
+ self._append_astModule(self.launcher, astModule, statement, type_ignores)
152
+
153
+ def appendIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
154
+ """Append one or more `IngredientsFunction`."""
155
+ for allegedIngredientsFunction in ingredientsFunction:
156
+ if isinstance(allegedIngredientsFunction, IngredientsFunction):
157
+ self.listIngredientsFunctions.append(allegedIngredientsFunction)
158
+ else:
159
+ raise ValueError(f"I received `{type(allegedIngredientsFunction) = }`, but I can only accept `{IngredientsFunction}`.")
160
+
161
+ @property
162
+ def list_astImportImportFrom(self) -> list[ast.Import | ast.ImportFrom]:
163
+ """List of `ast.Import` and `ast.ImportFrom` statements."""
164
+ sherpaLedger = LedgerOfImports()
165
+ listLedgers: list[LedgerOfImports] = [self.imports]
166
+ for ingredientsFunction in self.listIngredientsFunctions:
167
+ listLedgers.append(ingredientsFunction.imports)
168
+ sherpaLedger.update(*listLedgers)
169
+ return sherpaLedger.makeList_ast()
170
+
171
+ @property
172
+ def body(self) -> list[ast.stmt]:
173
+ list_stmt: list[ast.stmt] = []
174
+ list_stmt.extend(self.list_astImportImportFrom)
175
+ list_stmt.extend(self.prologue.body)
176
+ for ingredientsFunction in self.listIngredientsFunctions:
177
+ list_stmt.append(ingredientsFunction.astFunctionDef)
178
+ list_stmt.extend(self.epilogue.body)
179
+ list_stmt.extend(self.launcher.body)
180
+ # TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
181
+ return list_stmt
182
+
183
+ @property
184
+ def type_ignores(self) -> list[ast.TypeIgnore]:
185
+ listTypeIgnore: list[ast.TypeIgnore] = self.supplemental_type_ignores
186
+ # listTypeIgnore.extend(self.imports.makeListAst())
187
+ listTypeIgnore.extend(self.prologue.type_ignores)
188
+ for ingredientsFunction in self.listIngredientsFunctions:
189
+ listTypeIgnore.extend(ingredientsFunction.type_ignores)
190
+ listTypeIgnore.extend(self.epilogue.type_ignores)
191
+ listTypeIgnore.extend(self.launcher.type_ignores)
192
+ return listTypeIgnore
193
+
194
+ @dataclasses.dataclass
195
+ class RecipeSynthesizeFlow:
196
+ """Settings for synthesizing flow."""
197
+ # ========================================
198
+ # Source
199
+ # ========================================
200
+ source_astModule = parseLogicalPath2astModule(The.logicalPathModuleSourceAlgorithm)
201
+
202
+ # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
203
+ sourceCallableDispatcher: ast_Identifier = The.sourceCallableDispatcher
204
+ sourceCallableInitialize: ast_Identifier = The.sourceCallableInitialize
205
+ sourceCallableParallel: ast_Identifier = The.sourceCallableParallel
206
+ sourceCallableSequential: ast_Identifier = The.sourceCallableSequential
207
+
208
+ sourceDataclassIdentifier: ast_Identifier = The.dataclassIdentifier
209
+ sourceDataclassInstance: ast_Identifier = The.dataclassInstance
210
+ sourceDataclassInstanceTaskDistribution: ast_Identifier = The.dataclassInstanceTaskDistribution
211
+ sourceLogicalPathModuleDataclass: str_nameDOTname = The.logicalPathModuleDataclass
212
+
213
+ sourceConcurrencyManagerNamespace = The.sourceConcurrencyManagerNamespace
214
+ sourceConcurrencyManagerIdentifier = The.sourceConcurrencyManagerIdentifier
215
+
216
+ # ========================================
217
+ # Logical identifiers (as opposed to physical identifiers)
218
+ # ========================================
219
+ # Package ================================
220
+ packageIdentifier: ast_Identifier | None = The.packageName
221
+
222
+ # Qualified logical path ================================
223
+ logicalPathModuleDataclass: str_nameDOTname = sourceLogicalPathModuleDataclass
224
+ logicalPathFlowRoot: ast_Identifier | None = 'syntheticModules'
225
+ """ `logicalPathFlowRoot` likely corresponds to a physical filesystem directory."""
226
+
227
+ # Module ================================
228
+ moduleDispatcher: ast_Identifier = 'numbaCount_doTheNeedful'
229
+ moduleInitialize: ast_Identifier = moduleDispatcher
230
+ moduleParallel: ast_Identifier = moduleDispatcher
231
+ moduleSequential: ast_Identifier = moduleDispatcher
232
+
233
+ # Function ================================
234
+ callableDispatcher: ast_Identifier = sourceCallableDispatcher
235
+ callableInitialize: ast_Identifier = sourceCallableInitialize
236
+ callableParallel: ast_Identifier = sourceCallableParallel
237
+ callableSequential: ast_Identifier = sourceCallableSequential
238
+ concurrencyManagerNamespace: ast_Identifier = sourceConcurrencyManagerNamespace
239
+ concurrencyManagerIdentifier: ast_Identifier = sourceConcurrencyManagerIdentifier
240
+ dataclassIdentifier: ast_Identifier = sourceDataclassIdentifier
241
+
242
+ # Variable ================================
243
+ dataclassInstance: ast_Identifier = sourceDataclassInstance
244
+ dataclassInstanceTaskDistribution: ast_Identifier = sourceDataclassInstanceTaskDistribution
245
+
246
+ # ========================================
247
+ # Computed
248
+ # ========================================
249
+ """
250
+ theFormatStrModuleSynthetic = "{packageFlow}Count"
251
+ theFormatStrModuleForCallableSynthetic = theFormatStrModuleSynthetic + "_{callableTarget}"
252
+ theModuleDispatcherSynthetic: ast_Identifier = theFormatStrModuleForCallableSynthetic.format(packageFlow=packageFlowSynthetic, callableTarget=The.sourceCallableDispatcher)
253
+ theLogicalPathModuleDispatcherSynthetic: str = '.'.join([The.packageName, The.moduleOfSyntheticModules, theModuleDispatcherSynthetic])
254
+
255
+ """
256
+ # logicalPathModuleDispatcher: str = '.'.join([Z0Z_flowLogicalPathRoot, moduleDispatcher])
257
+ # ========================================
258
+ # Filesystem (names of physical objects)
259
+ # ========================================
260
+ pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
261
+ fileExtension: str = The.fileExtension
262
+
263
+ def _makePathFilename(self, filenameStem: str,
264
+ pathRoot: PurePosixPath | None = None,
265
+ logicalPathINFIX: str_nameDOTname | None = None,
266
+ fileExtension: str | None = None,
267
+ ) -> PurePosixPath:
268
+ """filenameStem: (hint: the name of the logical module)"""
269
+ if pathRoot is None:
270
+ pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
271
+ if logicalPathINFIX:
272
+ whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
273
+ pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
274
+ if fileExtension is None:
275
+ fileExtension = self.fileExtension
276
+ filename: str = filenameStem + fileExtension
277
+ return pathRoot.joinpath(filename)
278
+
279
+ @property
280
+ def pathFilenameDispatcher(self) -> PurePosixPath:
281
+ return self._makePathFilename(filenameStem=self.moduleDispatcher, logicalPathINFIX=self.logicalPathFlowRoot)
282
+ @property
283
+ def pathFilenameInitialize(self) -> PurePosixPath:
284
+ return self._makePathFilename(filenameStem=self.moduleInitialize, logicalPathINFIX=self.logicalPathFlowRoot)
285
+ @property
286
+ def pathFilenameParallel(self) -> PurePosixPath:
287
+ return self._makePathFilename(filenameStem=self.moduleParallel, logicalPathINFIX=self.logicalPathFlowRoot)
288
+ @property
289
+ def pathFilenameSequential(self) -> PurePosixPath:
290
+ return self._makePathFilename(filenameStem=self.moduleSequential, logicalPathINFIX=self.logicalPathFlowRoot)
291
+
292
+ def __post_init__(self) -> None:
293
+ if ((self.concurrencyManagerIdentifier is not None and self.concurrencyManagerIdentifier != self.sourceConcurrencyManagerIdentifier) # `submit` # type: ignore
294
+ or ((self.concurrencyManagerIdentifier is None) != (self.concurrencyManagerNamespace is None))): # type: ignore
295
+ import warnings
296
+ warnings.warn(f"If your synthesized module is weird, check `{self.concurrencyManagerIdentifier=}` and `{self.concurrencyManagerNamespace=}`. (ChildProcessError? 'Yeah! Children shouldn't be processing stuff, man.')", category=ChildProcessError, stacklevel=2) # pyright: ignore[reportCallIssue, reportArgumentType] Y'all Pynatics need to be less shrill and focus on making code that doesn't need 8000 error categories.
297
+
298
+ # self.logicalPathModuleDispatcher!=logicalPathModuleDispatcherHARDCODED or
299
+ if self.callableDispatcher!=callableDispatcherHARDCODED:
300
+ print(f"fyi: `{self.callableDispatcher=}` but\n\t`{callableDispatcherHARDCODED=}`.")
301
+
302
+ def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
303
+ from mapFolding.someAssemblyRequired import extractFunctionDef
304
+ astFunctionDef = extractFunctionDef(astModule, identifierFunctionDef)
305
+ if not astFunctionDef: raise raiseIfNoneGitHubIssueNumber3
306
+ return IngredientsFunction(astFunctionDef, LedgerOfImports(astModule))