Ticket #113: ticket113.diff
| File ticket113.diff, 9.6 KB (added by cmlenz, 16 years ago) |
|---|
-
genshi/template/tests/eval.py
379 379 expr.evaluate({'something': Something()}) 380 380 self.fail('Expected UndefinedError') 381 381 except UndefinedError, e: 382 self.assertEqual('<Something> has no member named "nil"', str(e)) 382 383 exc_type, exc_value, exc_traceback = sys.exc_info() 384 search_string = "<Expression 'something.nil'>" 383 385 frame = exc_traceback.tb_next 384 frames = []385 386 while frame.tb_next: 386 387 frame = frame.tb_next 387 frames.append(frame)388 self.assertEqual('<Something> has no member named "nil"', str(e))389 self.assertEqual("<Expression 'something.nil'>",390 frames[-3].tb_frame.f_code.co_name)391 self.assertEqual('index.html',392 frames[-3].tb_frame.f_code.co_filename)393 self.assertEqual(50, frame s[-3].tb_lineno)388 code = frame.tb_frame.f_code 389 if code.co_name == search_string: 390 break 391 else: 392 self.fail("never found the frame I was looking for") 393 self.assertEqual('index.html', code.co_filename) 394 self.assertEqual(50, frame.tb_lineno) 394 395 395 396 def test_error_getitem_undefined_string(self): 396 397 class Something(object): … … 402 403 expr.evaluate({'something': Something()}) 403 404 self.fail('Expected UndefinedError') 404 405 except UndefinedError, e: 406 self.assertEqual('<Something> has no member named "nil"', str(e)) 405 407 exc_type, exc_value, exc_traceback = sys.exc_info() 408 search_string = '''<Expression 'something["nil"]'>''' 406 409 frame = exc_traceback.tb_next 407 frames = []408 410 while frame.tb_next: 409 411 frame = frame.tb_next 410 frames.append(frame)411 self.assertEqual('<Something> has no member named "nil"', str(e))412 self.assertEqual('''<Expression 'something["nil"]'>''',413 frames[-3].tb_frame.f_code.co_name)414 self.assertEqual('index.html',415 frames[-3].tb_frame.f_code.co_filename)416 self.assertEqual(50, frame s[-3].tb_lineno)412 code = frame.tb_frame.f_code 413 if code.co_name == search_string: 414 break 415 else: 416 self.fail("never found the frame I was looking for") 417 self.assertEqual('index.html', code.co_filename) 418 self.assertEqual(50, frame.tb_lineno) 417 419 418 420 419 421 class SuiteTestCase(unittest.TestCase): … … 504 506 suite.execute(data) 505 507 self.assertEqual(4, data['x']) 506 508 509 def test_augmented_attribute_assignment(self): 510 suite = Suite("d['k'] += 42") 511 d = {"k": 1} 512 suite.execute({"d": d}) 513 self.assertEqual(43, d["k"]) 507 514 515 def test_local_augmented_assign(self): 516 Suite("x = 1; x += 42; assert x == 43").execute({}) 517 518 def test_assign_in_list(self): 519 suite = Suite("[d['k']] = 'foo',; assert d['k'] == 'foo'") 520 d = {"k": "bar"} 521 suite.execute({"d": d}) 522 self.assertEqual("foo", d["k"]) 523 524 def test_exec(self): 525 suite = Suite("x = 1; exec d['k']; assert x == 42, x") 526 suite.execute({"d": {"k": "x = 42"}}) 527 528 def test_return(self): 529 suite = Suite(""" 530 def f(): 531 return v 532 533 assert f() == 42 534 """) 535 suite.execute({"v": 42}) 536 537 def test_assign_to_dict_item(self): 538 suite = Suite("d['k'] = 'foo'") 539 data = {'d': {}} 540 suite.execute(data) 541 self.assertEqual('foo', data['d']['k']) 542 543 def test_assign_to_attribute(self): 544 class Something(object): pass 545 something = Something() 546 suite = Suite("obj.attr = 'foo'") 547 data = {"obj": something} 548 suite.execute(data) 549 self.assertEqual('foo', something.attr) 550 551 def test_delattr(self): 552 class Something(object): 553 def __init__(self): 554 self.attr = 'foo' 555 obj = Something() 556 Suite("del obj.attr").execute({'obj': obj}) 557 self.failIf(hasattr(obj, 'attr')) 558 559 def test_delitem(self): 560 d = {'k': 'foo'} 561 Suite("del d['k']").execute({'d': d}) 562 self.failIf('k' in d, `d`) 563 564 508 565 def suite(): 509 566 suite = unittest.TestSuite() 510 567 suite.addTest(doctest.DocTestSuite(Expression.__module__)) -
genshi/template/eval.py
140 140 """Executes Python statements used in templates. 141 141 142 142 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'}) 143 >>> Suite( 'foo = dict.some').execute(data)143 >>> Suite("foo = dict['some']").execute(data) 144 144 >>> data['foo'] 145 145 'thing' 146 146 """ … … 361 361 return parse(source, mode) 362 362 363 363 def _compile(node, source=None, mode='eval', filename=None, lineno=-1): 364 tree = TemplateASTTransformer().visit(node) 364 xform = {'eval': ExpressionASTTransformer}.get(mode, TemplateASTTransformer) 365 tree = xform().visit(node) 365 366 if isinstance(filename, unicode): 366 367 # unicode file names not allowed for code objects 367 368 filename = filename.encode('utf-8', 'replace') … … 397 398 Every visitor method can be overridden to return an AST node that has been 398 399 altered or replaced in some way. 399 400 """ 400 _visitors = {}401 401 402 402 def visit(self, node): 403 403 if node is None: 404 404 return None 405 v = self._visitors.get(node.__class__) 406 if not v: 407 v = getattr(self.__class__, 'visit%s' % node.__class__.__name__, 408 self.__class__._visitDefault) 409 self._visitors[node.__class__] = v 410 return v(self, node) 405 visitor = getattr(self, 'visit%s' % node.__class__.__name__, 406 self._visitDefault) 407 return visitor(node) 411 408 412 409 def _visitDefault(self, node): 413 410 return node … … 475 472 node.expr = self.visit(node.expr) 476 473 return node 477 474 475 def visitAssAttr(self, node): 476 node.expr = self.visit(node.expr) 477 return node 478 479 def visitAugAssign(self, node): 480 node.node = self.visit(node.node) 481 node.expr = self.visit(node.expr) 482 return node 483 478 484 def visitDecorators(self, node): 479 485 node.nodes = [self.visit(x) for x in node.nodes] 480 486 return node 481 487 488 def visitExec(self, node): 489 node.expr = self.visit(node.expr) 490 node.locals = self.visit(node.locals) 491 node.globals = self.visit(node.globals) 492 return node 493 482 494 def visitFor(self, node): 483 495 node.assign = self.visit(node.assign) 484 496 node.list = self.visit(node.list) … … 503 515 node.expr3 = self.visit(node.expr3) 504 516 return node 505 517 518 def visitReturn(self, node): 519 node.value = self.visit(node.value) 520 return node 521 506 522 def visitTryExcept(self, node): 507 523 node.body = self.visit(node.body) 508 524 node.handlers = self.visit(node.handlers) … … 536 552 node.nodes = [self.visit(x) for x in node.nodes] 537 553 return node 538 554 visitAnd = visitOr = visitBitand = visitBitor = visitBitxor = _visitBoolOp 539 visitAssTuple = _visitBoolOp555 visitAssTuple = visitAssList = _visitBoolOp 540 556 541 557 def _visitBinOp(self, node): 542 558 node.left = self.visit(node.left) … … 651 667 self.locals[-1].add(node.name) 652 668 return node 653 669 670 def visitAugAssign(self, node): 671 if isinstance(node.node, ast.Name): 672 name = node.node.name 673 node.node = ast.Subscript(ast.Name('data'), 'OP_APPLY', 674 [ast.Const(name)]) 675 node.expr = self.visit(node.expr) 676 return ast.If([ 677 (ast.Compare(ast.Const(name), [('in', ast.Name('data'))]), 678 ast.Stmt([node]))], 679 ast.Stmt([ast.Raise(ast.CallFunc(ast.Name('UndefinedError'), 680 [ast.Const(name)]), 681 None, None)])) 682 else: 683 return ASTTransformer.visitAugAssign(self, node) 684 654 685 def visitClass(self, node): 655 686 self.locals.append(set()) 656 687 node = ASTTransformer.visitClass(self, node) … … 675 706 self.locals.pop() 676 707 return node 677 708 678 def visitGetattr(self, node):679 return ast.CallFunc(ast.Name('_lookup_attr'), [680 ast.Name('data'), self.visit(node.expr),681 ast.Const(node.attrname)682 ])683 684 709 def visitLambda(self, node): 685 710 self.locals.append(set(flatten(node.argnames))) 686 711 node = ASTTransformer.visitLambda(self, node) … … 703 728 func_args = [ast.Name('data'), ast.Const(node.name)] 704 729 return ast.CallFunc(ast.Name('_lookup_name'), func_args) 705 730 731 732 class ExpressionASTTransformer(TemplateASTTransformer): 733 """Concrete AST transformer that implements the AST transformations needed 734 for code embedded in templates. 735 """ 736 737 def visitGetattr(self, node): 738 return ast.CallFunc(ast.Name('_lookup_attr'), [ 739 ast.Name('data'), self.visit(node.expr), 740 ast.Const(node.attrname) 741 ]) 742 706 743 def visitSubscript(self, node): 707 744 return ast.CallFunc(ast.Name('_lookup_item'), [ 708 745 ast.Name('data'), self.visit(node.expr),
