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

139 statements  

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 

6 

7"""Handles parsing of Python code. 

8 

9Parsing to AST is done via _ast on Python > 2.5, otherwise the compiler 

10module is used. 

11""" 

12 

13import _ast 

14import operator 

15 

16from mako import _ast_util 

17from mako import compat 

18from mako import exceptions 

19from mako import util 

20 

21# words that cannot be assigned to (notably 

22# smaller than the total keys in __builtins__) 

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

24 

25# the "id" attribute on a function node 

26arg_id = operator.attrgetter("arg") 

27 

28util.restore__ast(_ast) 

29 

30 

31def parse(code, mode="exec", **exception_kwargs): 

32 """Parse an expression into AST""" 

33 

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 

46 

47 

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 

55 

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) 

61 

62 def visit_ClassDef(self, node): 

63 self._add_declared(node.name) 

64 

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) 

69 

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 

76 

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) 

84 

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

86 self._visit_function(node, True) 

87 

88 def visit_FunctionDef(self, node): 

89 self._add_declared(node.name) 

90 self._visit_function(node, False) 

91 

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) 

99 

100 visit_SetComp = visit_GeneratorExp = visit_ListComp 

101 

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) 

109 

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 

116 

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" 

123 

124 inf = self.in_function 

125 self.in_function = True 

126 

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 

138 

139 def visit_For(self, node): 

140 # flip around visit 

141 

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) 

148 

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) 

160 

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

167 

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) 

182 

183 

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 

189 

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 ) 

203 

204 

205class ParseFunc(_ast_util.NodeVisitor): 

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

207 self.listener = listener 

208 self.exception_kwargs = exception_kwargs 

209 

210 def visit_FunctionDef(self, node): 

211 self.listener.funcname = node.name 

212 

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

214 if node.args.vararg: 

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

216 

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 

226 

227 

228class ExpressionGenerator: 

229 def __init__(self, astnode): 

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

231 self.generator.visit(astnode) 

232 

233 def value(self): 

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