astToolkit 0.7.1__tar.gz → 0.7.3__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.3}/PKG-INFO +2 -2
  2. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/README.md +1 -1
  3. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/__init__.py +1 -1
  4. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolGrab.py +1 -13
  5. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolMake.py +1 -19
  6. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolkitAST.py +6 -10
  7. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/transformationTools.py +57 -18
  8. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit.egg-info/PKG-INFO +2 -2
  9. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/pyproject.toml +1 -1
  10. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/LICENSE +0 -0
  11. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_astTypes.py +0 -0
  12. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_dumpFunctionDef.py +0 -0
  13. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_dumpHandmade.py +0 -0
  14. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_prototypeFind.py +0 -0
  15. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_theSSOT.py +0 -0
  16. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolBe.py +0 -0
  17. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolDOT.py +0 -0
  18. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolIfThis.py +0 -0
  19. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolThen.py +0 -0
  20. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolkitContainers.py +0 -0
  21. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/_toolkitNodeVisitor.py +0 -0
  22. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit/py.typed +0 -0
  23. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit.egg-info/SOURCES.txt +0 -0
  24. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit.egg-info/dependency_links.txt +0 -0
  25. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit.egg-info/requires.txt +0 -0
  26. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/astToolkit.egg-info/top_level.txt +0 -0
  27. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/setup.cfg +0 -0
  28. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/tests/__init__.py +0 -0
  29. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/tests/conftest.py +0 -0
  30. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/tests/dataSamples/Make.py +0 -0
  31. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/tests/dataSamples/__init__.py +0 -0
  32. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/tests/dataSamples/createTestData.py +0 -0
  33. {asttoolkit-0.7.1 → asttoolkit-0.7.3}/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.3
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/)
@@ -120,7 +120,7 @@ from astToolkit._toolkitNodeVisitor import (
120
120
 
121
121
  from astToolkit._toolBe import Be as Be
122
122
  from astToolkit._toolDOT import DOT as DOT
123
- from astToolkit._toolGrab import Grab as Grab, index as index, indices as indices
123
+ from astToolkit._toolGrab import Grab as Grab
124
124
  from astToolkit._toolMake import Make as Make
125
125
  from astToolkit._prototypeFind import Find as Find
126
126
 
@@ -12,25 +12,13 @@ from astToolkit import (
12
12
  hasDOTsubject, hasDOTtag, hasDOTtarget, hasDOTtargets, hasDOTtest, hasDOTtype, hasDOTtype_comment, hasDOTtype_ignores,
13
13
  hasDOTtype_params, hasDOTupper, hasDOTvalue, hasDOTvalues, hasDOTvararg, 一符, 个, 二符, 俪, 口, 工, 工位, 布尔符, 形, 比符)
14
14
  from collections.abc import Callable, Sequence
15
- from typing import Any, cast
15
+ from typing import Any
16
16
  import ast
17
17
  import sys
18
18
 
19
19
  if sys.version_info >= (3, 13):
20
20
  from astToolkit import hasDOTdefault_value
21
21
 
22
- def index[个, 归个](at: int) -> Callable[[Sequence[个]], 归个]:
23
-
24
- def workhorse(node: Sequence[个]) -> 归个:
25
- return cast('归个', node[at])
26
- return workhorse
27
-
28
- def indices[个, 归个](at: slice) -> Callable[[Sequence[个]], 归个]:
29
-
30
- def workhorse(node: Sequence[个]) -> 归个:
31
- return cast('归个', node[at])
32
- return workhorse
33
-
34
22
  class Grab:
35
23
  """Modify specific attributes of AST nodes while preserving the node structure.
36
24
 
@@ -2422,25 +2422,7 @@ class Make:
2422
2422
 
2423
2423
  @staticmethod
2424
2424
  def mod() -> ast.mod:
2425
- """Create an appropriate `ast.mod` (***mod***ule) node based on the body content.
2426
-
2427
- (AI generated docstring)
2428
-
2429
- The `mod` method creates the appropriate module type node based on the
2430
- provided body. This is a convenience method that determines whether to
2431
- create a `Module`, `Expression`, or `Interactive` node.
2432
-
2433
- Parameters
2434
- ----------
2435
- body : list[ast.stmt] | ast.expr
2436
- Either a list of statements or a single expression
2437
-
2438
- Returns
2439
- -------
2440
- nodeMod : ast.mod
2441
- The constructed module node of appropriate type
2442
-
2443
- """
2425
+ """Create an abstract `ast.mod` (***mod***ule) `object`."""
2444
2426
  return ast.mod()
2445
2427
 
2446
2428
  class Mod(ast.Mod):
@@ -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.3
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.3"
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