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.
Files changed (33) hide show
  1. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/PKG-INFO +2 -2
  2. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/README.md +1 -1
  3. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitAST.py +6 -10
  4. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/transformationTools.py +57 -18
  5. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/PKG-INFO +2 -2
  6. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/pyproject.toml +1 -1
  7. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/LICENSE +0 -0
  8. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/__init__.py +0 -0
  9. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_astTypes.py +0 -0
  10. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_dumpFunctionDef.py +0 -0
  11. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_dumpHandmade.py +0 -0
  12. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_prototypeFind.py +0 -0
  13. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_theSSOT.py +0 -0
  14. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolBe.py +0 -0
  15. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolDOT.py +0 -0
  16. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolGrab.py +0 -0
  17. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolIfThis.py +0 -0
  18. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolMake.py +0 -0
  19. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolThen.py +0 -0
  20. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitContainers.py +0 -0
  21. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/_toolkitNodeVisitor.py +0 -0
  22. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit/py.typed +0 -0
  23. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/SOURCES.txt +0 -0
  24. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/dependency_links.txt +0 -0
  25. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/requires.txt +0 -0
  26. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/astToolkit.egg-info/top_level.txt +0 -0
  27. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/setup.cfg +0 -0
  28. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/__init__.py +0 -0
  29. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/conftest.py +0 -0
  30. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/Make.py +0 -0
  31. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/__init__.py +0 -0
  32. {asttoolkit-0.7.1 → asttoolkit-0.7.2}/tests/dataSamples/createTestData.py +0 -0
  33. {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.1
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
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
357
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.png)](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
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
315
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.png)](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 a function definition with a specific name from an AST module.
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
- The AST object to search within.
79
+ (abstract syntax tree) An `ast.AST` subclass from which to extract the function.
84
80
  identifier : str
85
- The name of the function to find.
81
+ The identifier in `ast.FunctionDef.name`.
86
82
 
87
83
  Returns
88
84
  -------
89
- astFunctionDef | None
90
- The matching function definition AST node, or `None` if not found.
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. Supports all relevant `ast.parse` parameters.
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, module: ast.Module) -> ast.FunctionDef: # noqa: C901, PLR0912
131
+ def inlineFunctionDef(identifierToInline: str, astModule: ast.Module) -> ast.FunctionDef:
132
132
  """
133
- Inline function calls within a function definition to create a self-contained function.
133
+ Synthesize a new function from `identifierToInline` with the logic "inlined" from functions called by `identifierToInline`.
134
134
 
135
- (AI generated docstring)
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
- This function takes a function identifier and a module, finds the function definition,
138
- and then recursively inlines all function calls within that function with their
139
- implementation bodies. This produces a fully inlined function that doesn't depend
140
- on other function definitions from the module.
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 name of the function to inline.
146
- module : ast.Module
147
- The AST module containing the function and its dependencies.
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 inlined function definition as an `ast.FunctionDef` object.
160
+ The synthesized function with inlined logic from other functions defined in `astModule`.
153
161
 
154
162
  Raises
155
163
  ------
156
- ValueError: If the function to inline is not found in the module.
164
+ ValueError: If `identifierToInline` does not match any `ast.FunctionDef.name` in `astModule`.
157
165
  """
158
- dictionaryFunctionDef: dict[str, ast.FunctionDef] = makeDictionaryFunctionDef(module)
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"FunctionDefToInline not found in dictionaryIdentifier2FunctionDef: {identifierToInline = }"
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
- if NodeTourist(IfThis.matchesMeButNotAnyDescendant(IfThis.isCallIdentifier(identifier)), Then.extractIt).captureLastMatch(module) is not None:
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(module) is not None:
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 = raiseIfNone(ast.unparse(astModule))
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.1
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
- [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
357
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/astToolkit/blob/main/CC-BY-NC-4.0.png)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astToolkit"
3
- version = "0.7.1"
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