Edgewall Software

Ticket #113: ticket113.diff

File ticket113.diff, 9.6 KB (added by cmlenz, 16 years ago)

Alternative patch

  • genshi/template/tests/eval.py

     
    379379            expr.evaluate({'something': Something()})
    380380            self.fail('Expected UndefinedError')
    381381        except UndefinedError, e:
     382            self.assertEqual('<Something> has no member named "nil"', str(e))
    382383            exc_type, exc_value, exc_traceback = sys.exc_info()
     384            search_string = "<Expression 'something.nil'>"
    383385            frame = exc_traceback.tb_next
    384             frames = []
    385386            while frame.tb_next:
    386387                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, frames[-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)
    394395
    395396    def test_error_getitem_undefined_string(self):
    396397        class Something(object):
     
    402403            expr.evaluate({'something': Something()})
    403404            self.fail('Expected UndefinedError')
    404405        except UndefinedError, e:
     406            self.assertEqual('<Something> has no member named "nil"', str(e))
    405407            exc_type, exc_value, exc_traceback = sys.exc_info()
     408            search_string = '''<Expression 'something["nil"]'>'''
    406409            frame = exc_traceback.tb_next
    407             frames = []
    408410            while frame.tb_next:
    409411                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, frames[-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)
    417419
    418420
    419421class SuiteTestCase(unittest.TestCase):
     
    504506        suite.execute(data)
    505507        self.assertEqual(4, data['x'])
    506508
     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"])
    507514
     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("""
     530def f():
     531    return v
     532
     533assert 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
    508565def suite():
    509566    suite = unittest.TestSuite()
    510567    suite.addTest(doctest.DocTestSuite(Expression.__module__))
  • genshi/template/eval.py

     
    140140    """Executes Python statements used in templates.
    141141
    142142    >>> 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)
    144144    >>> data['foo']
    145145    'thing'
    146146    """
     
    361361    return parse(source, mode)
    362362
    363363def _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)
    365366    if isinstance(filename, unicode):
    366367        # unicode file names not allowed for code objects
    367368        filename = filename.encode('utf-8', 'replace')
     
    397398    Every visitor method can be overridden to return an AST node that has been
    398399    altered or replaced in some way.
    399400    """
    400     _visitors = {}
    401401
    402402    def visit(self, node):
    403403        if node is None:
    404404            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)
    411408
    412409    def _visitDefault(self, node):
    413410        return node
     
    475472        node.expr = self.visit(node.expr)
    476473        return node
    477474
     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
    478484    def visitDecorators(self, node):
    479485        node.nodes = [self.visit(x) for x in node.nodes]
    480486        return node
    481487
     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
    482494    def visitFor(self, node):
    483495        node.assign = self.visit(node.assign)
    484496        node.list = self.visit(node.list)
     
    503515        node.expr3 = self.visit(node.expr3)
    504516        return node
    505517
     518    def visitReturn(self, node):
     519        node.value = self.visit(node.value)
     520        return node
     521
    506522    def visitTryExcept(self, node):
    507523        node.body = self.visit(node.body)
    508524        node.handlers = self.visit(node.handlers)
     
    536552        node.nodes = [self.visit(x) for x in node.nodes]
    537553        return node
    538554    visitAnd = visitOr = visitBitand = visitBitor = visitBitxor = _visitBoolOp
    539     visitAssTuple = _visitBoolOp
     555    visitAssTuple = visitAssList = _visitBoolOp
    540556
    541557    def _visitBinOp(self, node):
    542558        node.left = self.visit(node.left)
     
    651667            self.locals[-1].add(node.name)
    652668        return node
    653669
     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
    654685    def visitClass(self, node):
    655686        self.locals.append(set())
    656687        node = ASTTransformer.visitClass(self, node)
     
    675706        self.locals.pop()
    676707        return node
    677708
    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 
    684709    def visitLambda(self, node):
    685710        self.locals.append(set(flatten(node.argnames)))
    686711        node = ASTTransformer.visitLambda(self, node)
     
    703728        func_args = [ast.Name('data'), ast.Const(node.name)]
    704729        return ast.CallFunc(ast.Name('_lookup_name'), func_args)
    705730
     731
     732class 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
    706743    def visitSubscript(self, node):
    707744        return ast.CallFunc(ast.Name('_lookup_item'), [
    708745            ast.Name('data'), self.visit(node.expr),