Edgewall Software

Ticket #113: ticket113.2.diff

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

Updated 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):
     
    431433        assert 'donothing' in data
    432434        self.assertEqual(None, data['donothing']())
    433435
     436    def test_def_with_multiple_statements(self):
     437        suite = Suite("""def donothing():
     438    if True:
     439        return foo
     440""")
     441        data = {'foo': 'bar'}
     442        suite.execute(data)
     443        assert 'donothing' in data
     444        self.assertEqual('bar', data['donothing']())
     445
    434446    def test_delete(self):
    435447        suite = Suite("""foo = 42
    436448del foo
     
    504516        suite.execute(data)
    505517        self.assertEqual(4, data['x'])
    506518
     519    def test_augmented_attribute_assignment(self):
     520        suite = Suite("d['k'] += 42")
     521        d = {"k": 1}
     522        suite.execute({"d": d})
     523        self.assertEqual(43, d["k"])
    507524
     525    def test_local_augmented_assign(self):
     526        Suite("x = 1; x += 42; assert x == 43").execute({})
     527
     528    def test_assign_in_list(self):
     529        suite = Suite("[d['k']] = 'foo',; assert d['k'] == 'foo'")
     530        d = {"k": "bar"}
     531        suite.execute({"d": d})
     532        self.assertEqual("foo", d["k"])
     533
     534    def test_exec(self):
     535        suite = Suite("x = 1; exec d['k']; assert x == 42, x")
     536        suite.execute({"d": {"k": "x = 42"}})
     537
     538    def test_return(self):
     539        suite = Suite("""
     540def f():
     541    return v
     542
     543assert f() == 42
     544""")
     545        suite.execute({"v": 42})
     546
     547    def test_assign_to_dict_item(self):
     548        suite = Suite("d['k'] = 'foo'")
     549        data = {'d': {}}
     550        suite.execute(data)
     551        self.assertEqual('foo', data['d']['k'])
     552
     553    def test_assign_to_attribute(self):
     554        class Something(object): pass
     555        something = Something()
     556        suite = Suite("obj.attr = 'foo'")
     557        data = {"obj": something}
     558        suite.execute(data)
     559        self.assertEqual('foo', something.attr)
     560
     561    def test_delattr(self):
     562        class Something(object):
     563            def __init__(self):
     564                self.attr = 'foo'
     565        obj = Something()
     566        Suite("del obj.attr").execute({'obj': obj})
     567        self.failIf(hasattr(obj, 'attr'))
     568
     569    def test_delitem(self):
     570        d = {'k': 'foo'}
     571        Suite("del d['k']").execute({'d': d})
     572        self.failIf('k' in d, `d`)
     573
     574
    508575def suite():
    509576    suite = unittest.TestSuite()
    510577    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        if type(node) is tuple:
     406            return tuple([self.visit(n) for n in node])
     407        visitor = getattr(self, 'visit%s' % node.__class__.__name__,
     408                          self._visitDefault)
     409        return visitor(node)
    411410
    412411    def _visitDefault(self, node):
    413412        return node
     
    475474        node.expr = self.visit(node.expr)
    476475        return node
    477476
     477    def visitAssAttr(self, node):
     478        node.expr = self.visit(node.expr)
     479        return node
     480
     481    def visitAugAssign(self, node):
     482        node.node = self.visit(node.node)
     483        node.expr = self.visit(node.expr)
     484        return node
     485
    478486    def visitDecorators(self, node):
    479487        node.nodes = [self.visit(x) for x in node.nodes]
    480488        return node
    481489
     490    def visitExec(self, node):
     491        node.expr = self.visit(node.expr)
     492        node.locals = self.visit(node.locals)
     493        node.globals = self.visit(node.globals)
     494        return node
     495
    482496    def visitFor(self, node):
    483497        node.assign = self.visit(node.assign)
    484498        node.list = self.visit(node.list)
     
    503517        node.expr3 = self.visit(node.expr3)
    504518        return node
    505519
     520    def visitReturn(self, node):
     521        node.value = self.visit(node.value)
     522        return node
     523
    506524    def visitTryExcept(self, node):
    507525        node.body = self.visit(node.body)
    508526        node.handlers = self.visit(node.handlers)
     
    536554        node.nodes = [self.visit(x) for x in node.nodes]
    537555        return node
    538556    visitAnd = visitOr = visitBitand = visitBitor = visitBitxor = _visitBoolOp
    539     visitAssTuple = _visitBoolOp
     557    visitAssTuple = visitAssList = _visitBoolOp
    540558
    541559    def _visitBinOp(self, node):
    542560        node.left = self.visit(node.left)
     
    651669            self.locals[-1].add(node.name)
    652670        return node
    653671
     672    def visitAugAssign(self, node):
     673        if isinstance(node.node, ast.Name):
     674            name = node.node.name
     675            node.node = ast.Subscript(ast.Name('data'), 'OP_APPLY',
     676                                      [ast.Const(name)])
     677            node.expr = self.visit(node.expr)
     678            return ast.If([
     679                (ast.Compare(ast.Const(name), [('in', ast.Name('data'))]),
     680                 ast.Stmt([node]))],
     681                ast.Stmt([ast.Raise(ast.CallFunc(ast.Name('UndefinedError'),
     682                                                 [ast.Const(name)]),
     683                                    None, None)]))
     684        else:
     685            return ASTTransformer.visitAugAssign(self, node)
     686
    654687    def visitClass(self, node):
    655688        self.locals.append(set())
    656689        node = ASTTransformer.visitClass(self, node)
     
    675708        self.locals.pop()
    676709        return node
    677710
    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 
    684711    def visitLambda(self, node):
    685712        self.locals.append(set(flatten(node.argnames)))
    686713        node = ASTTransformer.visitLambda(self, node)
     
    703730        func_args = [ast.Name('data'), ast.Const(node.name)]
    704731        return ast.CallFunc(ast.Name('_lookup_name'), func_args)
    705732
     733
     734class ExpressionASTTransformer(TemplateASTTransformer):
     735    """Concrete AST transformer that implements the AST transformations needed
     736    for code embedded in templates.
     737    """
     738
     739    def visitGetattr(self, node):
     740        return ast.CallFunc(ast.Name('_lookup_attr'), [
     741            ast.Name('data'), self.visit(node.expr),
     742            ast.Const(node.attrname)
     743        ])
     744
    706745    def visitSubscript(self, node):
    707746        return ast.CallFunc(ast.Name('_lookup_item'), [
    708747            ast.Name('data'), self.visit(node.expr),