mapFolding 0.8.2__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 (37) hide show
  1. mapFolding/__init__.py +6 -2
  2. mapFolding/basecamp.py +11 -5
  3. mapFolding/filesystem.py +134 -109
  4. mapFolding/oeis.py +1 -1
  5. mapFolding/reference/__init__.py +7 -0
  6. mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +197 -0
  7. mapFolding/reference/jobsCompleted/__init__.py +50 -0
  8. mapFolding/reference/jobsCompleted/p2x19/p2x19.py +29 -0
  9. mapFolding/someAssemblyRequired/__init__.py +37 -18
  10. mapFolding/someAssemblyRequired/_theTypes.py +35 -0
  11. mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
  12. mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
  13. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
  14. mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
  15. mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
  16. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +20 -1
  17. mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
  18. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +112 -149
  19. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
  20. mapFolding/someAssemblyRequired/transformDataStructures.py +167 -100
  21. mapFolding/someAssemblyRequired/transformationTools.py +63 -678
  22. mapFolding/syntheticModules/__init__.py +1 -0
  23. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
  24. mapFolding/theDao.py +13 -11
  25. mapFolding/theSSOT.py +69 -119
  26. {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/METADATA +4 -2
  27. mapfolding-0.8.4.dist-info/RECORD +49 -0
  28. {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/WHEEL +1 -1
  29. tests/conftest.py +34 -29
  30. tests/test_computations.py +40 -31
  31. tests/test_filesystem.py +3 -3
  32. tests/test_other.py +4 -3
  33. mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
  34. mapfolding-0.8.2.dist-info/RECORD +0 -39
  35. {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/entry_points.txt +0 -0
  36. {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/licenses/LICENSE +0 -0
  37. {mapfolding-0.8.2.dist-info → mapfolding-0.8.4.dist-info}/top_level.txt +0 -0
@@ -23,642 +23,69 @@ they are designed as general-purpose utilities applicable to a wide range of cod
23
23
  transformation scenarios beyond the scope of this package.
24
24
  """
25
25
  from autoflake import fix_code as autoflake_fix_code
26
- from collections import defaultdict
27
- from collections.abc import Callable, Container, Sequence
26
+ from collections.abc import Callable, Mapping
28
27
  from copy import deepcopy
29
- from importlib import import_module as importlib_import_module
30
- from inspect import getsource as inspect_getsource
31
28
  from mapFolding.filesystem import writeStringToHere
32
- from mapFolding.theSSOT import (
33
- raiseIfNoneGitHubIssueNumber3,
34
- The,
35
- theFormatStrModuleForCallableSynthetic,
36
- theFormatStrModuleSynthetic,
37
- theLogicalPathModuleDispatcherSynthetic,
38
- theModuleDispatcherSynthetic,
39
- )
29
+ from mapFolding.someAssemblyRequired import ast_Identifier, be, ifThis, Make, NodeChanger, NodeTourist, Then, typeCertified
30
+ from mapFolding.someAssemblyRequired._toolboxContainers import IngredientsModule
31
+ from mapFolding.theSSOT import raiseIfNoneGitHubIssueNumber3
40
32
  from os import PathLike
41
- from pathlib import Path, PurePath, PurePosixPath
42
- from types import ModuleType
43
- from typing import Any, cast, Generic, TypeAlias, TypeGuard, TypeVar
44
- from Z0Z_tools import updateExtendPolishDictionaryLists
33
+ from pathlib import PurePath
34
+ from typing import Any
45
35
  import ast
46
- import dataclasses
47
36
 
48
- """
49
- Semiotic notes:
50
- 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.
51
-
52
- astName: always means `ast.Name`.
53
- Name: uppercase, _should_ be interchangeable with astName, even in camelCase.
54
- Hunter: ^^ did you do that ^^ ? Are you sure? You just fixed some that should have been "_name" because it confused you.
55
- 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.
56
- _Identifier: very strongly correlates with the private `ast._Identifier`, which is a TypeAlias for `str`.
57
- identifier: lowercase, a general term that includes the above and other Python identifiers.
58
- Identifier: uppercase, without the leading underscore should only appear in camelCase and means "identifier", lowercase.
59
- namespace: lowercase, in dotted-names, such as `pathlib.Path` or `collections.abc`, "namespace" is the part before the dot.
60
- Namespace: uppercase, should only appear in camelCase and means "namespace", lowercase.
61
- """
62
-
63
- # Would `LibCST` be better than `ast` in some cases? https://github.com/hunterhogan/mapFolding/issues/7
64
-
65
- ast_expr_Slice: TypeAlias = ast.expr
66
- ast_Identifier: TypeAlias = str
67
- astClassHasAttributeDOTname: TypeAlias = ast.FunctionDef | ast.ClassDef | ast.AsyncFunctionDef
68
- astMosDef = TypeVar('astMosDef', bound=astClassHasAttributeDOTname)
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
74
-
75
- class NodeCollector(Generic[nodeType], ast.NodeVisitor):
76
- """A node visitor that collects data via one or more actions when a predicate is met."""
77
- def __init__(self, findThis: Callable[[ast.AST], TypeGuard[nodeType] | bool], doThat: list[Callable[[nodeType], Any]]) -> None:
78
- self.findThis = findThis
79
- self.doThat = doThat
80
-
81
- def visit(self, node: ast.AST) -> None:
82
- if self.findThis(node):
83
- for action in self.doThat:
84
- action(cast(nodeType, node))
85
- self.generic_visit(node)
86
-
87
- class NodeReplacer(Generic[nodeType], ast.NodeTransformer):
88
- """A node transformer that replaces or removes AST nodes based on a condition."""
89
- def __init__(self, findThis: Callable[[ast.AST], TypeGuard[nodeType] | bool], doThat: Callable[[nodeType], ast.AST | Sequence[ast.AST] | None]) -> None:
90
- self.findThis = findThis
91
- self.doThat = doThat
92
-
93
- def visit(self, node: ast.AST) -> ast.AST | Sequence[ast.AST] | None:
94
- if self.findThis(node):
95
- return self.doThat(cast(nodeType, node))
96
- return super().visit(node)
97
-
98
- class ifThis:
99
- @staticmethod
100
- def ast_IdentifierIsIn(container: Container[ast_Identifier]) -> Callable[[ast_Identifier], TypeGuard[ast_Identifier] | bool]:
101
- return lambda node: node in container
102
- @staticmethod
103
- def CallDoesNotCallItself(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
104
- """If `namespace` is not applicable to your case, then call with `namespace=""`."""
105
- return lambda node: ifThis.matchesMeButNotAnyDescendant(ifThis.CallReallyIs(namespace, identifier))(node)
106
- @staticmethod
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)
109
- @staticmethod
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))
112
- @staticmethod
113
- def is_keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
114
- return isinstance(node, ast.keyword)
115
- @staticmethod
116
- def is_keywordAndValueIsConstant(node: ast.AST) -> TypeGuard[ast.keyword]:
117
- return ifThis.is_keyword(node) and ifThis.isConstant(node.value)
118
- @staticmethod
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
123
- @staticmethod
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)
126
- @staticmethod
127
- def isAllOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
128
- return lambda node: all(predicate(node) for predicate in thesePredicates)
129
- @staticmethod
130
- def isAnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
131
- return isinstance(node, ast.AnnAssign)
132
- @staticmethod
133
- def isAnnAssignAndAnnotationIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
134
- return ifThis.isAnnAssign(node) and ifThis.isName(node.annotation)
135
- @staticmethod
136
- def isAnnAssignAndTargetIsName(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
137
- return ifThis.isAnnAssign(node) and ifThis.isName(node.target)
138
- @staticmethod
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)
141
- @staticmethod
142
- def isAnyAssignmentTo(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
143
- return ifThis.isAnyOf(ifThis.isAssignOnlyTo(identifier), ifThis.isAnnAssignTo(identifier), ifThis.isAugAssignTo(identifier))
144
- @staticmethod
145
- def isAnyCompare(node: ast.AST) -> TypeGuard[ast.Compare] | TypeGuard[ast.BoolOp]:
146
- return ifThis.isCompare(node) or ifThis.isBoolOp(node)
147
- @staticmethod
148
- def isAnyOf(*thesePredicates: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
149
- return lambda node: any(predicate(node) for predicate in thesePredicates)
150
- @staticmethod
151
- def isAssign(node: ast.AST) -> TypeGuard[ast.Assign]:
152
- return isinstance(node, ast.Assign)
153
- @staticmethod
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)
156
- @staticmethod
157
- def isAssignAndValueIsCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
158
- return ifThis.isAssignAndValueIs(ifThis.isCallNamespace_Identifier(namespace, identifier))
159
- @staticmethod
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])
162
- @staticmethod
163
- def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
164
- """node is Assign and node.targets[0] matches `targets0Predicate`."""
165
- return lambda node: ifThis.isAssign(node) and targets0Predicate(node.targets[0])
166
- @staticmethod
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`.
169
- Parameters:
170
- valuePredicate: Function that evaluates the value of the assignment
171
- Returns:
172
- predicate: matches assignments with values meeting the criteria
173
- """
174
- return lambda node: ifThis.isAssign(node) and valuePredicate(node.value)
175
- @staticmethod
176
- def isAttribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
177
- return isinstance(node, ast.Attribute)
178
- @staticmethod
179
- def isAugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
180
- return isinstance(node, ast.AugAssign)
181
- @staticmethod
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)
184
- @staticmethod
185
- def isBoolOp(node: ast.AST) -> TypeGuard[ast.BoolOp]:
186
- return isinstance(node, ast.BoolOp)
187
- @staticmethod
188
- def isCall(node: ast.AST) -> TypeGuard[ast.Call]:
189
- return isinstance(node, ast.Call)
190
- @staticmethod
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)
193
- @staticmethod
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)
196
- @staticmethod
197
- def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
198
- return ifThis.isCall(node) and ifThis.isName(node.func)
199
- @staticmethod
200
- def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
201
- return isinstance(node, ast.ClassDef)
202
- @staticmethod
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
205
- @staticmethod
206
- def isCompare(node: ast.AST) -> TypeGuard[ast.Compare]:
207
- return isinstance(node, ast.Compare)
208
- @staticmethod
209
- def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
210
- return isinstance(node, ast.Constant)
211
- @staticmethod
212
- def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
213
- return lambda node: ifThis.isConstant(node) and node.value == value
214
- @staticmethod
215
- def isExpr(node: ast.AST) -> TypeGuard[ast.Expr]:
216
- return isinstance(node, ast.Expr)
217
- @staticmethod
218
- def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
219
- return isinstance(node, ast.FunctionDef)
220
- @staticmethod
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
223
- @staticmethod
224
- def isImport(node: ast.AST) -> TypeGuard[ast.Import]:
225
- return isinstance(node, ast.Import)
226
- @staticmethod
227
- def isName(node: ast.AST) -> TypeGuard[ast.Name]:
228
- """TODO
229
- ast.Name()
230
- ast.Attribute()
231
- ast.Subscript()
232
- ast.Starred()
233
- """
234
- return isinstance(node, ast.Name)
235
- @staticmethod
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
238
- @staticmethod
239
- def is_nameDOTname(node: ast.AST) -> TypeGuard[ast.Attribute]:
240
- return ifThis.isAttribute(node) and ifThis.isName(node.value)
241
- @staticmethod
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)
244
- @staticmethod
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
247
- @staticmethod
248
- def NameReallyIs_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], bool]:
249
- # The following logic is incomplete.
250
- return ifThis.isAnyOf(ifThis.isName_Identifier(identifier), ifThis.isSubscriptIsName_Identifier(identifier))
251
- @staticmethod
252
- def isReturn(node: ast.AST) -> TypeGuard[ast.Return]:
253
- return isinstance(node, ast.Return)
254
- @staticmethod
255
- def isReturnAnyCompare(node: ast.AST) -> TypeGuard[ast.Return]:
256
- return ifThis.isReturn(node) and node.value is not None and ifThis.isAnyCompare(node.value)
257
- @staticmethod
258
- def isReturnUnaryOp(node: ast.AST) -> TypeGuard[ast.Return]:
259
- return ifThis.isReturn(node) and node.value is not None and ifThis.isUnaryOp(node.value)
260
- @staticmethod
261
- def isSubscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
262
- return isinstance(node, ast.Subscript)
263
- @staticmethod
264
- def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript]]:
265
- """node is `ast.Subscript` and the top-level `ast.Name` is `identifier`
266
- Parameters:
267
- identifier: The identifier to look for in the value chain
268
- Returns:
269
- predicate: function that checks if a node matches the criteria
270
- """
271
- def predicate(node: ast.AST) -> TypeGuard[ast.Subscript]:
272
- if not ifThis.isSubscript(node):
273
- return False
274
- def checkNodeDOTvalue(nodeDOTvalue: ast.AST) -> bool:
275
- if ifThis.isName(nodeDOTvalue):
276
- if nodeDOTvalue.id == identifier:
277
- return True
278
- elif hasattr(nodeDOTvalue, "value"):
279
- return checkNodeDOTvalue(nodeDOTvalue.value) # type: ignore
280
- return False
281
- return checkNodeDOTvalue(node.value)
282
- return predicate
283
- @staticmethod
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)
286
- @staticmethod
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)
289
- @staticmethod
290
- def isUnaryOp(node: ast.AST) -> TypeGuard[ast.UnaryOp]:
291
- return isinstance(node, ast.UnaryOp)
292
- # TODO Does this work?
293
- @staticmethod
294
- def matchesAtLeast1Descendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
295
- """Create a predicate that returns True if any descendant of the node matches the given predicate."""
296
- return lambda node: not ifThis.matchesNoDescendant(predicate)(node)
297
- # TODO Does this work?
298
- @staticmethod
299
- def matchesMeAndMyDescendantsExactlyNTimes(predicate: Callable[[ast.AST], bool], nTimes: int) -> Callable[[ast.AST], bool]:
300
- """Create a predicate that returns True if exactly 'count' nodes in the tree match the predicate."""
301
- def countMatchingNodes(node: ast.AST) -> bool:
302
- matches = sum(1 for descendant in ast.walk(node) if predicate(descendant))
303
- return matches == nTimes
304
- return countMatchingNodes
305
- @staticmethod
306
- def matchesMeButNotAnyDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
307
- """Create a predicate that returns True if the node matches but none of its descendants match the predicate."""
308
- return lambda node: predicate(node) and ifThis.matchesNoDescendant(predicate)(node)
309
- @staticmethod
310
- def matchesNoDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
311
- """Create a predicate that returns True if no descendant of the node matches the given predicate."""
312
- def checkNoMatchingDescendant(node: ast.AST) -> bool:
313
- for descendant in ast.walk(node):
314
- if descendant is not node and predicate(descendant):
315
- return False
316
- return True
317
- return checkNoMatchingDescendant
318
- @staticmethod
319
- def onlyReturnAnyCompare(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
320
- return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnAnyCompare(astFunctionDef.body[0])
321
- @staticmethod
322
- def onlyReturnUnaryOp(astFunctionDef: ast.AST) -> TypeGuard[ast.FunctionDef]:
323
- return ifThis.isFunctionDef(astFunctionDef) and len(astFunctionDef.body) == 1 and ifThis.isReturnUnaryOp(astFunctionDef.body[0])
324
-
325
- class Make:
326
- @staticmethod
327
- def ast_arg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: strORintORNone) -> ast.arg:
328
- """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
329
- return ast.arg(identifier, annotation, **keywordArguments)
330
- @staticmethod
331
- def ast_keyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
332
- return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
333
- @staticmethod
334
- def astAlias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
335
- return ast.alias(name, asname)
336
- @staticmethod
337
- def astAnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign:
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."""
339
- return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
340
- @staticmethod
341
- def astAssign(listTargets: Any, value: ast.expr, **keywordArguments: strORintORNone) -> ast.Assign:
342
- """keywordArguments: type_comment:str|None, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
343
- return ast.Assign(targets=listTargets, value=value, **keywordArguments)
344
- @staticmethod
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:
346
- return ast.arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
347
- @staticmethod
348
- def astAttribute(value: ast.expr, attribute: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Attribute:
349
- """
350
- Parameters:
351
- value: the part before the dot (hint `ast.Name` for nameDOTname)
352
- attribute: the `str` after the dot
353
- context (ast.Load()): Load/Store/Del"""
354
- return ast.Attribute(value, attribute, context, **keywordArguments)
355
- @staticmethod
356
- def astCall(caller: ast.Name | ast.Attribute, listArguments: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
357
- return ast.Call(func=caller, args=list(listArguments) if listArguments else [], keywords=list(list_astKeywords) if list_astKeywords else [])
358
- @staticmethod
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:
360
- """keywordArguments: type_params:list[ast.type_param], lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
361
- return ast.ClassDef(name=name, bases=listBases, keywords=list_keyword, body=body, decorator_list=decorator_list, **keywordArguments)
362
- @staticmethod
363
- def astConstant(value: Any, **keywordArguments: strORintORNone) -> ast.Constant:
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.
365
- keywordArguments: kind:str, lineno:int, col_offset:int, end_lineno:int|None, end_col_offset:int|None"""
366
- return ast.Constant(value, **keywordArguments)
367
- @staticmethod
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:
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"""
370
- return ast.FunctionDef(name=name, args=argumentsSpecification, body=body, decorator_list=decorator_list, returns=returns, **keywordArguments)
371
- @staticmethod
372
- def astImport(moduleName: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
373
- return ast.Import(names=[Make.astAlias(moduleName, asname)], **keywordArguments)
374
- @staticmethod
375
- def astImportFrom(moduleName: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
376
- return ast.ImportFrom(module=moduleName, names=list_astAlias, level=0, **keywordArguments)
377
- @staticmethod
378
- def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
379
- return ast.Module(body, type_ignores)
380
- @staticmethod
381
- def astName(identifier: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Name:
382
- return ast.Name(identifier, context, **keywordArguments)
383
- @staticmethod
384
- def itDOTname(nameChain: ast.Name | ast.Attribute, dotName: str) -> ast.Attribute:
385
- return ast.Attribute(value=nameChain, attr=dotName, ctx=ast.Load())
386
- @staticmethod
387
- # TODO rewrite with all parameters
388
- def nameDOTname(identifier: ast_Identifier, *dotName: str) -> ast.Name | ast.Attribute:
389
- nameDOTname: ast.Name | ast.Attribute = Make.astName(identifier)
390
- if not dotName:
391
- return nameDOTname
392
- for suffix in dotName:
393
- nameDOTname = Make.itDOTname(nameDOTname, suffix)
394
- return nameDOTname
395
- @staticmethod
396
- def astReturn(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
397
- return ast.Return(value, **keywordArguments)
398
- @staticmethod
399
- def astSubscript(value: ast.expr, slice: ast_expr_Slice, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Subscript:
400
- return ast.Subscript(value, slice, ctx=context, **keywordArguments)
401
- @staticmethod
402
- def astTuple(elements: Sequence[ast.expr], context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Tuple:
403
- """context: Load/Store/Del"""
404
- return ast.Tuple(elts=list(elements), ctx=context, **keywordArguments)
405
-
406
- class LedgerOfImports:
407
- # TODO When resolving the ledger of imports, remove self-referential imports
408
-
409
- def __init__(self, startWith: ast.AST | None = None) -> None:
410
- self.dictionaryImportFrom: dict[str, list[tuple[str, str | None]]] = defaultdict(list)
411
- self.listImport: list[str] = []
412
-
413
- if startWith:
414
- self.walkThis(startWith)
415
-
416
- def addAst(self, astImport_: ast.Import | ast.ImportFrom) -> None:
417
- assert isinstance(astImport_, (ast.Import, ast.ImportFrom)), f"Expected ast.Import or ast.ImportFrom, got {type(astImport_)}"
418
- if isinstance(astImport_, ast.Import):
419
- for alias in astImport_.names:
420
- self.listImport.append(alias.name)
421
- else:
422
- if astImport_.module is not None:
423
- for alias in astImport_.names:
424
- self.dictionaryImportFrom[astImport_.module].append((alias.name, alias.asname))
425
-
426
- def addImportStr(self, module: str) -> None:
427
- self.listImport.append(module)
428
-
429
- def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
430
- self.dictionaryImportFrom[module].append((name, asname))
431
-
432
- def exportListModuleNames(self) -> list[str]:
433
- listModuleNames: list[str] = list(self.dictionaryImportFrom.keys())
434
- listModuleNames.extend(self.listImport)
435
- return sorted(set(listModuleNames))
436
-
437
- def makeListAst(self) -> list[ast.ImportFrom | ast.Import]:
438
- listAstImportFrom: list[ast.ImportFrom] = []
439
-
440
- for module, listOfNameTuples in sorted(self.dictionaryImportFrom.items()):
441
- listOfNameTuples = sorted(list(set(listOfNameTuples)), key=lambda nameTuple: nameTuple[0])
442
- listAlias: list[ast.alias] = []
443
- for name, asname in listOfNameTuples:
444
- listAlias.append(Make.astAlias(name, asname))
445
- listAstImportFrom.append(Make.astImportFrom(module, listAlias))
446
-
447
- listAstImport: list[ast.Import] = [Make.astImport(name) for name in sorted(set(self.listImport))]
448
- return listAstImportFrom + listAstImport
449
-
450
- def update(self, *fromLedger: 'LedgerOfImports') -> None:
451
- """Update this ledger with imports from one or more other ledgers.
452
- Parameters:
453
- *fromLedger: One or more other `LedgerOfImports` objects from which to merge.
454
- """
455
- self.dictionaryImportFrom = updateExtendPolishDictionaryLists(self.dictionaryImportFrom, *(ledger.dictionaryImportFrom for ledger in fromLedger), destroyDuplicates=True, reorderLists=True)
456
-
457
- for ledger in fromLedger:
458
- self.listImport.extend(ledger.listImport)
37
+ def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
38
+ return NodeTourist(ifThis.isClassDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
459
39
 
460
- def walkThis(self, walkThis: ast.AST) -> None:
461
- for smurf in ast.walk(walkThis):
462
- if isinstance(smurf, (ast.Import, ast.ImportFrom)):
463
- self.addAst(smurf)
40
+ def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
41
+ return NodeTourist(ifThis.isFunctionDef_Identifier(identifier), Then.getIt).captureLastMatch(module)
464
42
 
465
- class Then:
466
- @staticmethod
467
- def append_targetTo(listName: list[ast.AST]) -> Callable[[ast.AnnAssign], None]:
468
- return lambda node: listName.append(node.target)
469
- @staticmethod
470
- def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST], None]:
471
- return lambda node: listOfAny.append(node)
472
- @staticmethod
473
- def insertThisAbove(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
474
- return lambda aboveMe: [*list_astAST, aboveMe]
475
- @staticmethod
476
- def insertThisBelow(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
477
- return lambda belowMe: [belowMe, *list_astAST]
478
- @staticmethod
479
- def removeThis(_node: ast.AST) -> None:
480
- return None
481
- @staticmethod
482
- def replaceWith(astAST: ast.AST) -> Callable[[ast.AST], ast.AST]:
483
- return lambda _replaceMe: astAST
484
- @staticmethod
485
- def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
486
- return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
487
- @staticmethod
488
- def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AnnAssign], None]:
489
- return lambda node: ledger.addImportFromStr(logicalPath, node.annotation.id) # type: ignore
490
- @staticmethod
491
- def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AnnAssign], None]:
492
- return lambda node: list_keyword.append(Make.ast_keyword(node.target.id, node.target)) # type: ignore
493
- @staticmethod
494
- def Z0Z_appendAnnAssignOf_nameDOTnameTo(identifier: ast_Identifier, list_nameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AnnAssign], None]:
495
- return lambda node: list_nameDOTname.append(Make.astAnnAssign(node.target, node.annotation, Make.nameDOTname(identifier, node.target.id))) # type: ignore
496
-
497
- @dataclasses.dataclass
498
- class IngredientsFunction:
499
- """Everything necessary to integrate a function into a module should be here."""
500
- astFunctionDef: ast.FunctionDef # hint `Make.astFunctionDef`
501
- imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
502
-
503
- @dataclasses.dataclass
504
- class IngredientsModule:
505
- """Everything necessary to create one _logical_ `ast.Module` should be here.
506
- Extrinsic qualities should _probably_ be handled externally."""
507
- ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
508
-
509
- # init var with an existing module? method to deconstruct an existing module?
510
-
511
- # `body` attribute of `ast.Module`
512
- imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
513
- prologue: list[ast.stmt] = dataclasses.field(default_factory=list)
514
- functions: list[ast.FunctionDef | ast.stmt] = dataclasses.field(default_factory=list)
515
- epilogue: list[ast.stmt] = dataclasses.field(default_factory=list)
516
- launcher: list[ast.stmt] = dataclasses.field(default_factory=list)
517
-
518
- # parameter for `ast.Module` constructor
519
- type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
520
-
521
- def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
522
- if ingredientsFunction is not None:
523
- if isinstance(ingredientsFunction, IngredientsFunction):
524
- self.addIngredientsFunction(ingredientsFunction)
525
- else:
526
- self.addIngredientsFunction(*ingredientsFunction)
527
-
528
- def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
529
- """Add one or more `IngredientsFunction`."""
530
- listLedgers: list[LedgerOfImports] = []
531
- for definition in ingredientsFunction:
532
- self.functions.append(definition.astFunctionDef)
533
- listLedgers.append(definition.imports)
534
- self.imports.update(*listLedgers)
535
-
536
- def _makeModuleBody(self) -> list[ast.stmt]:
537
- body: list[ast.stmt] = []
538
- body.extend(self.imports.makeListAst())
539
- body.extend(self.prologue)
540
- body.extend(self.functions)
541
- body.extend(self.epilogue)
542
- body.extend(self.launcher)
543
- # TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
544
- return body
545
-
546
- def export(self) -> ast.Module:
547
- """Create a new `ast.Module` from the ingredients."""
548
- return Make.astModule(self._makeModuleBody(), self.type_ignores)
549
-
550
- @dataclasses.dataclass
551
- class RecipeSynthesizeFlow:
552
- """Settings for synthesizing flow."""
553
- # ========================================
554
- # Source
555
- sourceAlgorithm: ModuleType = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
556
- sourcePython: str = inspect_getsource(sourceAlgorithm)
557
- source_astModule: ast.Module = ast.parse(sourcePython)
558
-
559
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
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
572
- # ========================================
573
- # Filesystem
574
- pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
575
- fileExtension: str = The.fileExtension
576
-
577
- # ========================================
578
- # Logical identifiers
579
- # meta
580
- formatStrModuleSynthetic: str = theFormatStrModuleSynthetic
581
- formatStrModuleForCallableSynthetic: str = theFormatStrModuleForCallableSynthetic
582
-
583
- # Package
584
- packageName: ast_Identifier | None = The.packageName
585
-
586
- # Module
587
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
588
- Z0Z_flowLogicalPathRoot: str | None = The.moduleOfSyntheticModules
589
- moduleDispatcher: str = theModuleDispatcherSynthetic
590
- logicalPathModuleDataclass: str = sourcePathModuleDataclass
591
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
592
- # `theLogicalPathModuleDispatcherSynthetic` is a problem. It is defined in theSSOT, but it can also be calculated.
593
- logicalPathModuleDispatcher: str = theLogicalPathModuleDispatcherSynthetic
594
-
595
- # Function
596
- dispatcherCallable: str = sourceDispatcherCallable
597
- initializeCallable: str = sourceInitializeCallable
598
- parallelCallable: str = sourceParallelCallable
599
- sequentialCallable: str = sourceSequentialCallable
600
-
601
- dataclassIdentifier: str = sourceDataclassIdentifier
602
-
603
- # Variable
604
- dataclassInstance: str = sourceDataclassInstance
43
+ def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
44
+ astModule = Make.Module(ingredients.body, ingredients.type_ignores)
45
+ ast.fix_missing_locations(astModule)
46
+ pythonSource: str = ast.unparse(astModule)
47
+ if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
48
+ autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleIdentifiers()
49
+ if packageName:
50
+ autoflake_additional_imports.append(packageName)
51
+ 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)
52
+ writeStringToHere(pythonSource, pathFilename)
605
53
 
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)
54
+ # END of acceptable classes and functions ======================================================
621
55
 
622
- @property
623
- def pathFilenameDispatcher(self) -> PurePosixPath:
624
- return self._makePathFilename(filenameStem=self.moduleDispatcher, logicalPathINFIX=self.Z0Z_flowLogicalPathRoot)
56
+ def makeDictionaryFunctionDef(module: ast.AST) -> dict[ast_Identifier, ast.FunctionDef]:
57
+ dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
58
+ NodeTourist(be.FunctionDef, Then.updateThis(dictionaryFunctionDef)).visit(module)
59
+ return dictionaryFunctionDef
625
60
 
626
- def extractClassDef(identifier: ast_Identifier, module: ast.Module) -> ast.ClassDef | None:
627
- sherpa: list[ast.ClassDef] = []
628
- extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
629
- extractor.visit(module)
630
- astClassDef = sherpa[0] if sherpa else None
631
- return astClassDef
61
+ dictionaryEstimates: dict[tuple[int, ...], int] = {
62
+ (2,2,2,2,2,2,2,2): 362794844160000,
63
+ (2,21): 1493028892051200,
64
+ (3,15): 9842024675968800,
65
+ (3,3,3,3): 85109616000000000000000000000000,
66
+ (8,8): 129950723279272000,
67
+ }
632
68
 
633
- def extractFunctionDef(identifier: ast_Identifier, module: ast.Module) -> ast.FunctionDef | None:
634
- sherpa: list[ast.FunctionDef] = []
635
- extractor = NodeCollector(ifThis.isFunctionDef_Identifier(identifier), [Then.appendTo(sherpa)])
636
- extractor.visit(module)
637
- astClassDef = sherpa[0] if sherpa else None
638
- return astClassDef
69
+ # END of marginal classes and functions ======================================================
70
+ def Z0Z_lameFindReplace(astTree: typeCertified, mappingFindReplaceNodes: Mapping[ast.AST, ast.AST]) -> typeCertified:
71
+ keepGoing = True
72
+ newTree = deepcopy(astTree)
639
73
 
640
- def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.FunctionDef]:
641
- dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
642
- NodeCollector(ifThis.isFunctionDef, [Then.updateThis(dictionaryFunctionDef)]).visit(module)
643
- return dictionaryFunctionDef
74
+ while keepGoing:
75
+ for nodeFind, nodeReplace in mappingFindReplaceNodes.items():
76
+ NodeChanger(ifThis.Z0Z_unparseIs(nodeFind), Then.replaceWith(nodeReplace)).visit(newTree)
644
77
 
645
- def makeDictionaryReplacementStatements(module: ast.Module) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
646
- """Return a dictionary of function names and their replacement statements."""
647
- dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
648
- dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
649
- for name, astFunctionDef in dictionaryFunctionDef.items():
650
- if ifThis.onlyReturnAnyCompare(astFunctionDef):
651
- dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
652
- elif ifThis.onlyReturnUnaryOp(astFunctionDef):
653
- dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
78
+ if ast.unparse(newTree) == ast.unparse(astTree):
79
+ keepGoing = False
654
80
  else:
655
- dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
656
- return dictionaryReplacementStatements
81
+ astTree = deepcopy(newTree)
82
+ return newTree
657
83
 
84
+ # Start of I HATE PROGRAMMING ==========================================================
85
+ # Similar functionality to call does not call itself, but it is used for something else. I hate this function, too.
658
86
  def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callable[[ast.AST], bool]) -> bool:
659
87
  """Return True if any descendant of the node (or the node itself) matches the predicateFunction."""
660
88
  matchFound = False
661
-
662
89
  class DescendantFinder(ast.NodeVisitor):
663
90
  def generic_visit(self, node: ast.AST) -> None:
664
91
  nonlocal matchFound
@@ -666,7 +93,6 @@ def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callabl
666
93
  matchFound = True
667
94
  else:
668
95
  super().generic_visit(node)
669
-
670
96
  DescendantFinder().visit(node)
671
97
  return matchFound
672
98
 
@@ -677,7 +103,21 @@ def Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.A
677
103
  actionFunction(node)
678
104
  return wrappedAction
679
105
 
680
- def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
106
+ # Inlining functions ==========================================================
107
+ def Z0Z_makeDictionaryReplacementStatements(module: ast.AST) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
108
+ """Return a dictionary of function names and their replacement statements."""
109
+ dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
110
+ dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
111
+ for name, astFunctionDef in dictionaryFunctionDef.items():
112
+ if ifThis.onlyReturnAnyCompare(astFunctionDef):
113
+ dictionaryReplacementStatements[name] = astFunctionDef.body[0].value
114
+ elif ifThis.onlyReturnUnaryOp(astFunctionDef):
115
+ dictionaryReplacementStatements[name] = astFunctionDef.body[0].value
116
+ else:
117
+ dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
118
+ return dictionaryReplacementStatements
119
+
120
+ def Z0Z_inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> ast.FunctionDef:
681
121
  class FunctionInliner(ast.NodeTransformer):
682
122
  def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
683
123
  self.dictionaryReplacementStatements = dictionaryReplacementStatements
@@ -688,17 +128,17 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
688
128
 
689
129
  def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
690
130
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
691
- return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
131
+ return self.dictionaryReplacementStatements[node.value.func.id]
692
132
  return node
693
133
 
694
134
  def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
695
135
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
696
- return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
136
+ return self.dictionaryReplacementStatements[node.value.func.id]
697
137
  return node
698
138
 
699
139
  def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
700
140
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
701
- replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore[attr-defined]
141
+ replacement = self.dictionaryReplacementStatements[node.func.id]
702
142
  if not isinstance(replacement, list):
703
143
  return replacement
704
144
  return node
@@ -714,58 +154,3 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
714
154
  astFunctionDef = deepcopy(ImaInlineFunction)
715
155
  ast.fix_missing_locations(astFunctionDef)
716
156
  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)