astToolkit 0.7.1__tar.gz → 0.7.2__tar.gz
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.
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/PKG-INFO +2 -2
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/README.md +1 -1
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitAST.py +6 -10
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/transformationTools.py +57 -18
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/PKG-INFO +2 -2
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/pyproject.toml +1 -1
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/LICENSE +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/__init__.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_astTypes.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_dumpFunctionDef.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_dumpHandmade.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_prototypeFind.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_theSSOT.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolBe.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolDOT.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolGrab.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolIfThis.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolMake.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolThen.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitContainers.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitNodeVisitor.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/py.typed +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/SOURCES.txt +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/dependency_links.txt +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/requires.txt +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/top_level.txt +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/setup.cfg +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/__init__.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/conftest.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/Make.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/__init__.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/createTestData.py +0 -0
- {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/test_Be.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astToolkit
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: A powerfully composable, type-safe toolkit for Python abstract syntax tree (AST) manipulation, analysis, transformation, and code generation with a layered architecture designed for building sophisticated code processing assembly-lines.
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -354,4 +354,4 @@ Coding One Step at a Time:
|
|
|
354
354
|
2. Write good code.
|
|
355
355
|
3. When revising, write better code.
|
|
356
356
|
|
|
357
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -312,4 +312,4 @@ Coding One Step at a Time:
|
|
|
312
312
|
2. Write good code.
|
|
313
313
|
3. When revising, write better code.
|
|
314
314
|
|
|
315
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -71,23 +71,19 @@ def extractClassDef(astAST: ast.AST, identifier: str) -> ast.ClassDef | None:
|
|
|
71
71
|
|
|
72
72
|
def extractFunctionDef(astAST: ast.AST, identifier: str) -> ast.FunctionDef | None:
|
|
73
73
|
"""
|
|
74
|
-
Extract
|
|
75
|
-
|
|
76
|
-
(AI generated docstring)
|
|
77
|
-
|
|
78
|
-
This function searches through an AST module for a function definition that matches the provided identifier and returns it if found.
|
|
74
|
+
Extract the function from `astAST` with `ast.FunctionDef.name == identifier`.
|
|
79
75
|
|
|
80
76
|
Parameters
|
|
81
77
|
----------
|
|
82
78
|
astAST : ast.AST
|
|
83
|
-
|
|
79
|
+
(abstract syntax tree) An `ast.AST` subclass from which to extract the function.
|
|
84
80
|
identifier : str
|
|
85
|
-
The
|
|
81
|
+
The identifier in `ast.FunctionDef.name`.
|
|
86
82
|
|
|
87
83
|
Returns
|
|
88
84
|
-------
|
|
89
|
-
astFunctionDef | None
|
|
90
|
-
The
|
|
85
|
+
astFunctionDef : ast.FunctionDef | None
|
|
86
|
+
The target function, or `None` if `extractFunctionDef` does not find `ast.FunctionDef.name == identifier`.
|
|
91
87
|
|
|
92
88
|
"""
|
|
93
89
|
return NodeTourist(IfThis.isFunctionDefIdentifier(identifier), Then.extractIt).captureLastMatch(astAST)
|
|
@@ -99,7 +95,7 @@ def parseLogicalPath2astModule(logicalPath: identifierDotAttribute, package: str
|
|
|
99
95
|
(AI generated docstring)
|
|
100
96
|
|
|
101
97
|
This function imports a module using its logical path (e.g., 'scipy.signal.windows') and converts its source code into an
|
|
102
|
-
`ast.Module` (abstract syntax tree) object
|
|
98
|
+
`ast.Module` (abstract syntax tree) `object`. Supports all relevant `ast.parse` parameters.
|
|
103
99
|
|
|
104
100
|
Parameters
|
|
105
101
|
----------
|
|
@@ -25,7 +25,6 @@ These transformation tools form the backbone of the code optimization pipeline,
|
|
|
25
25
|
maintaining semantic integrity and performance characteristics.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
import io
|
|
29
28
|
from astToolkit import (
|
|
30
29
|
Be, DOT, Grab, IfThis, IngredientsFunction, IngredientsModule, Make, NodeChanger, NodeTourist, Then, 木)
|
|
31
30
|
from autoflake import fix_code as autoflake_fix_code
|
|
@@ -36,6 +35,7 @@ from os import PathLike
|
|
|
36
35
|
from pathlib import PurePath
|
|
37
36
|
from typing import Any
|
|
38
37
|
import ast
|
|
38
|
+
import io
|
|
39
39
|
|
|
40
40
|
def makeDictionaryAsyncFunctionDef(astAST: ast.AST) -> dict[str, ast.AsyncFunctionDef]:
|
|
41
41
|
"""
|
|
@@ -128,48 +128,60 @@ def makeDictionaryMosDef(astAST: ast.AST) -> dict[str, ast.AsyncFunctionDef | as
|
|
|
128
128
|
dictionaryIdentifier2MosDef.update(makeDictionaryFunctionDef(astAST))
|
|
129
129
|
return dictionaryIdentifier2MosDef
|
|
130
130
|
|
|
131
|
-
def inlineFunctionDef(identifierToInline: str,
|
|
131
|
+
def inlineFunctionDef(identifierToInline: str, astModule: ast.Module) -> ast.FunctionDef:
|
|
132
132
|
"""
|
|
133
|
-
|
|
133
|
+
Synthesize a new function from `identifierToInline` with the logic "inlined" from functions called by `identifierToInline`.
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
`inlineFunctionDef` starts with the `ast.FunctionDef` in `astModule` whose `ast.FunctionDef.name == identifierToInline`, then
|
|
136
|
+
`inlineFunctionDef` searches through `identifierToInline` for calls to functions defined in `astModule`.
|
|
137
|
+
|
|
138
|
+
More specifically, `inlineFunctionDef` searches for an `ast.Call` to an `ast.Name` (*e.g.*, `Path`, but not the
|
|
139
|
+
`ast.Attribute`, `pathlib.Path`). The `ast.Name` identifier is in `ast.Name.id`, for example, `ast.Name.id = "Path"`.
|
|
140
|
+
`inlineFunctionDef` then searches in `astModule` for an `ast.FunctionDef.name` that is the same as the `ast.Name.id`. If
|
|
141
|
+
`inlineFunctionDef` finds a matching `ast.FunctionDef`, it will replace `ast.Call` with the logic from the called function.
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
`inlineFunctionDef` repeats the process until it gets bored or until it cannot find any more functions to inline. Therefore,
|
|
144
|
+
`inlineFunctionDef` will inline functions that were not called directly by `identifierToInline` in the original `astModule`.
|
|
145
|
+
|
|
146
|
+
`inlineFunctionDef`, however, does not inline an `ast.FunctionDef` if the `ast.FunctionDef` calls itself or if a "child" of
|
|
147
|
+
the `ast.FunctionDef` calls the original `ast.FunctionDef`.
|
|
141
148
|
|
|
142
149
|
Parameters
|
|
143
150
|
----------
|
|
144
151
|
identifierToInline : str
|
|
145
|
-
The
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
The identifier of the target function; the `str` must match an `ast.FunctionDef.name` in `astModule`.
|
|
153
|
+
astModule : ast.Module
|
|
154
|
+
(abstract syntax tree Module) An `ast.Module` with `ast.FunctionDef.name == identifierToInline` and zero or more other
|
|
155
|
+
`ast.FunctionDef` to inline.
|
|
148
156
|
|
|
149
157
|
Returns
|
|
150
158
|
-------
|
|
151
159
|
FunctionDefToInline : ast.FunctionDef
|
|
152
|
-
The
|
|
160
|
+
The synthesized function with inlined logic from other functions defined in `astModule`.
|
|
153
161
|
|
|
154
162
|
Raises
|
|
155
163
|
------
|
|
156
|
-
ValueError: If
|
|
164
|
+
ValueError: If `identifierToInline` does not match any `ast.FunctionDef.name` in `astModule`.
|
|
157
165
|
"""
|
|
158
|
-
dictionaryFunctionDef: dict[str, ast.FunctionDef] = makeDictionaryFunctionDef(
|
|
166
|
+
dictionaryFunctionDef: dict[str, ast.FunctionDef] = makeDictionaryFunctionDef(astModule)
|
|
159
167
|
try:
|
|
160
168
|
FunctionDefToInline = dictionaryFunctionDef[identifierToInline]
|
|
161
169
|
except KeyError as ERRORmessage:
|
|
162
|
-
message = f"
|
|
170
|
+
message = f"I was unable to find an `ast.FunctionDef` with name {identifierToInline = } in {astModule = }."
|
|
163
171
|
raise ValueError(message) from ERRORmessage
|
|
164
172
|
|
|
165
173
|
listIdentifiersCalledFunctions: list[str] = []
|
|
174
|
+
# TODO I probably have the skill now to expand this from only `IfThis.isCallToName` to include more options for `ast.Call.func`,
|
|
175
|
+
# such as attribute and subscript access. `IfThis.isNestedNameIdentifier` might be perfect. BUT, I think this will affect how I
|
|
176
|
+
# key `dictionaryFunctionDef`.
|
|
166
177
|
findIdentifiersToInline = NodeTourist[ast.Call, ast.expr](IfThis.isCallToName
|
|
167
178
|
, Grab.funcAttribute(Grab.idAttribute(Then.appendTo(listIdentifiersCalledFunctions))))
|
|
168
179
|
findIdentifiersToInline.visit(FunctionDefToInline)
|
|
169
180
|
|
|
170
181
|
dictionary4Inlining: dict[str, ast.FunctionDef] = {}
|
|
171
182
|
for identifier in sorted(set(listIdentifiersCalledFunctions).intersection(dictionaryFunctionDef.keys())):
|
|
172
|
-
|
|
183
|
+
# TODO Learn how real programmers avoid infinite loops but still inline recursive functions.
|
|
184
|
+
if NodeTourist(IfThis.matchesMeButNotAnyDescendant(IfThis.isCallIdentifier(identifier)), Then.extractIt).captureLastMatch(astModule) is not None:
|
|
173
185
|
dictionary4Inlining[identifier] = dictionaryFunctionDef[identifier]
|
|
174
186
|
|
|
175
187
|
keepGoing = True
|
|
@@ -182,7 +194,7 @@ def inlineFunctionDef(identifierToInline: str, module: ast.Module) -> ast.Functi
|
|
|
182
194
|
if len(listIdentifiersCalledFunctions) > 0:
|
|
183
195
|
keepGoing = True
|
|
184
196
|
for identifier in listIdentifiersCalledFunctions:
|
|
185
|
-
if NodeTourist(IfThis.matchesMeButNotAnyDescendant(IfThis.isCallIdentifier(identifier)), Then.extractIt).captureLastMatch(
|
|
197
|
+
if NodeTourist(IfThis.matchesMeButNotAnyDescendant(IfThis.isCallIdentifier(identifier)), Then.extractIt).captureLastMatch(astModule) is not None:
|
|
186
198
|
FunctionDefTarget = dictionaryFunctionDef[identifier]
|
|
187
199
|
if len(FunctionDefTarget.body) == 1:
|
|
188
200
|
replacement = NodeTourist(Be.Return, Then.extractIt(DOT.value)).captureLastMatch(FunctionDefTarget)
|
|
@@ -206,6 +218,32 @@ def inlineFunctionDef(identifierToInline: str, module: ast.Module) -> ast.Functi
|
|
|
206
218
|
ast.fix_missing_locations(FunctionDefToInline)
|
|
207
219
|
return FunctionDefToInline
|
|
208
220
|
|
|
221
|
+
def pythonCode2ast_expr(string: str) -> ast.expr:
|
|
222
|
+
"""PROTOTYPE Convert *one* expression-as-a-string of Python code to an `ast.expr`.
|
|
223
|
+
|
|
224
|
+
TODO add list of applicable subclasses. Until then, see `Make.expr` for an approximate list.
|
|
225
|
+
|
|
226
|
+
Note well
|
|
227
|
+
---------
|
|
228
|
+
This prototype "shortcut" function has approximately 482 *implied* constraints and pitfalls. If you can't get it to do what
|
|
229
|
+
you want, I recommend saving yourself a bunch of stress and not using this shortcut.
|
|
230
|
+
"""
|
|
231
|
+
return raiseIfNone(NodeTourist(Be.Expr, Then.extractIt(DOT.value)).captureLastMatch(ast.parse(string)))
|
|
232
|
+
|
|
233
|
+
def pythonCode2ast_stmt(string: str):
|
|
234
|
+
"""PROTOTYPE Convert *one* statement-as-a-string of Python code to an `ast.stmt`.
|
|
235
|
+
|
|
236
|
+
TODO add list of applicable subclasses. Until then, see `Make.stmt` for an approximate list.
|
|
237
|
+
|
|
238
|
+
Note well
|
|
239
|
+
---------
|
|
240
|
+
This prototype "shortcut" function has approximately eleventy gazillion *implied* constraints and pitfalls. If you can't get
|
|
241
|
+
it to do what you want, I recommend saving yourself a bunch of stress and not using this shortcut.
|
|
242
|
+
"""
|
|
243
|
+
astModule: ast.Module = ast.parse(string)
|
|
244
|
+
findThis = Be.Module
|
|
245
|
+
doThat = "not today; I can't think through the pain."
|
|
246
|
+
|
|
209
247
|
def removeUnusedParameters(ingredientsFunction: IngredientsFunction) -> IngredientsFunction:
|
|
210
248
|
"""
|
|
211
249
|
Remove unused parameters from `IngredientsFunction.astFunctionDef`, but not from `import` statements.
|
|
@@ -367,9 +405,10 @@ def write_astModule(ingredients: IngredientsModule, pathFilename: PathLike[Any]
|
|
|
367
405
|
"""
|
|
368
406
|
astModule = Make.Module(ingredients.body, ingredients.type_ignores)
|
|
369
407
|
ast.fix_missing_locations(astModule)
|
|
370
|
-
pythonSource: str =
|
|
408
|
+
pythonSource: str = ast.unparse(astModule)
|
|
371
409
|
autoflake_additional_imports: list[str] = ingredients.imports.exportListModuleIdentifiers()
|
|
372
410
|
if packageName:
|
|
373
411
|
autoflake_additional_imports.append(packageName)
|
|
374
412
|
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=True, remove_duplicate_keys = False, remove_unused_variables = False)
|
|
375
413
|
writeStringToHere(pythonSource, pathFilename)
|
|
414
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: astToolkit
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: A powerfully composable, type-safe toolkit for Python abstract syntax tree (AST) manipulation, analysis, transformation, and code generation with a layered architecture designed for building sophisticated code processing assembly-lines.
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -354,4 +354,4 @@ Coding One Step at a Time:
|
|
|
354
354
|
2. Write good code.
|
|
355
355
|
3. When revising, write better code.
|
|
356
356
|
|
|
357
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "astToolkit"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.2"
|
|
4
4
|
description = "A powerfully composable, type-safe toolkit for Python abstract syntax tree (AST) manipulation, analysis, transformation, and code generation with a layered architecture designed for building sophisticated code processing assembly-lines."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|