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