Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mako/pyparser.py: 76%
140 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-04 06:22 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-04 06:22 +0000
1# mako/pyparser.py
2# Copyright 2006-2024 the Mako authors and contributors <see AUTHORS file>
3#
4# This module is part of Mako and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
7"""Handles parsing of Python code.
9Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler
10module is used.
11"""
13import operator
15import _ast
17from mako import _ast_util
18from mako import compat
19from mako import exceptions
20from mako import util
22# words that cannot be assigned to (notably
23# smaller than the total keys in __builtins__)
24reserved = {"True", "False", "None", "print"}
26# the "id" attribute on a function node
27arg_id = operator.attrgetter("arg")
29util.restore__ast(_ast)
32def parse(code, mode="exec", **exception_kwargs):
33 """Parse an expression into AST"""
35 try:
36 return _ast_util.parse(code, "<unknown>", mode)
37 except Exception as e:
38 raise exceptions.SyntaxException(
39 "(%s) %s (%r)"
40 % (
41 compat.exception_as().__class__.__name__,
42 compat.exception_as(),
43 code[0:50],
44 ),
45 **exception_kwargs,
46 ) from e
49class FindIdentifiers(_ast_util.NodeVisitor):
50 def __init__(self, listener, **exception_kwargs):
51 self.in_function = False
52 self.in_assign_targets = False
53 self.local_ident_stack = set()
54 self.listener = listener
55 self.exception_kwargs = exception_kwargs
57 def _add_declared(self, name):
58 if not self.in_function:
59 self.listener.declared_identifiers.add(name)
60 else:
61 self.local_ident_stack.add(name)
63 def visit_ClassDef(self, node):
64 self._add_declared(node.name)
66 def visit_Assign(self, node):
67 # flip around the visiting of Assign so the expression gets
68 # evaluated first, in the case of a clause like "x=x+5" (x
69 # is undeclared)
71 self.visit(node.value)
72 in_a = self.in_assign_targets
73 self.in_assign_targets = True
74 for n in node.targets:
75 self.visit(n)
76 self.in_assign_targets = in_a
78 def visit_ExceptHandler(self, node):
79 if node.name is not None:
80 self._add_declared(node.name)
81 if node.type is not None:
82 self.visit(node.type)
83 for statement in node.body:
84 self.visit(statement)
86 def visit_Lambda(self, node, *args):
87 self._visit_function(node, True)
89 def visit_FunctionDef(self, node):
90 self._add_declared(node.name)
91 self._visit_function(node, False)
93 def visit_ListComp(self, node):
94 if self.in_function:
95 if not isinstance(node.elt, _ast.Name):
96 self.visit(node.elt)
97 for comp in node.generators:
98 self.visit(comp.iter)
99 else:
100 self.generic_visit(node)
102 visit_SetComp = visit_GeneratorExp = visit_ListComp
104 def visit_DictComp(self, node):
105 if self.in_function:
106 if not isinstance(node.key, _ast.Name):
107 self.visit(node.elt)
108 for comp in node.generators:
109 self.visit(comp.iter)
110 else:
111 self.generic_visit(node)
113 def _expand_tuples(self, args):
114 for arg in args:
115 if isinstance(arg, _ast.Tuple):
116 yield from arg.elts
117 else:
118 yield arg
120 def _visit_function(self, node, islambda):
121 # push function state onto stack. dont log any more
122 # identifiers as "declared" until outside of the function,
123 # but keep logging identifiers as "undeclared". track
124 # argument names in each function header so they arent
125 # counted as "undeclared"
127 inf = self.in_function
128 self.in_function = True
130 local_ident_stack = self.local_ident_stack
131 self.local_ident_stack = local_ident_stack.union(
132 [arg_id(arg) for arg in self._expand_tuples(node.args.args)]
133 )
134 if islambda:
135 self.visit(node.body)
136 else:
137 for n in node.body:
138 self.visit(n)
139 self.in_function = inf
140 self.local_ident_stack = local_ident_stack
142 def visit_For(self, node):
143 # flip around visit
145 self.visit(node.iter)
146 self.visit(node.target)
147 for statement in node.body:
148 self.visit(statement)
149 for statement in node.orelse:
150 self.visit(statement)
152 def visit_Name(self, node):
153 if isinstance(node.ctx, _ast.Store):
154 # this is eqiuvalent to visit_AssName in
155 # compiler
156 self._add_declared(node.id)
157 elif (
158 node.id not in reserved
159 and node.id not in self.listener.declared_identifiers
160 and node.id not in self.local_ident_stack
161 ):
162 self.listener.undeclared_identifiers.add(node.id)
164 def visit_Import(self, node):
165 for name in node.names:
166 if name.asname is not None:
167 self._add_declared(name.asname)
168 else:
169 self._add_declared(name.name.split(".")[0])
171 def visit_ImportFrom(self, node):
172 for name in node.names:
173 if name.asname is not None:
174 self._add_declared(name.asname)
175 elif name.name == "*":
176 raise exceptions.CompileException(
177 "'import *' is not supported, since all identifier "
178 "names must be explicitly declared. Please use the "
179 "form 'from <modulename> import <name1>, <name2>, "
180 "...' instead.",
181 **self.exception_kwargs,
182 )
183 else:
184 self._add_declared(name.name)
187class FindTuple(_ast_util.NodeVisitor):
188 def __init__(self, listener, code_factory, **exception_kwargs):
189 self.listener = listener
190 self.exception_kwargs = exception_kwargs
191 self.code_factory = code_factory
193 def visit_Tuple(self, node):
194 for n in node.elts:
195 p = self.code_factory(n, **self.exception_kwargs)
196 self.listener.codeargs.append(p)
197 self.listener.args.append(ExpressionGenerator(n).value())
198 ldi = self.listener.declared_identifiers
199 self.listener.declared_identifiers = ldi.union(
200 p.declared_identifiers
201 )
202 lui = self.listener.undeclared_identifiers
203 self.listener.undeclared_identifiers = lui.union(
204 p.undeclared_identifiers
205 )
208class ParseFunc(_ast_util.NodeVisitor):
209 def __init__(self, listener, **exception_kwargs):
210 self.listener = listener
211 self.exception_kwargs = exception_kwargs
213 def visit_FunctionDef(self, node):
214 self.listener.funcname = node.name
216 argnames = [arg_id(arg) for arg in node.args.args]
217 if node.args.vararg:
218 argnames.append(node.args.vararg.arg)
220 kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
221 if node.args.kwarg:
222 kwargnames.append(node.args.kwarg.arg)
223 self.listener.argnames = argnames
224 self.listener.defaults = node.args.defaults # ast
225 self.listener.kwargnames = kwargnames
226 self.listener.kwdefaults = node.args.kw_defaults
227 self.listener.varargs = node.args.vararg
228 self.listener.kwargs = node.args.kwarg
231class ExpressionGenerator:
232 def __init__(self, astnode):
233 self.generator = _ast_util.SourceGenerator(" " * 4)
234 self.generator.visit(astnode)
236 def value(self):
237 return "".join(self.generator.result)