Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mako/pyparser.py: 76%
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-2025 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 _ast
14import operator
16from mako import _ast_util
17from mako import compat
18from mako import exceptions
19from mako import util
21# words that cannot be assigned to (notably
22# smaller than the total keys in __builtins__)
23reserved = {"True", "False", "None", "print"}
25# the "id" attribute on a function node
26arg_id = operator.attrgetter("arg")
28util.restore__ast(_ast)
31def parse(code, mode="exec", **exception_kwargs):
32 """Parse an expression into AST"""
34 try:
35 return _ast_util.parse(code, "<unknown>", mode)
36 except Exception as e:
37 raise exceptions.SyntaxException(
38 "(%s) %s (%r)"
39 % (
40 compat.exception_as().__class__.__name__,
41 compat.exception_as(),
42 code[0:50],
43 ),
44 **exception_kwargs,
45 ) from e
48class FindIdentifiers(_ast_util.NodeVisitor):
49 def __init__(self, listener, **exception_kwargs):
50 self.in_function = False
51 self.in_assign_targets = False
52 self.local_ident_stack = set()
53 self.listener = listener
54 self.exception_kwargs = exception_kwargs
56 def _add_declared(self, name):
57 if not self.in_function:
58 self.listener.declared_identifiers.add(name)
59 else:
60 self.local_ident_stack.add(name)
62 def visit_ClassDef(self, node):
63 self._add_declared(node.name)
65 def visit_Assign(self, node):
66 # flip around the visiting of Assign so the expression gets
67 # evaluated first, in the case of a clause like "x=x+5" (x
68 # is undeclared)
70 self.visit(node.value)
71 in_a = self.in_assign_targets
72 self.in_assign_targets = True
73 for n in node.targets:
74 self.visit(n)
75 self.in_assign_targets = in_a
77 def visit_ExceptHandler(self, node):
78 if node.name is not None:
79 self._add_declared(node.name)
80 if node.type is not None:
81 self.visit(node.type)
82 for statement in node.body:
83 self.visit(statement)
85 def visit_Lambda(self, node, *args):
86 self._visit_function(node, True)
88 def visit_FunctionDef(self, node):
89 self._add_declared(node.name)
90 self._visit_function(node, False)
92 def visit_ListComp(self, node):
93 if self.in_function:
94 for comp in node.generators:
95 self.visit(comp.target)
96 self.visit(comp.iter)
97 else:
98 self.generic_visit(node)
100 visit_SetComp = visit_GeneratorExp = visit_ListComp
102 def visit_DictComp(self, node):
103 if self.in_function:
104 for comp in node.generators:
105 self.visit(comp.target)
106 self.visit(comp.iter)
107 else:
108 self.generic_visit(node)
110 def _expand_tuples(self, args):
111 for arg in args:
112 if isinstance(arg, _ast.Tuple):
113 yield from arg.elts
114 else:
115 yield arg
117 def _visit_function(self, node, islambda):
118 # push function state onto stack. dont log any more
119 # identifiers as "declared" until outside of the function,
120 # but keep logging identifiers as "undeclared". track
121 # argument names in each function header so they arent
122 # counted as "undeclared"
124 inf = self.in_function
125 self.in_function = True
127 local_ident_stack = self.local_ident_stack
128 self.local_ident_stack = local_ident_stack.union(
129 [arg_id(arg) for arg in self._expand_tuples(node.args.args)]
130 )
131 if islambda:
132 self.visit(node.body)
133 else:
134 for n in node.body:
135 self.visit(n)
136 self.in_function = inf
137 self.local_ident_stack = local_ident_stack
139 def visit_For(self, node):
140 # flip around visit
142 self.visit(node.iter)
143 self.visit(node.target)
144 for statement in node.body:
145 self.visit(statement)
146 for statement in node.orelse:
147 self.visit(statement)
149 def visit_Name(self, node):
150 if isinstance(node.ctx, _ast.Store):
151 # this is eqiuvalent to visit_AssName in
152 # compiler
153 self._add_declared(node.id)
154 elif (
155 node.id not in reserved
156 and node.id not in self.listener.declared_identifiers
157 and node.id not in self.local_ident_stack
158 ):
159 self.listener.undeclared_identifiers.add(node.id)
161 def visit_Import(self, node):
162 for name in node.names:
163 if name.asname is not None:
164 self._add_declared(name.asname)
165 else:
166 self._add_declared(name.name.split(".")[0])
168 def visit_ImportFrom(self, node):
169 for name in node.names:
170 if name.asname is not None:
171 self._add_declared(name.asname)
172 elif name.name == "*":
173 raise exceptions.CompileException(
174 "'import *' is not supported, since all identifier "
175 "names must be explicitly declared. Please use the "
176 "form 'from <modulename> import <name1>, <name2>, "
177 "...' instead.",
178 **self.exception_kwargs,
179 )
180 else:
181 self._add_declared(name.name)
184class FindTuple(_ast_util.NodeVisitor):
185 def __init__(self, listener, code_factory, **exception_kwargs):
186 self.listener = listener
187 self.exception_kwargs = exception_kwargs
188 self.code_factory = code_factory
190 def visit_Tuple(self, node):
191 for n in node.elts:
192 p = self.code_factory(n, **self.exception_kwargs)
193 self.listener.codeargs.append(p)
194 self.listener.args.append(ExpressionGenerator(n).value())
195 ldi = self.listener.declared_identifiers
196 self.listener.declared_identifiers = ldi.union(
197 p.declared_identifiers
198 )
199 lui = self.listener.undeclared_identifiers
200 self.listener.undeclared_identifiers = lui.union(
201 p.undeclared_identifiers
202 )
205class ParseFunc(_ast_util.NodeVisitor):
206 def __init__(self, listener, **exception_kwargs):
207 self.listener = listener
208 self.exception_kwargs = exception_kwargs
210 def visit_FunctionDef(self, node):
211 self.listener.funcname = node.name
213 argnames = [arg_id(arg) for arg in node.args.args]
214 if node.args.vararg:
215 argnames.append(node.args.vararg.arg)
217 kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
218 if node.args.kwarg:
219 kwargnames.append(node.args.kwarg.arg)
220 self.listener.argnames = argnames
221 self.listener.defaults = node.args.defaults # ast
222 self.listener.kwargnames = kwargnames
223 self.listener.kwdefaults = node.args.kw_defaults
224 self.listener.varargs = node.args.vararg
225 self.listener.kwargs = node.args.kwarg
228class ExpressionGenerator:
229 def __init__(self, astnode):
230 self.generator = _ast_util.SourceGenerator(" " * 4)
231 self.generator.visit(astnode)
233 def value(self):
234 return "".join(self.generator.result)