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

144 statements  

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 

6 

7"""Handles parsing of Python code.""" 

8 

9import _ast 

10import operator 

11 

12from mako import _ast_util 

13from mako import compat 

14from mako import exceptions 

15 

16# words that cannot be assigned to (notably 

17# smaller than the total keys in __builtins__) 

18reserved = {"True", "False", "None", "print"} 

19 

20# the "id" attribute on a function node 

21arg_id = operator.attrgetter("arg") 

22 

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>" 

27 

28 

29def parse(code, mode="exec", lineno_offset=0, **exception_kwargs): 

30 """Parse an expression into AST. 

31 

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. 

36 

37 """ 

38 

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 

51 

52 

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. 

56 

57 """ 

58 

59 lineno = exception_kwargs.get("lineno") 

60 exc_lineno = getattr(exc, "lineno", None) 

61 

62 if lineno is None or exc_lineno is None: 

63 return exception_kwargs 

64 

65 return { 

66 **exception_kwargs, 

67 "lineno": lineno + lineno_offset + exc_lineno - 1, 

68 } 

69 

70 

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 

78 

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) 

84 

85 def visit_ClassDef(self, node): 

86 self._add_declared(node.name) 

87 

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) 

92 

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 

99 

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) 

107 

108 def visit_Lambda(self, node, *args): 

109 self._visit_function(node, True) 

110 

111 def visit_FunctionDef(self, node): 

112 self._add_declared(node.name) 

113 self._visit_function(node, False) 

114 

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) 

122 

123 visit_SetComp = visit_GeneratorExp = visit_ListComp 

124 

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) 

132 

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 

139 

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" 

146 

147 inf = self.in_function 

148 self.in_function = True 

149 

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 

161 

162 def visit_For(self, node): 

163 # flip around visit 

164 

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) 

171 

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) 

183 

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]) 

190 

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) 

205 

206 

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 

212 

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 ) 

226 

227 

228class ParseFunc(_ast_util.NodeVisitor): 

229 def __init__(self, listener, **exception_kwargs): 

230 self.listener = listener 

231 self.exception_kwargs = exception_kwargs 

232 

233 def visit_FunctionDef(self, node): 

234 self.listener.funcname = node.name 

235 

236 argnames = [arg_id(arg) for arg in node.args.args] 

237 if node.args.vararg: 

238 argnames.append(node.args.vararg.arg) 

239 

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 

249 

250 

251class ExpressionGenerator: 

252 def __init__(self, astnode): 

253 self.generator = _ast_util.SourceGenerator(" " * 4) 

254 self.generator.visit(astnode) 

255 

256 def value(self): 

257 return "".join(self.generator.result)