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

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 

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 operator 

14 

15import _ast 

16 

17from mako import _ast_util 

18from mako import compat 

19from mako import exceptions 

20from mako import util 

21 

22# words that cannot be assigned to (notably 

23# smaller than the total keys in __builtins__) 

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

25 

26# the "id" attribute on a function node 

27arg_id = operator.attrgetter("arg") 

28 

29util.restore__ast(_ast) 

30 

31 

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

33 """Parse an expression into AST""" 

34 

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 

47 

48 

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 

56 

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) 

62 

63 def visit_ClassDef(self, node): 

64 self._add_declared(node.name) 

65 

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) 

70 

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 

77 

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) 

85 

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

87 self._visit_function(node, True) 

88 

89 def visit_FunctionDef(self, node): 

90 self._add_declared(node.name) 

91 self._visit_function(node, False) 

92 

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) 

101 

102 visit_SetComp = visit_GeneratorExp = visit_ListComp 

103 

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) 

112 

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 

119 

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" 

126 

127 inf = self.in_function 

128 self.in_function = True 

129 

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 

141 

142 def visit_For(self, node): 

143 # flip around visit 

144 

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) 

151 

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) 

163 

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

170 

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) 

185 

186 

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 

192 

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 ) 

206 

207 

208class ParseFunc(_ast_util.NodeVisitor): 

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

210 self.listener = listener 

211 self.exception_kwargs = exception_kwargs 

212 

213 def visit_FunctionDef(self, node): 

214 self.listener.funcname = node.name 

215 

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

217 if node.args.vararg: 

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

219 

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 

229 

230 

231class ExpressionGenerator: 

232 def __init__(self, astnode): 

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

234 self.generator.visit(astnode) 

235 

236 def value(self): 

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