comparison markup/eval.py @ 131:2ad83f1d337c trunk

* Support for line numbers in exceptions in expression evaluation (#22). * Fix bug in expression evaluation when item access was used inside a lambda or list comprehension. Thanks to Kevin Dangoor for reporting the problem.
author cmlenz
date Fri, 04 Aug 2006 13:07:52 +0000
parents c9f0a26e28a2
children dc42cb3c02dc
comparison
equal deleted inserted replaced
130:6edc71acb642 131:2ad83f1d337c
14 """Support for "safe" evaluation of Python expressions.""" 14 """Support for "safe" evaluation of Python expressions."""
15 15
16 import __builtin__ 16 import __builtin__
17 from compiler import ast, parse 17 from compiler import ast, parse
18 from compiler.pycodegen import ExpressionCodeGenerator 18 from compiler.pycodegen import ExpressionCodeGenerator
19 import new
19 20
20 from markup.core import Stream 21 from markup.core import Stream
21 22
22 __all__ = ['Expression'] 23 __all__ = ['Expression']
23 24
97 tree = parse(source, 'eval') 98 tree = parse(source, 'eval')
98 xform = ExpressionASTTransformer() 99 xform = ExpressionASTTransformer()
99 tree = xform.visit(tree) 100 tree = xform.visit(tree)
100 101
101 if isinstance(filename, unicode): 102 if isinstance(filename, unicode):
102 # pycodegen doesn't like unicode in the filename 103 # unicode file names not allowed for code objects
103 filename = filename.encode('utf-8', 'replace') 104 filename = filename.encode('utf-8', 'replace')
104 tree.filename = filename or '<string>' 105 elif not filename:
106 filename = '<string>'
107 tree.filename = '<string>'
108 if lineno <= 0:
109 lineno = 1
105 110
106 gen = ExpressionCodeGenerator(tree) 111 gen = ExpressionCodeGenerator(tree)
107 if lineno >= 0: 112 gen.optimized = True
108 gen.emit('SET_LINENO', lineno) 113 code = gen.getCode()
109 114
110 return gen.getCode() 115 # We'd like to just set co_firstlineno, but it's readonly. So we need to
116 # clone the code object while adjusting the line number
117 return new.code(0, code.co_nlocals, code.co_stacksize,
118 code.co_flags | 0x0040, code.co_code, code.co_consts,
119 code.co_names, code.co_varnames, filename, code.co_name,
120 lineno, code.co_lnotab, (), ())
111 121
112 def _lookup_name(data, name, locals=None): 122 def _lookup_name(data, name, locals=None):
113 val = data.get(name) 123 val = data.get(name)
114 if val is None and locals: 124 if val is None and locals:
115 val = locals.get(name) 125 val = locals.get(name)
255 class ExpressionASTTransformer(ASTTransformer): 265 class ExpressionASTTransformer(ASTTransformer):
256 """Concrete AST transformer that implements the AST transformations needed 266 """Concrete AST transformer that implements the AST transformations needed
257 for template expressions. 267 for template expressions.
258 """ 268 """
259 269
260 def visitGetattr(self, node, *args, **kwargs): 270 def visitGetattr(self, node, locals_=False):
261 return ast.CallFunc(ast.Name('_lookup_attr'), 271 return ast.CallFunc(ast.Name('_lookup_attr'), [
262 [ast.Name('data'), self.visit(node.expr, *args, **kwargs), 272 ast.Name('data'), self.visit(node.expr, locals_=locals_),
263 ast.Const(node.attrname)] 273 ast.Const(node.attrname)
264 ) 274 ])
265 275
266 def visitLambda(self, node, *args, **kwargs): 276 def visitLambda(self, node, locals_=False):
267 old_lookup_locals = kwargs.get('lookup_locals', False) 277 node.code = self.visit(node.code, locals_=True)
268 kwargs['lookup_locals'] = True
269 node.code = self.visit(node.code, *args, **kwargs)
270 node.filename = '<string>' # workaround for bug in pycodegen 278 node.filename = '<string>' # workaround for bug in pycodegen
271 kwargs['lookup_locals'] = old_lookup_locals 279 return node
272 return node 280
273 281 def visitListComp(self, node, locals_=False):
274 def visitListComp(self, node, *args, **kwargs): 282 node.expr = self.visit(node.expr, locals_=True)
275 old_lookup_locals = kwargs.get('lookup_locals', False) 283 node.quals = map(lambda x: self.visit(x, locals_=True), node.quals)
276 kwargs['lookup_locals'] = True 284 return node
277 node.expr = self.visit(node.expr, *args, **kwargs) 285
278 node.quals = map(lambda x: self.visit(x, *args, **kwargs), node.quals) 286 def visitName(self, node, locals_=False):
279 kwargs['lookup_locals'] = old_lookup_locals
280 return node
281
282 def visitName(self, node, *args, **kwargs):
283 func_args = [ast.Name('data'), ast.Const(node.name)] 287 func_args = [ast.Name('data'), ast.Const(node.name)]
284 if kwargs.get('lookup_locals'): 288 if locals_:
285 func_args.append(ast.CallFunc(ast.Name('locals'), [])) 289 func_args.append(ast.CallFunc(ast.Name('locals'), []))
286 return ast.CallFunc(ast.Name('_lookup_name'), func_args) 290 return ast.CallFunc(ast.Name('_lookup_name'), func_args)
287 return node 291
288 292 def visitSubscript(self, node, locals_=False):
289 def visitSubscript(self, node, *args, **kwargs): 293 return ast.CallFunc(ast.Name('_lookup_item'), [
290 return ast.CallFunc(ast.Name('_lookup_item'), 294 ast.Name('data'), self.visit(node.expr, locals_=locals_),
291 [ast.Name('data'), self.visit(node.expr, *args, **kwargs), 295 ast.Tuple(map(lambda x: self.visit(x, locals_=locals_), node.subs))
292 ast.Tuple(map(self.visit, node.subs, *args, **kwargs))] 296 ])
293 )
Copyright (C) 2012-2017 Edgewall Software