Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mako/pyparser.py: 77%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# mako/pyparser.py
2# Copyright 2006-2026 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."""
9import _ast
10import operator
12from mako import _ast_util
13from mako import compat
14from mako import exceptions
16# words that cannot be assigned to (notably
17# smaller than the total keys in __builtins__)
18reserved = {"True", "False", "None", "print"}
20# the "id" attribute on a function node
21arg_id = operator.attrgetter("arg")
23# filename given to the compiler when an individual expression is parsed.
24# warnings raised against this name have no location that can be related
25# back to the template
26EXPRESSION_FILENAME = "<unknown>"
29def parse(code, mode="exec", lineno_offset=0, **exception_kwargs):
30 """Parse an expression into AST.
32 ``lineno_offset`` is the line within the template on which ``code``
33 begins, relative to the line given in ``exception_kwargs``. It is used
34 to report a syntax error against the line it occurred on, rather than
35 against the start of the construct that contains it.
37 """
39 try:
40 return _ast_util.parse(code, EXPRESSION_FILENAME, mode)
41 except Exception as e:
42 raise exceptions.SyntaxException(
43 "(%s) %s (%r)"
44 % (
45 compat.exception_as().__class__.__name__,
46 compat.exception_as(),
47 code[0:50],
48 ),
49 **_adjust_lineno(e, lineno_offset, exception_kwargs),
50 ) from e
53def _adjust_lineno(exc, lineno_offset, exception_kwargs):
54 """Return ``exception_kwargs`` with the line of ``exc`` within the parsed
55 code applied to it.
57 """
59 lineno = exception_kwargs.get("lineno")
60 exc_lineno = getattr(exc, "lineno", None)
62 if lineno is None or exc_lineno is None:
63 return exception_kwargs
65 return {
66 **exception_kwargs,
67 "lineno": lineno + lineno_offset + exc_lineno - 1,
68 }
71class FindIdentifiers(_ast_util.NodeVisitor):
72 def __init__(self, listener, **exception_kwargs):
73 self.in_function = False
74 self.in_assign_targets = False
75 self.local_ident_stack = set()
76 self.listener = listener
77 self.exception_kwargs = exception_kwargs
79 def _add_declared(self, name):
80 if not self.in_function:
81 self.listener.declared_identifiers.add(name)
82 else:
83 self.local_ident_stack.add(name)
85 def visit_ClassDef(self, node):
86 self._add_declared(node.name)
88 def visit_Assign(self, node):
89 # flip around the visiting of Assign so the expression gets
90 # evaluated first, in the case of a clause like "x=x+5" (x
91 # is undeclared)
93 self.visit(node.value)
94 in_a = self.in_assign_targets
95 self.in_assign_targets = True
96 for n in node.targets:
97 self.visit(n)
98 self.in_assign_targets = in_a
100 def visit_ExceptHandler(self, node):
101 if node.name is not None:
102 self._add_declared(node.name)
103 if node.type is not None:
104 self.visit(node.type)
105 for statement in node.body:
106 self.visit(statement)
108 def visit_Lambda(self, node, *args):
109 self._visit_function(node, True)
111 def visit_FunctionDef(self, node):
112 self._add_declared(node.name)
113 self._visit_function(node, False)
115 def visit_ListComp(self, node):
116 if self.in_function:
117 for comp in node.generators:
118 self.visit(comp.target)
119 self.visit(comp.iter)
120 else:
121 self.generic_visit(node)
123 visit_SetComp = visit_GeneratorExp = visit_ListComp
125 def visit_DictComp(self, node):
126 if self.in_function:
127 for comp in node.generators:
128 self.visit(comp.target)
129 self.visit(comp.iter)
130 else:
131 self.generic_visit(node)
133 def _expand_tuples(self, args):
134 for arg in args:
135 if isinstance(arg, _ast.Tuple):
136 yield from arg.elts
137 else:
138 yield arg
140 def _visit_function(self, node, islambda):
141 # push function state onto stack. dont log any more
142 # identifiers as "declared" until outside of the function,
143 # but keep logging identifiers as "undeclared". track
144 # argument names in each function header so they arent
145 # counted as "undeclared"
147 inf = self.in_function
148 self.in_function = True
150 local_ident_stack = self.local_ident_stack
151 self.local_ident_stack = local_ident_stack.union(
152 [arg_id(arg) for arg in self._expand_tuples(node.args.args)]
153 )
154 if islambda:
155 self.visit(node.body)
156 else:
157 for n in node.body:
158 self.visit(n)
159 self.in_function = inf
160 self.local_ident_stack = local_ident_stack
162 def visit_For(self, node):
163 # flip around visit
165 self.visit(node.iter)
166 self.visit(node.target)
167 for statement in node.body:
168 self.visit(statement)
169 for statement in node.orelse:
170 self.visit(statement)
172 def visit_Name(self, node):
173 if isinstance(node.ctx, _ast.Store):
174 # this is eqiuvalent to visit_AssName in
175 # compiler
176 self._add_declared(node.id)
177 elif (
178 node.id not in reserved
179 and node.id not in self.listener.declared_identifiers
180 and node.id not in self.local_ident_stack
181 ):
182 self.listener.undeclared_identifiers.add(node.id)
184 def visit_Import(self, node):
185 for name in node.names:
186 if name.asname is not None:
187 self._add_declared(name.asname)
188 else:
189 self._add_declared(name.name.split(".")[0])
191 def visit_ImportFrom(self, node):
192 for name in node.names:
193 if name.asname is not None:
194 self._add_declared(name.asname)
195 elif name.name == "*":
196 raise exceptions.CompileException(
197 "'import *' is not supported, since all identifier "
198 "names must be explicitly declared. Please use the "
199 "form 'from <modulename> import <name1>, <name2>, "
200 "...' instead.",
201 **self.exception_kwargs,
202 )
203 else:
204 self._add_declared(name.name)
207class FindTuple(_ast_util.NodeVisitor):
208 def __init__(self, listener, code_factory, **exception_kwargs):
209 self.listener = listener
210 self.exception_kwargs = exception_kwargs
211 self.code_factory = code_factory
213 def visit_Tuple(self, node):
214 for n in node.elts:
215 p = self.code_factory(n, **self.exception_kwargs)
216 self.listener.codeargs.append(p)
217 self.listener.args.append(ExpressionGenerator(n).value())
218 ldi = self.listener.declared_identifiers
219 self.listener.declared_identifiers = ldi.union(
220 p.declared_identifiers
221 )
222 lui = self.listener.undeclared_identifiers
223 self.listener.undeclared_identifiers = lui.union(
224 p.undeclared_identifiers
225 )
228class ParseFunc(_ast_util.NodeVisitor):
229 def __init__(self, listener, **exception_kwargs):
230 self.listener = listener
231 self.exception_kwargs = exception_kwargs
233 def visit_FunctionDef(self, node):
234 self.listener.funcname = node.name
236 argnames = [arg_id(arg) for arg in node.args.args]
237 if node.args.vararg:
238 argnames.append(node.args.vararg.arg)
240 kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
241 if node.args.kwarg:
242 kwargnames.append(node.args.kwarg.arg)
243 self.listener.argnames = argnames
244 self.listener.defaults = node.args.defaults # ast
245 self.listener.kwargnames = kwargnames
246 self.listener.kwdefaults = node.args.kw_defaults
247 self.listener.varargs = node.args.vararg
248 self.listener.kwargs = node.args.kwarg
251class ExpressionGenerator:
252 def __init__(self, astnode):
253 self.generator = _ast_util.SourceGenerator(" " * 4)
254 self.generator.visit(astnode)
256 def value(self):
257 return "".join(self.generator.result)