astToolkit 0.1.0__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.
@@ -0,0 +1,192 @@
1
+ """
2
+ AST Node Predicate and Access Utilities for Pattern Matching and Traversal
3
+
4
+ This module provides utilities for accessing and matching AST nodes in a consistent way. It contains three primary
5
+ classes:
6
+
7
+ 1. DOT: Provides consistent accessor methods for AST node attributes across different node types, simplifying the access
8
+ to node properties.
9
+
10
+ 2. be: Offers type-guard functions that verify AST node types, enabling safe type narrowing for static type checking and
11
+ improving code safety.
12
+
13
+ 3. ifThis: Contains predicate functions for matching AST nodes based on various criteria, enabling precise targeting of
14
+ nodes for analysis or transformation.
15
+
16
+ These utilities form the foundation of the pattern-matching component in the AST manipulation framework, working in
17
+ conjunction with the NodeChanger and NodeTourist classes to enable precise and targeted code transformations. Together,
18
+ they implement a declarative approach to AST manipulation that separates node identification (ifThis), type verification
19
+ (be), and data access (DOT).
20
+ """
21
+
22
+ from collections.abc import Callable
23
+ from astToolkit import ast_Identifier, Be, DOT, ImaCallToName
24
+ from typing import Any, TypeGuard
25
+ import ast
26
+
27
+ class IfThis:
28
+ """
29
+ Provide predicate functions for matching and filtering AST nodes based on various criteria.
30
+
31
+ The ifThis class contains static methods that generate predicate functions used to test whether AST nodes match
32
+ specific criteria. These predicates can be used with NodeChanger and NodeTourist to identify and process specific
33
+ patterns in the AST.
34
+
35
+ The class provides predicates for matching various node types, attributes, identifiers, and structural patterns,
36
+ enabling precise targeting of AST elements for analysis or transformation.
37
+ """
38
+ @staticmethod
39
+ def is_arg_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg] | bool]:
40
+ """see also `isArgument_Identifier`"""
41
+ return lambda node: Be.arg(node) and IfThis.isIdentifier(identifier)(DOT.arg(node))
42
+ @staticmethod
43
+ def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
44
+ """see also `isArgument_Identifier`"""
45
+ return lambda node: Be.keyword(node) and IfThis.isIdentifier(identifier)(DOT.arg(node))
46
+
47
+ @staticmethod
48
+ def isAnnAssign_targetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
49
+ def workhorse(node: ast.AST) -> TypeGuard[ast.AnnAssign] | bool:
50
+ return Be.AnnAssign(node) and targetPredicate(DOT.target(node))
51
+ return workhorse
52
+
53
+ @staticmethod
54
+ def isArgument_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg | ast.keyword] | bool]:
55
+ return lambda node: (Be.arg(node) or Be.keyword(node)) and IfThis.isIdentifier(identifier)(DOT.arg(node))
56
+
57
+ @staticmethod
58
+ def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
59
+ """ `node` is `ast.Assign` and `node.targets[0]` matches `targets0Predicate`."""
60
+ return lambda node: Be.Assign(node) and targets0Predicate(node.targets[0])
61
+ @staticmethod
62
+ def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
63
+ """ `node` is `ast.Assign` and `node.value` matches `valuePredicate`. """
64
+ return lambda node: Be.Assign(node) and valuePredicate(DOT.value(node))
65
+
66
+ @staticmethod
67
+ def isAttribute_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
68
+ """ 1. `node` is `ast.Attribute`,
69
+ 2. zero or more direct descendants in an unbroken chain are `ast.Attribute`, `ast.Subscript`, or `ast.Starred`,
70
+ 3. the direct descendant chain ends with `ast.Name`, and
71
+ 4. the `ast.Name` `id` attribute is `identifier`."""
72
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Attribute]:
73
+ return Be.Attribute(node) and IfThis.isNestedName_Identifier(identifier)(DOT.value(node))
74
+ return workhorse
75
+ @staticmethod
76
+ def isAttributeName(node: ast.AST) -> TypeGuard[ast.Attribute]:
77
+ """ Displayed as Name.attribute."""
78
+ return Be.Attribute(node) and Be.Name(DOT.value(node))
79
+ @staticmethod
80
+ def isAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
81
+ return lambda node: IfThis.isAttributeName(node) and IfThis.isName_Identifier(namespace)(DOT.value(node)) and IfThis.isIdentifier(identifier)(DOT.attr(node))
82
+
83
+ @staticmethod
84
+ def isAttributeNamespace_IdentifierLessThanOrEqual(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Compare] | bool]:
85
+ return lambda node: (Be.Compare(node)
86
+ and IfThis.isAttributeNamespace_Identifier(namespace, identifier)(node.left)
87
+ and Be.LtE(node.ops[0]))
88
+
89
+ @staticmethod
90
+ def isAugAssignAndTargetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AugAssign] | bool]:
91
+ def workhorse(node: ast.AST) -> TypeGuard[ast.AugAssign] | bool:
92
+ return Be.AugAssign(node) and targetPredicate(DOT.target(node))
93
+ return workhorse
94
+
95
+ @staticmethod
96
+ def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ImaCallToName] | bool]:
97
+ def workhorse(node: ast.AST) -> TypeGuard[ImaCallToName] | bool:
98
+ return IfThis.isCallToName(node) and IfThis.isIdentifier(identifier)(DOT.id(DOT.func(node)))
99
+ return workhorse
100
+
101
+ @staticmethod
102
+ def isCallAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
103
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Call] | bool:
104
+ return Be.Call(node) and IfThis.isAttributeNamespace_Identifier(namespace, identifier)(DOT.func(node))
105
+ return workhorse
106
+ @staticmethod
107
+ def isCallToName(node: ast.AST) -> TypeGuard[ImaCallToName]:
108
+ return Be.Call(node) and Be.Name(DOT.func(node))
109
+
110
+ @staticmethod
111
+ def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
112
+ return lambda node: Be.ClassDef(node) and IfThis.isIdentifier(identifier)(DOT.name(node))
113
+
114
+ @staticmethod
115
+ def isConstant_value(value: Any) -> Callable[[ast.AST], TypeGuard[ast.Constant] | bool]:
116
+ return lambda node: Be.Constant(node) and DOT.value(node) == value
117
+
118
+ @staticmethod
119
+ def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef] | bool]:
120
+ return lambda node: Be.FunctionDef(node) and IfThis.isIdentifier(identifier)(DOT.name(node))
121
+
122
+ @staticmethod
123
+ def isIdentifier(identifier: ast_Identifier) -> Callable[[ast_Identifier | None], TypeGuard[ast_Identifier] | bool]:
124
+ return lambda node: node == identifier
125
+
126
+ @staticmethod
127
+ def isIfUnaryNotAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.If] | bool]:
128
+ return lambda node: (Be.If(node)
129
+ and IfThis.isUnaryNotAttributeNamespace_Identifier(namespace, identifier)(node.test))
130
+
131
+ @staticmethod
132
+ def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name] | bool]:
133
+ return lambda node: Be.Name(node) and IfThis.isIdentifier(identifier)(DOT.id(node))
134
+ @staticmethod
135
+ def isNestedName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute | ast.Starred | ast.Subscript] | bool]:
136
+ """ `node` is `ast.Name`
137
+
138
+ OR
139
+
140
+ 1. `node` is one of `ast.Attribute`, `ast.Subscript`, or `ast.Starred`,
141
+ 2. zero or more direct descendants in an unbroken chain are `ast.Attribute`, `ast.Subscript`, or `ast.Starred`,
142
+ 3. the direct descendant chain ends with `ast.Name`,
143
+
144
+ and
145
+
146
+ The `ast.Name` `id` attribute is `identifier`."""
147
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Attribute | ast.Starred | ast.Subscript] | bool:
148
+ return IfThis.isName_Identifier(identifier)(node) or IfThis.isAttribute_Identifier(identifier)(node) or IfThis.isSubscript_Identifier(identifier)(node) or IfThis.isStarred_Identifier(identifier)(node)
149
+ return workhorse
150
+
151
+ @staticmethod
152
+ def isStarred_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Starred] | bool]:
153
+ """ 1. `node` is `ast.Starred`,
154
+ 2. zero or more direct descendants in an unbroken chain are `ast.Attribute`, `ast.Subscript`, or `ast.Starred`,
155
+ 3. the direct descendant chain ends with `ast.Name`, and
156
+ 4. the `ast.Name` `id` attribute is `identifier`."""
157
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Starred]:
158
+ return Be.Starred(node) and IfThis.isNestedName_Identifier(identifier)(DOT.value(node))
159
+ return workhorse
160
+ @staticmethod
161
+ def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
162
+ """ 1. `node` is `ast.Subscript`,
163
+ 2. zero or more direct descendants in an unbroken chain are `ast.Attribute`, `ast.Subscript`, or `ast.Starred`,
164
+ 3. the direct descendant chain ends with `ast.Name`, and
165
+ 4. the `ast.Name` `id` attribute is `identifier`."""
166
+ def workhorse(node: ast.AST) -> TypeGuard[ast.Subscript]:
167
+ return Be.Subscript(node) and IfThis.isNestedName_Identifier(identifier)(DOT.value(node))
168
+ return workhorse
169
+
170
+ @staticmethod
171
+ def isUnaryNotAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.UnaryOp] | bool]:
172
+ return lambda node: (Be.UnaryOp(node)
173
+ and Be.Not(node.op)
174
+ and IfThis.isAttributeNamespace_Identifier(namespace, identifier)(node.operand))
175
+
176
+ @staticmethod
177
+ def matchesMeButNotAnyDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
178
+ return lambda node: predicate(node) and IfThis.matchesNoDescendant(predicate)(node)
179
+ @staticmethod
180
+ def matchesNoDescendant(predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], bool]:
181
+ def workhorse(node: ast.AST) -> bool:
182
+ for descendant in ast.walk(node):
183
+ if descendant is not node and predicate(descendant):
184
+ return False
185
+ return True
186
+ return workhorse
187
+
188
+ @staticmethod
189
+ def unparseIs(astAST: ast.AST) -> Callable[[ast.AST], bool]:
190
+ def workhorse(node: ast.AST) -> bool:
191
+ return ast.unparse(node) == ast.unparse(astAST)
192
+ return workhorse
@@ -0,0 +1,339 @@
1
+ """This file is generated automatically, so changes to this file will be lost."""
2
+ from collections.abc import Sequence
3
+ from astToolkit import astDOTParamSpec, astDOTTryStar, astDOTTypeAlias, astDOTTypeVar, astDOTTypeVarTuple, astDOTtype_param
4
+ from astToolkit import ast_Identifier, ast_expr_Slice, intORstr, intORstrORtype_params, intORtype_params, str_nameDOTname
5
+ from typing import Any, Literal
6
+ import ast
7
+
8
+ class Make:
9
+ """
10
+ Almost all parameters described here are only accessible through a method's `**keywordArguments` parameter.
11
+
12
+ Parameters:
13
+ context (ast.Load()): Are you loading from, storing to, or deleting the identifier? The `context` (also, `ctx`) value is `ast.Load()`, `ast.Store()`, or `ast.Del()`.
14
+ col_offset (0): int Position information specifying the column where an AST node begins.
15
+ end_col_offset (None): int|None Position information specifying the column where an AST node ends.
16
+ end_lineno (None): int|None Position information specifying the line number where an AST node ends.
17
+ level (0): int Module import depth level that controls relative vs absolute imports. Default 0 indicates absolute import.
18
+ lineno: int Position information manually specifying the line number where an AST node begins.
19
+ kind (None): str|None Used for type annotations in limited cases.
20
+ type_comment (None): str|None "type_comment is an optional string with the type annotation as a comment." or `# type: ignore`.
21
+ type_params: list[ast.type_param] Type parameters for generic type definitions.
22
+
23
+ The `ast._Attributes`, lineno, col_offset, end_lineno, and end_col_offset, hold position information; however, they are, importantly, _not_ `ast._fields`.
24
+ """
25
+
26
+ @staticmethod
27
+ def alias(name: ast_Identifier, asName: ast_Identifier | None=None, **keywordArguments: int) -> ast.alias:
28
+ return ast.alias(name, asName, **keywordArguments)
29
+
30
+ @staticmethod
31
+ def AnnAssign(target: ast.Name | ast.Attribute | ast.Subscript, annotation: ast.expr, value: ast.expr | None, **keywordArguments: int) -> ast.AnnAssign:
32
+ return ast.AnnAssign(target, annotation, value, **keywordArguments, simple=int(isinstance(target, ast.Name)))
33
+
34
+ @staticmethod
35
+ def arg(arg: ast_Identifier, annotation: ast.expr | None, **keywordArguments: intORstr) -> ast.arg:
36
+ return ast.arg(arg, annotation, **keywordArguments)
37
+
38
+ @staticmethod
39
+ def arguments(posonlyargs: list[ast.arg]=[], args: list[ast.arg]=[], vararg: ast.arg | None=None, kwonlyargs: list[ast.arg]=[], kw_defaults: Sequence[ast.expr | None]=[None], kwarg: ast.arg | None=None, defaults: Sequence[ast.expr]=[], **keywordArguments: int) -> ast.arguments:
40
+ return ast.arguments(posonlyargs, args, vararg, kwonlyargs, list(kw_defaults), kwarg, list(defaults), **keywordArguments)
41
+
42
+ @staticmethod
43
+ def Assert(test: ast.expr, msg: ast.expr | None, **keywordArguments: int) -> ast.Assert:
44
+ return ast.Assert(test, msg, **keywordArguments)
45
+
46
+ @staticmethod
47
+ def Assign(targets: Sequence[ast.expr], value: ast.expr, **keywordArguments: intORstr) -> ast.Assign:
48
+ return ast.Assign(list(targets), value, **keywordArguments)
49
+
50
+ @staticmethod
51
+ def AsyncFor(target: ast.expr, iter: ast.expr, body: Sequence[ast.stmt], orElse: Sequence[ast.stmt]=[], **keywordArguments: intORstr) -> ast.AsyncFor:
52
+ return ast.AsyncFor(target, iter, list(body), list(orElse), **keywordArguments)
53
+
54
+ @staticmethod
55
+ def AsyncFunctionDef(name: ast_Identifier, args: ast.arguments, body: Sequence[ast.stmt], decorator_list: Sequence[ast.expr]=[], returns: ast.expr | None=None, **keywordArguments: intORstrORtype_params) -> ast.AsyncFunctionDef:
56
+ return ast.AsyncFunctionDef(name, args, list(body), list(decorator_list), returns, **keywordArguments)
57
+
58
+ @staticmethod
59
+ def AsyncWith(items: list[ast.withitem], body: Sequence[ast.stmt], **keywordArguments: intORstr) -> ast.AsyncWith:
60
+ return ast.AsyncWith(items, list(body), **keywordArguments)
61
+
62
+ @staticmethod
63
+ def Attribute(value: ast.expr, *attribute: ast_Identifier, context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.Attribute:
64
+ """ If two `ast_Identifier` are joined by a dot `.`, they are _usually_ an `ast.Attribute`, but see `ast.ImportFrom`.
65
+ Parameters:
66
+ value: the part before the dot (e.g., `ast.Name`.)
67
+ attribute: an `ast_Identifier` after a dot `.`; you can pass multiple `attribute` and they will be chained together.
68
+ """
69
+
70
+ def addDOTattribute(chain: ast.expr, identifier: ast_Identifier, context: ast.expr_context, **keywordArguments: int) -> ast.Attribute:
71
+ return ast.Attribute(value=chain, attr=identifier, ctx=context, **keywordArguments)
72
+ buffaloBuffalo = addDOTattribute(value, attribute[0], context, **keywordArguments)
73
+ for identifier in attribute[1:None]:
74
+ buffaloBuffalo = addDOTattribute(buffaloBuffalo, identifier, context, **keywordArguments)
75
+ return buffaloBuffalo
76
+
77
+ @staticmethod
78
+ def AugAssign(target: ast.Name | ast.Attribute | ast.Subscript, op: ast.operator, value: ast.expr, **keywordArguments: int) -> ast.AugAssign:
79
+ return ast.AugAssign(target, op, value, **keywordArguments)
80
+
81
+ @staticmethod
82
+ def Await(value: ast.expr, **keywordArguments: int) -> ast.Await:
83
+ return ast.Await(value, **keywordArguments)
84
+
85
+ @staticmethod
86
+ def BinOp(left: ast.expr, op: ast.operator, right: ast.expr, **keywordArguments: int) -> ast.BinOp:
87
+ return ast.BinOp(left, op, right, **keywordArguments)
88
+
89
+ @staticmethod
90
+ def BoolOp(op: ast.boolop, values: Sequence[ast.expr], **keywordArguments: int) -> ast.BoolOp:
91
+ return ast.BoolOp(op, list(values), **keywordArguments)
92
+
93
+ @staticmethod
94
+ def Call(callee: ast.expr, args: Sequence[ast.expr]=[], list_keyword: list[ast.keyword]=[], **keywordArguments: int) -> ast.Call:
95
+ return ast.Call(callee, list(args), list_keyword, **keywordArguments)
96
+
97
+ @staticmethod
98
+ def ClassDef(name: ast_Identifier, bases: Sequence[ast.expr], list_keyword: list[ast.keyword]=[], body: Sequence[ast.stmt]=[], decorator_list: Sequence[ast.expr]=[], **keywordArguments: intORtype_params) -> ast.ClassDef:
99
+ return ast.ClassDef(name, list(bases), list_keyword, list(body), list(decorator_list), **keywordArguments)
100
+
101
+ @staticmethod
102
+ def Compare(left: ast.expr, ops: Sequence[ast.cmpop], comparators: Sequence[ast.expr], **keywordArguments: int) -> ast.Compare:
103
+ return ast.Compare(left, list(ops), list(comparators), **keywordArguments)
104
+
105
+ @staticmethod
106
+ def comprehension(target: ast.expr, iter: ast.expr, ifs: Sequence[ast.expr], is_async: int, **keywordArguments: int) -> ast.comprehension:
107
+ return ast.comprehension(target, iter, list(ifs), is_async, **keywordArguments)
108
+
109
+ @staticmethod
110
+ def Constant(value: Any, **keywordArguments: intORstr) -> ast.Constant:
111
+ return ast.Constant(value, **keywordArguments)
112
+
113
+ @staticmethod
114
+ def Delete(targets: Sequence[ast.expr], **keywordArguments: int) -> ast.Delete:
115
+ return ast.Delete(list(targets), **keywordArguments)
116
+
117
+ @staticmethod
118
+ def Dict(keys: Sequence[ast.expr | None], values: Sequence[ast.expr], **keywordArguments: int) -> ast.Dict:
119
+ return ast.Dict(list(keys), list(values), **keywordArguments)
120
+
121
+ @staticmethod
122
+ def DictComp(key: ast.expr, value: ast.expr, generators: list[ast.comprehension], **keywordArguments: int) -> ast.DictComp:
123
+ return ast.DictComp(key, value, generators, **keywordArguments)
124
+
125
+ @staticmethod
126
+ def ExceptHandler(type: ast.expr | None, name: ast_Identifier | None, body: Sequence[ast.stmt], **keywordArguments: int) -> ast.ExceptHandler:
127
+ return ast.ExceptHandler(type, name, list(body), **keywordArguments)
128
+
129
+ @staticmethod
130
+ def Expr(value: ast.expr, **keywordArguments: int) -> ast.Expr:
131
+ return ast.Expr(value, **keywordArguments)
132
+
133
+ @staticmethod
134
+ def Expression(body: ast.expr) -> ast.Expression:
135
+ return ast.Expression(body)
136
+
137
+ @staticmethod
138
+ def For(target: ast.expr, iter: ast.expr, body: Sequence[ast.stmt], orElse: Sequence[ast.stmt]=[], **keywordArguments: intORstr) -> ast.For:
139
+ return ast.For(target, iter, list(body), list(orElse), **keywordArguments)
140
+
141
+ @staticmethod
142
+ def FormattedValue(value: ast.expr, conversion: int, format_spec: ast.expr | None, **keywordArguments: int) -> ast.FormattedValue:
143
+ return ast.FormattedValue(value, conversion, format_spec, **keywordArguments)
144
+
145
+ @staticmethod
146
+ def FunctionDef(name: ast_Identifier, args: ast.arguments, body: Sequence[ast.stmt], decorator_list: Sequence[ast.expr]=[], returns: ast.expr | None=None, **keywordArguments: intORstrORtype_params) -> ast.FunctionDef:
147
+ return ast.FunctionDef(name, args, list(body), list(decorator_list), returns, **keywordArguments)
148
+
149
+ @staticmethod
150
+ def FunctionType(argtypes: Sequence[ast.expr], returns: ast.expr) -> ast.FunctionType:
151
+ return ast.FunctionType(list(argtypes), returns)
152
+
153
+ @staticmethod
154
+ def GeneratorExp(elt: ast.expr, generators: list[ast.comprehension], **keywordArguments: int) -> ast.GeneratorExp:
155
+ return ast.GeneratorExp(elt, generators, **keywordArguments)
156
+
157
+ @staticmethod
158
+ def Global(names: list[ast_Identifier], **keywordArguments: int) -> ast.Global:
159
+ return ast.Global(names, **keywordArguments)
160
+
161
+ @staticmethod
162
+ def If(test: ast.expr, body: Sequence[ast.stmt], orElse: Sequence[ast.stmt]=[], **keywordArguments: int) -> ast.If:
163
+ return ast.If(test, list(body), list(orElse), **keywordArguments)
164
+
165
+ @staticmethod
166
+ def IfExp(test: ast.expr, body: ast.expr, orElse: ast.expr, **keywordArguments: int) -> ast.IfExp:
167
+ return ast.IfExp(test, body, orElse, **keywordArguments)
168
+
169
+ @staticmethod
170
+ def Import(moduleWithLogicalPath: str_nameDOTname, asName: ast_Identifier | None=None, **keywordArguments: int) -> ast.Import:
171
+ return ast.Import(names=[Make.alias(moduleWithLogicalPath, asName)], **keywordArguments)
172
+
173
+ @staticmethod
174
+ def ImportFrom(module: ast_Identifier | None, list_alias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
175
+ return ast.ImportFrom(module, list_alias, **keywordArguments, level=0)
176
+
177
+ @staticmethod
178
+ def Interactive(body: Sequence[ast.stmt]) -> ast.Interactive:
179
+ return ast.Interactive(list(body))
180
+
181
+ @staticmethod
182
+ def JoinedStr(values: Sequence[ast.expr], **keywordArguments: int) -> ast.JoinedStr:
183
+ return ast.JoinedStr(list(values), **keywordArguments)
184
+
185
+ @staticmethod
186
+ def keyword(arg: ast_Identifier | None, value: ast.expr, **keywordArguments: int) -> ast.keyword:
187
+ return ast.keyword(arg, value, **keywordArguments)
188
+
189
+ @staticmethod
190
+ def Lambda(args: ast.arguments, body: ast.expr, **keywordArguments: int) -> ast.Lambda:
191
+ return ast.Lambda(args, body, **keywordArguments)
192
+
193
+ @staticmethod
194
+ def List(elts: Sequence[ast.expr], context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.List:
195
+ return ast.List(list(elts), context, **keywordArguments)
196
+
197
+ @staticmethod
198
+ def ListComp(elt: ast.expr, generators: list[ast.comprehension], **keywordArguments: int) -> ast.ListComp:
199
+ return ast.ListComp(elt, generators, **keywordArguments)
200
+
201
+ @staticmethod
202
+ def Match(subject: ast.expr, cases: list[ast.match_case], **keywordArguments: int) -> ast.Match:
203
+ return ast.Match(subject, cases, **keywordArguments)
204
+
205
+ @staticmethod
206
+ def match_case(pattern: ast.pattern, guard: ast.expr | None, body: Sequence[ast.stmt], **keywordArguments: int) -> ast.match_case:
207
+ return ast.match_case(pattern, guard, list(body), **keywordArguments)
208
+
209
+ @staticmethod
210
+ def MatchAs(pattern: ast.pattern | None, name: ast_Identifier | None, **keywordArguments: int) -> ast.MatchAs:
211
+ return ast.MatchAs(pattern, name, **keywordArguments)
212
+
213
+ @staticmethod
214
+ def MatchClass(cls: ast.expr, patterns: Sequence[ast.pattern], kwd_attrs: list[ast_Identifier], kwd_patterns: Sequence[ast.pattern], **keywordArguments: int) -> ast.MatchClass:
215
+ return ast.MatchClass(cls, list(patterns), kwd_attrs, list(kwd_patterns), **keywordArguments)
216
+
217
+ @staticmethod
218
+ def MatchMapping(keys: Sequence[ast.expr], patterns: Sequence[ast.pattern], rest: ast_Identifier | None, **keywordArguments: int) -> ast.MatchMapping:
219
+ return ast.MatchMapping(list(keys), list(patterns), rest, **keywordArguments)
220
+
221
+ @staticmethod
222
+ def MatchOr(patterns: Sequence[ast.pattern], **keywordArguments: int) -> ast.MatchOr:
223
+ return ast.MatchOr(list(patterns), **keywordArguments)
224
+
225
+ @staticmethod
226
+ def MatchSequence(patterns: Sequence[ast.pattern], **keywordArguments: int) -> ast.MatchSequence:
227
+ return ast.MatchSequence(list(patterns), **keywordArguments)
228
+
229
+ @staticmethod
230
+ def MatchSingleton(value: Literal[True, False] | None, **keywordArguments: int) -> ast.MatchSingleton:
231
+ return ast.MatchSingleton(value, **keywordArguments)
232
+
233
+ @staticmethod
234
+ def MatchStar(name: ast_Identifier | None, **keywordArguments: int) -> ast.MatchStar:
235
+ return ast.MatchStar(name, **keywordArguments)
236
+
237
+ @staticmethod
238
+ def MatchValue(value: ast.expr, **keywordArguments: int) -> ast.MatchValue:
239
+ return ast.MatchValue(value, **keywordArguments)
240
+
241
+ @staticmethod
242
+ def Module(body: Sequence[ast.stmt], type_ignores: list[ast.TypeIgnore]=[]) -> ast.Module:
243
+ return ast.Module(list(body), type_ignores)
244
+
245
+ @staticmethod
246
+ def Name(id: ast_Identifier, context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.Name:
247
+ return ast.Name(id, context, **keywordArguments)
248
+
249
+ @staticmethod
250
+ def NamedExpr(target: ast.Name, value: ast.expr, **keywordArguments: int) -> ast.NamedExpr:
251
+ return ast.NamedExpr(target, value, **keywordArguments)
252
+
253
+ @staticmethod
254
+ def Nonlocal(names: list[ast_Identifier], **keywordArguments: int) -> ast.Nonlocal:
255
+ return ast.Nonlocal(names, **keywordArguments)
256
+
257
+ @staticmethod
258
+ def ParamSpec(name: ast_Identifier, default_value: ast.expr | None, **keywordArguments: int) -> astDOTParamSpec:
259
+ return astDOTParamSpec(name, default_value, **keywordArguments)
260
+
261
+ @staticmethod
262
+ def Raise(exc: ast.expr | None, cause: ast.expr | None, **keywordArguments: int) -> ast.Raise:
263
+ return ast.Raise(exc, cause, **keywordArguments)
264
+
265
+ @staticmethod
266
+ def Return(value: ast.expr | None, **keywordArguments: int) -> ast.Return:
267
+ return ast.Return(value, **keywordArguments)
268
+
269
+ @staticmethod
270
+ def Set(elts: Sequence[ast.expr], **keywordArguments: int) -> ast.Set:
271
+ return ast.Set(list(elts), **keywordArguments)
272
+
273
+ @staticmethod
274
+ def SetComp(elt: ast.expr, generators: list[ast.comprehension], **keywordArguments: int) -> ast.SetComp:
275
+ return ast.SetComp(elt, generators, **keywordArguments)
276
+
277
+ @staticmethod
278
+ def Slice(lower: ast.expr | None, upper: ast.expr | None, step: ast.expr | None, **keywordArguments: int) -> ast.Slice:
279
+ return ast.Slice(lower, upper, step, **keywordArguments)
280
+
281
+ @staticmethod
282
+ def Starred(value: ast.expr, context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.Starred:
283
+ return ast.Starred(value, context, **keywordArguments)
284
+
285
+ @staticmethod
286
+ def Subscript(value: ast.expr, slice: ast_expr_Slice, context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.Subscript:
287
+ return ast.Subscript(value, slice, context, **keywordArguments)
288
+
289
+ @staticmethod
290
+ def Try(body: Sequence[ast.stmt], handlers: list[ast.ExceptHandler], orElse: Sequence[ast.stmt], finalbody: Sequence[ast.stmt]=[], **keywordArguments: int) -> ast.Try:
291
+ return ast.Try(list(body), handlers, list(orElse), list(finalbody), **keywordArguments)
292
+
293
+ @staticmethod
294
+ def TryStar(body: Sequence[ast.stmt], handlers: list[ast.ExceptHandler], orElse: Sequence[ast.stmt], finalbody: Sequence[ast.stmt]=[], **keywordArguments: int) -> astDOTTryStar:
295
+ return astDOTTryStar(list(body), handlers, list(orElse), list(finalbody), **keywordArguments)
296
+
297
+ @staticmethod
298
+ def Tuple(elts: Sequence[ast.expr], context: ast.expr_context=ast.Load(), **keywordArguments: int) -> ast.Tuple:
299
+ return ast.Tuple(list(elts), context, **keywordArguments)
300
+
301
+ @staticmethod
302
+ def TypeAlias(name: ast.Name, type_params: Sequence[astDOTtype_param], value: ast.expr, **keywordArguments: int) -> astDOTTypeAlias:
303
+ return astDOTTypeAlias(name, list(type_params), value, **keywordArguments)
304
+
305
+ @staticmethod
306
+ def TypeIgnore(lineno: int, tag: ast_Identifier, **keywordArguments: int) -> ast.TypeIgnore:
307
+ return ast.TypeIgnore(lineno, tag, **keywordArguments)
308
+
309
+ @staticmethod
310
+ def TypeVar(name: ast_Identifier, bound: ast.expr | None, default_value: ast.expr | None, **keywordArguments: int) -> astDOTTypeVar:
311
+ return astDOTTypeVar(name, bound, default_value, **keywordArguments)
312
+
313
+ @staticmethod
314
+ def TypeVarTuple(name: ast_Identifier, default_value: ast.expr | None, **keywordArguments: int) -> astDOTTypeVarTuple:
315
+ return astDOTTypeVarTuple(name, default_value, **keywordArguments)
316
+
317
+ @staticmethod
318
+ def UnaryOp(op: ast.unaryop, operand: ast.expr, **keywordArguments: int) -> ast.UnaryOp:
319
+ return ast.UnaryOp(op, operand, **keywordArguments)
320
+
321
+ @staticmethod
322
+ def While(test: ast.expr, body: Sequence[ast.stmt], orElse: Sequence[ast.stmt]=[], **keywordArguments: int) -> ast.While:
323
+ return ast.While(test, list(body), list(orElse), **keywordArguments)
324
+
325
+ @staticmethod
326
+ def With(items: list[ast.withitem], body: Sequence[ast.stmt], **keywordArguments: intORstr) -> ast.With:
327
+ return ast.With(items, list(body), **keywordArguments)
328
+
329
+ @staticmethod
330
+ def withitem(context_expr: ast.expr, optional_vars: ast.expr | None, **keywordArguments: int) -> ast.withitem:
331
+ return ast.withitem(context_expr, optional_vars, **keywordArguments)
332
+
333
+ @staticmethod
334
+ def Yield(value: ast.expr | None, **keywordArguments: int) -> ast.Yield:
335
+ return ast.Yield(value, **keywordArguments)
336
+
337
+ @staticmethod
338
+ def YieldFrom(value: ast.expr, **keywordArguments: int) -> ast.YieldFrom:
339
+ return ast.YieldFrom(value, **keywordArguments)
@@ -0,0 +1,63 @@
1
+ """
2
+ AST Node Transformation Actions for Python Code Manipulation
3
+
4
+ This module provides the Then class with static methods for generating callable action functions that specify what to do
5
+ with AST nodes that match predicates. These action functions are used primarily with NodeChanger and NodeTourist to
6
+ transform or extract information from AST nodes.
7
+
8
+ The module also contains the grab class that provides functions for modifying specific attributes of AST nodes while
9
+ preserving their structure, enabling fine-grained control when transforming AST structures.
10
+
11
+ Together, these classes provide a complete system for manipulating AST nodes once they have been identified using
12
+ predicate functions from ifThis.
13
+ """
14
+
15
+ from collections.abc import Callable, Sequence
16
+ from astToolkit import ast_Identifier, NodeORattribute
17
+ from typing import Any
18
+ import ast
19
+
20
+ class Then:
21
+ """
22
+ Provide action functions that specify what to do with AST nodes that match predicates.
23
+
24
+ The Then class contains static methods that generate action functions used with NodeChanger and NodeTourist to
25
+ transform or extract information from AST nodes that match specific predicates. These actions include node
26
+ replacement, insertion, extraction, and collection operations.
27
+
28
+ When paired with predicates from the ifThis class, Then methods complete the pattern-matching-and-action workflow
29
+ for AST manipulation.
30
+ """
31
+ @staticmethod
32
+ def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST | ast_Identifier], ast.AST | ast_Identifier]:
33
+ def workhorse(node: ast.AST | ast_Identifier) -> ast.AST | ast_Identifier:
34
+ listOfAny.append(node)
35
+ return node
36
+ return workhorse
37
+
38
+ @staticmethod
39
+ def extractIt(node: NodeORattribute) -> NodeORattribute:
40
+ return node
41
+
42
+ @staticmethod
43
+ def insertThisAbove(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
44
+ return lambda aboveMe: [*list_astAST, aboveMe]
45
+
46
+ @staticmethod
47
+ def insertThisBelow(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
48
+ return lambda belowMe: [belowMe, *list_astAST]
49
+
50
+ @staticmethod
51
+ def removeIt(_removeMe: ast.AST) -> None:
52
+ return None
53
+
54
+ @staticmethod
55
+ def replaceWith(astAST: NodeORattribute) -> Callable[[NodeORattribute], NodeORattribute]:
56
+ return lambda _replaceMe: astAST
57
+
58
+ @staticmethod
59
+ def updateKeyValueIn(key: Callable[..., Any], value: Callable[..., Any], dictionary: dict[Any, Any]) -> Callable[[ast.AST], dict[Any, Any]]:
60
+ def workhorse(node: ast.AST) -> dict[Any, Any]:
61
+ dictionary.setdefault(key(node), value(node))
62
+ return dictionary
63
+ return workhorse
@@ -0,0 +1,57 @@
1
+ from astToolkit import ast_Identifier, IfThis, IngredientsFunction, LedgerOfImports, NodeTourist, Then
2
+ from astToolkit import FREAKOUT
3
+ import ast
4
+
5
+ def astModuleToIngredientsFunction(astModule: ast.AST, identifierFunctionDef: ast_Identifier) -> IngredientsFunction:
6
+ """
7
+ Extract a function definition from an AST module and create an `IngredientsFunction`.
8
+
9
+ This function finds a function definition with the specified identifier in the given AST module, extracts it, and
10
+ stores all module imports in the `LedgerOfImports`.
11
+
12
+ Parameters:
13
+ astModule: The AST module containing the function definition.
14
+ identifierFunctionDef: The name of the function to extract.
15
+
16
+ Returns:
17
+ ingredientsFunction: `IngredientsFunction` object containing the `ast.FunctionDef` and _all_ imports from the
18
+ source module.
19
+
20
+ Raises:
21
+ FREAKOUT: If the function definition is not found.
22
+ """
23
+ astFunctionDef = extractFunctionDef(astModule, identifierFunctionDef)
24
+ if not astFunctionDef: raise FREAKOUT
25
+ return IngredientsFunction(astFunctionDef, LedgerOfImports(astModule))
26
+
27
+ def extractClassDef(module: ast.AST, identifier: ast_Identifier) -> ast.ClassDef | None:
28
+ """
29
+ Extract a class definition with a specific name from an AST module.
30
+
31
+ This function searches through an AST module for a class definition that matches the provided identifier and returns
32
+ it if found.
33
+
34
+ Parameters:
35
+ module: The AST module to search within.
36
+ identifier: The name of the class to find.
37
+
38
+ Returns:
39
+ astClassDef|None: The matching class definition AST node, or `None` if not found.
40
+ """
41
+ return NodeTourist(IfThis.isClassDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)
42
+
43
+ def extractFunctionDef(module: ast.AST, identifier: ast_Identifier) -> ast.FunctionDef | None:
44
+ """
45
+ Extract a function definition with a specific name from an AST module.
46
+
47
+ This function searches through an AST module for a function definition that matches the provided identifier and
48
+ returns it if found.
49
+
50
+ Parameters:
51
+ module: The AST module to search within.
52
+ identifier: The name of the function to find.
53
+
54
+ Returns:
55
+ astFunctionDef|None: The matching function definition AST node, or `None` if not found.
56
+ """
57
+ return NodeTourist(IfThis.isFunctionDef_Identifier(identifier), Then.extractIt).captureLastMatch(module)