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
@@ -23,649 +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
- def workhorse(node: ast.AST) -> TypeGuard[ast.Call] | bool: return ifThis.isCall(node) and ifThis.isName_Identifier(identifier)(node.func)
193
- return workhorse
194
- @staticmethod
195
- def isCallNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
196
- return lambda node: ifThis.isCall(node) and ifThis.is_nameDOTnameNamespace_Identifier(namespace, identifier)(node.func)
197
- @staticmethod
198
- def isCallToName(node: ast.AST) -> TypeGuard[ast.Call]:
199
- return ifThis.isCall(node) and ifThis.isName(node.func)
200
- @staticmethod
201
- def isClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
202
- return isinstance(node, ast.ClassDef)
203
- @staticmethod
204
- def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
205
- return lambda node: ifThis.isClassDef(node) and node.name == identifier
206
- @staticmethod
207
- def isCompare(node: ast.AST) -> TypeGuard[ast.Compare]:
208
- return isinstance(node, ast.Compare)
209
- @staticmethod
210
- def isConstant(node: ast.AST) -> TypeGuard[ast.Constant]:
211
- return isinstance(node, ast.Constant)
212
- @staticmethod
213
- def isConstantEquals(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
214
- return lambda node: ifThis.isConstant(node) and node.value == value
215
- @staticmethod
216
- def isExpr(node: ast.AST) -> TypeGuard[ast.Expr]:
217
- return isinstance(node, ast.Expr)
218
- @staticmethod
219
- def isFunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]: 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
- workhorse: function that checks if a node matches the criteria
270
- """
271
- def workhorse(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 workhorse
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 workhorse(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 workhorse
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: return None
480
- @staticmethod
481
- def replaceWith(astAST: ast.AST) -> Callable[[ast.AST], ast.AST]: return lambda _replaceMe: astAST
482
- @staticmethod
483
- def replaceDOTfuncWith(ast_expr: ast.expr) -> Callable[[ast.Call], ast.Call]:
484
- def workhorse(node: ast.Call) -> ast.Call:
485
- node.func = ast_expr
486
- return node
487
- return workhorse
488
- @staticmethod
489
- def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
490
- return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
491
- @staticmethod
492
- def Z0Z_ledger(logicalPath: strDotStrCuzPyStoopid, ledger: LedgerOfImports) -> Callable[[ast.AnnAssign], None]:
493
- return lambda node: ledger.addImportFromStr(logicalPath, node.annotation.id) # type: ignore
494
- @staticmethod
495
- def Z0Z_appendKeywordMirroredTo(list_keyword: list[ast.keyword]) -> Callable[[ast.AnnAssign], None]:
496
- return lambda node: list_keyword.append(Make.ast_keyword(node.target.id, node.target)) # type: ignore
497
- @staticmethod
498
- def Z0Z_appendAnnAssignOf_nameDOTnameTo(identifier: ast_Identifier, list_nameDOTname: list[ast.AnnAssign]) -> Callable[[ast.AnnAssign], None]:
499
- return lambda node: list_nameDOTname.append(Make.astAnnAssign(node.target, node.annotation, Make.nameDOTname(identifier, node.target.id))) # type: ignore
500
-
501
- @dataclasses.dataclass
502
- class IngredientsFunction:
503
- """Everything necessary to integrate a function into a module should be here."""
504
- astFunctionDef: ast.FunctionDef # hint `Make.astFunctionDef`
505
- imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
506
-
507
- @dataclasses.dataclass
508
- class IngredientsModule:
509
- """Everything necessary to create one _logical_ `ast.Module` should be here.
510
- Extrinsic qualities should _probably_ be handled externally."""
511
- ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
512
-
513
- # init var with an existing module? method to deconstruct an existing module?
514
-
515
- # `body` attribute of `ast.Module`
516
- imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
517
- prologue: list[ast.stmt] = dataclasses.field(default_factory=list)
518
- functions: list[ast.FunctionDef | ast.stmt] = dataclasses.field(default_factory=list)
519
- epilogue: list[ast.stmt] = dataclasses.field(default_factory=list)
520
- launcher: list[ast.stmt] = dataclasses.field(default_factory=list)
521
-
522
- # parameter for `ast.Module` constructor
523
- type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
524
-
525
- def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
526
- if ingredientsFunction is not None:
527
- if isinstance(ingredientsFunction, IngredientsFunction):
528
- self.addIngredientsFunction(ingredientsFunction)
529
- else:
530
- self.addIngredientsFunction(*ingredientsFunction)
531
-
532
- def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
533
- """Add one or more `IngredientsFunction`."""
534
- listLedgers: list[LedgerOfImports] = []
535
- for definition in ingredientsFunction:
536
- self.functions.append(definition.astFunctionDef)
537
- listLedgers.append(definition.imports)
538
- self.imports.update(*listLedgers)
539
-
540
- def _makeModuleBody(self) -> list[ast.stmt]:
541
- body: list[ast.stmt] = []
542
- body.extend(self.imports.makeListAst())
543
- body.extend(self.prologue)
544
- body.extend(self.functions)
545
- body.extend(self.epilogue)
546
- body.extend(self.launcher)
547
- # TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
548
- return body
549
-
550
- def export(self) -> ast.Module:
551
- """Create a new `ast.Module` from the ingredients."""
552
- return Make.astModule(self._makeModuleBody(), self.type_ignores)
553
-
554
- @dataclasses.dataclass
555
- class RecipeSynthesizeFlow:
556
- """Settings for synthesizing flow."""
557
- # ========================================
558
- # Source
559
- sourceAlgorithm: ModuleType = importlib_import_module(The.logicalPathModuleSourceAlgorithm)
560
- sourcePython: str = inspect_getsource(sourceAlgorithm)
561
- source_astModule: ast.Module = ast.parse(sourcePython)
562
-
563
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
564
- sourceDispatcherCallable: str = The.dispatcherCallable
565
- sourceInitializeCallable: str = The.sourceInitializeCallable
566
- sourceParallelCallable: str = The.sourceParallelCallable
567
- sourceSequentialCallable: str = The.sourceSequentialCallable
568
-
569
- sourceDataclassIdentifier: str = The.dataclassIdentifier
570
- sourceDataclassInstance: str = The.dataclassInstance
571
- sourceDataclassInstanceTaskDistribution: str = The.dataclassInstanceTaskDistribution
572
- sourcePathModuleDataclass: str = The.logicalPathModuleDataclass
573
-
574
- sourceConcurrencyManagerNamespace = The.sourceConcurrencyManagerNamespace
575
- sourceConcurrencyManagerIdentifier = The.sourceConcurrencyManagerIdentifier
576
- # ========================================
577
- # Filesystem
578
- pathPackage: PurePosixPath | None = PurePosixPath(The.pathPackage)
579
- fileExtension: str = The.fileExtension
580
-
581
- # ========================================
582
- # Logical identifiers
583
- # meta
584
- formatStrModuleSynthetic: str = theFormatStrModuleSynthetic
585
- formatStrModuleForCallableSynthetic: str = theFormatStrModuleForCallableSynthetic
586
-
587
- # Package
588
- packageName: ast_Identifier | None = The.packageName
589
-
590
- # Module
591
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
592
- Z0Z_flowLogicalPathRoot: str | None = The.moduleOfSyntheticModules
593
- moduleDispatcher: str = theModuleDispatcherSynthetic
594
- logicalPathModuleDataclass: str = sourcePathModuleDataclass
595
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
596
- # `theLogicalPathModuleDispatcherSynthetic` is a problem. It is defined in theSSOT, but it can also be calculated.
597
- logicalPathModuleDispatcher: str = theLogicalPathModuleDispatcherSynthetic
598
-
599
- # Function
600
- dispatcherCallable: str = sourceDispatcherCallable
601
- initializeCallable: str = sourceInitializeCallable
602
- parallelCallable: str = sourceParallelCallable
603
- sequentialCallable: str = sourceSequentialCallable
604
- # initializeCallable: str = 'StartTheCommotion'
605
- # parallelCallable: str = sourceParallelCallable
606
- # sequentialCallable: str = sourceSequentialCallable
607
-
608
- dataclassIdentifier: str = sourceDataclassIdentifier
609
-
610
- # Variable
611
- 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)
612
53
 
613
- def _makePathFilename(self, filenameStem: str,
614
- pathRoot: PurePosixPath | None = None,
615
- logicalPathINFIX: strDotStrCuzPyStoopid | None = None,
616
- fileExtension: str | None = None,
617
- ) -> PurePosixPath:
618
- """filenameStem: (hint: the name of the logical module)"""
619
- if pathRoot is None:
620
- pathRoot = self.pathPackage or PurePosixPath(Path.cwd())
621
- if logicalPathINFIX:
622
- whyIsThisStillAThing: list[str] = logicalPathINFIX.split('.')
623
- pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
624
- if fileExtension is None:
625
- fileExtension = self.fileExtension
626
- filename: str = filenameStem + fileExtension
627
- return pathRoot.joinpath(filename)
54
+ # END of acceptable classes and functions ======================================================
628
55
 
629
- @property
630
- def pathFilenameDispatcher(self) -> PurePosixPath:
631
- 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
632
60
 
633
- def extractClassDef(module: ast.Module, identifier: ast_Identifier) -> ast.ClassDef | None:
634
- sherpa: list[ast.ClassDef] = []
635
- extractor = NodeCollector(ifThis.isClassDef_Identifier(identifier), [Then.appendTo(sherpa)])
636
- extractor.visit(module)
637
- astClassDef = sherpa[0] if sherpa else None
638
- 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
+ }
639
68
 
640
- def extractFunctionDef(module: ast.Module, identifier: ast_Identifier) -> ast.FunctionDef | None:
641
- sherpa: list[ast.FunctionDef] = []
642
- extractor = NodeCollector(ifThis.isFunctionDef_Identifier(identifier), [Then.appendTo(sherpa)])
643
- extractor.visit(module)
644
- astClassDef = sherpa[0] if sherpa else None
645
- 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)
646
73
 
647
- def makeDictionaryFunctionDef(module: ast.Module) -> dict[ast_Identifier, ast.FunctionDef]:
648
- dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = {}
649
- NodeCollector(ifThis.isFunctionDef, [Then.updateThis(dictionaryFunctionDef)]).visit(module)
650
- return dictionaryFunctionDef
74
+ while keepGoing:
75
+ for nodeFind, nodeReplace in mappingFindReplaceNodes.items():
76
+ NodeChanger(ifThis.Z0Z_unparseIs(nodeFind), Then.replaceWith(nodeReplace)).visit(newTree)
651
77
 
652
- def makeDictionaryReplacementStatements(module: ast.Module) -> dict[ast_Identifier, ast.stmt | list[ast.stmt]]:
653
- """Return a dictionary of function names and their replacement statements."""
654
- dictionaryFunctionDef: dict[ast_Identifier, ast.FunctionDef] = makeDictionaryFunctionDef(module)
655
- dictionaryReplacementStatements: dict[ast_Identifier, ast.stmt | list[ast.stmt]] = {}
656
- for name, astFunctionDef in dictionaryFunctionDef.items():
657
- if ifThis.onlyReturnAnyCompare(astFunctionDef):
658
- dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
659
- elif ifThis.onlyReturnUnaryOp(astFunctionDef):
660
- dictionaryReplacementStatements[name] = astFunctionDef.body[0].value # type: ignore
78
+ if ast.unparse(newTree) == ast.unparse(astTree):
79
+ keepGoing = False
661
80
  else:
662
- dictionaryReplacementStatements[name] = astFunctionDef.body[0:-1]
663
- return dictionaryReplacementStatements
81
+ astTree = deepcopy(newTree)
82
+ return newTree
664
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.
665
86
  def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callable[[ast.AST], bool]) -> bool:
666
87
  """Return True if any descendant of the node (or the node itself) matches the predicateFunction."""
667
88
  matchFound = False
668
-
669
89
  class DescendantFinder(ast.NodeVisitor):
670
90
  def generic_visit(self, node: ast.AST) -> None:
671
91
  nonlocal matchFound
@@ -673,7 +93,6 @@ def Z0Z_descendantContainsMatchingNode(node: ast.AST, predicateFunction: Callabl
673
93
  matchFound = True
674
94
  else:
675
95
  super().generic_visit(node)
676
-
677
96
  DescendantFinder().visit(node)
678
97
  return matchFound
679
98
 
@@ -684,7 +103,21 @@ def Z0Z_executeActionUnlessDescendantMatches(exclusionPredicate: Callable[[ast.A
684
103
  actionFunction(node)
685
104
  return wrappedAction
686
105
 
687
- 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:
688
121
  class FunctionInliner(ast.NodeTransformer):
689
122
  def __init__(self, dictionaryReplacementStatements: dict[str, ast.stmt | list[ast.stmt]]) -> None:
690
123
  self.dictionaryReplacementStatements = dictionaryReplacementStatements
@@ -695,17 +128,17 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
695
128
 
696
129
  def visit_Expr(self, node: ast.Expr) -> ast.AST | list[ast.stmt]:
697
130
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
698
- return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
131
+ return self.dictionaryReplacementStatements[node.value.func.id]
699
132
  return node
700
133
 
701
134
  def visit_Assign(self, node: ast.Assign) -> ast.AST | list[ast.stmt]:
702
135
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node.value):
703
- return self.dictionaryReplacementStatements[node.value.func.id] # type: ignore[attr-defined]
136
+ return self.dictionaryReplacementStatements[node.value.func.id]
704
137
  return node
705
138
 
706
139
  def visit_Call(self, node: ast.Call) -> ast.AST | list[ast.stmt]:
707
140
  if ifThis.CallDoesNotCallItselfAndNameDOTidIsIn(self.dictionaryReplacementStatements)(node):
708
- replacement = self.dictionaryReplacementStatements[node.func.id] # type: ignore[attr-defined]
141
+ replacement = self.dictionaryReplacementStatements[node.func.id]
709
142
  if not isinstance(replacement, list):
710
143
  return replacement
711
144
  return node
@@ -721,58 +154,3 @@ def inlineThisFunctionWithTheseValues(astFunctionDef: ast.FunctionDef, dictionar
721
154
  astFunctionDef = deepcopy(ImaInlineFunction)
722
155
  ast.fix_missing_locations(astFunctionDef)
723
156
  return ImaInlineFunction
724
-
725
- def Z0Z_replaceMatchingASTnodes(astTree: ast.AST, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> ast.AST:
726
- class TargetedNodeReplacer(ast.NodeTransformer):
727
- def __init__(self, mappingFindReplaceNodes: dict[ast.AST, ast.AST]) -> None:
728
- self.mappingFindReplaceNodes = mappingFindReplaceNodes
729
-
730
- def visit(self, node: ast.AST) -> ast.AST:
731
- for nodeFind, nodeReplace in self.mappingFindReplaceNodes.items():
732
- if self.nodesMatchStructurally(node, nodeFind):
733
- return nodeReplace
734
- return self.generic_visit(node)
735
-
736
- def nodesMatchStructurally(self, nodeSubject: ast.AST | list[Any] | Any, nodePattern: ast.AST | list[Any] | Any) -> bool:
737
- if nodeSubject is None or nodePattern is None:
738
- return nodeSubject is None and nodePattern is None
739
-
740
- if type(nodeSubject) != type(nodePattern):
741
- return False
742
-
743
- if isinstance(nodeSubject, ast.AST):
744
- for field, fieldValueSubject in ast.iter_fields(nodeSubject):
745
- if field in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'ctx'):
746
- continue
747
- attrPattern = getattr(nodePattern, field, None)
748
- if not self.nodesMatchStructurally(fieldValueSubject, attrPattern):
749
- return False
750
- return True
751
-
752
- if isinstance(nodeSubject, list) and isinstance(nodePattern, list):
753
- nodeSubjectList: list[Any] = nodeSubject
754
- nodePatternList: list[Any] = nodePattern
755
- return len(nodeSubjectList) == len(nodePatternList) and all(
756
- self.nodesMatchStructurally(elementSubject, elementPattern)
757
- for elementSubject, elementPattern in zip(nodeSubjectList, nodePatternList)
758
- )
759
-
760
- return nodeSubject == nodePattern
761
-
762
- astTreeCurrent, astTreePrevious = None, astTree
763
- while astTreeCurrent is None or ast.unparse(astTreeCurrent) != ast.unparse(astTreePrevious):
764
- astTreePrevious = astTreeCurrent if astTreeCurrent else astTree
765
- astTreeCurrent = TargetedNodeReplacer(mappingFindReplaceNodes).visit(astTreePrevious)
766
-
767
- return astTreeCurrent
768
-
769
- def write_astModule(ingredients: IngredientsModule, pathFilename: str | PathLike[Any] | PurePath, packageName: ast_Identifier | None = None) -> None:
770
- astModule = ingredients.export()
771
- ast.fix_missing_locations(astModule)
772
- pythonSource: str = ast.unparse(astModule)
773
- if not pythonSource: raise raiseIfNoneGitHubIssueNumber3
774
- autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleNames()
775
- if packageName:
776
- autoflake_additional_imports.append(packageName)
777
- 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)
778
- writeStringToHere(pythonSource, pathFilename)