changeset 304:b2fdaff8c385 trunk

Return an `Undefined` instance from failed item or attribute lookups.
author cmlenz
date Mon, 16 Oct 2006 12:16:33 +0000
parents 53d502c7c874
children 60111a041e7c
files genshi/eval.py genshi/tests/eval.py
diffstat 2 files changed, 58 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/eval.py
+++ b/genshi/eval.py
@@ -65,6 +65,14 @@
     __slots__ = ['source', 'code']
 
     def __init__(self, source, filename=None, lineno=-1):
+        """Create the expression, either from a string, or from an AST node.
+        
+        @param source: either a string containing the source code of the
+            expression, or an AST node
+        @param filename: the (preferably absolute) name of the file containing
+            the expression
+        @param lineno: the number of the line on which the expression was found
+        """
         if isinstance(source, basestring):
             self.source = source
             if isinstance(source, unicode):
@@ -128,10 +136,10 @@
         ...
     NameError: Variable "foo" is not defined
     """
-    __slots__ = ['name']
+    __slots__ = ['_name']
 
     def __init__(self, name):
-        self.name = name
+        self._name = name
 
     def __call__(self, *args, **kwargs):
         self.throw()
@@ -149,7 +157,7 @@
         return 'undefined'
 
     def throw(self):
-        raise NameError('Variable "%s" is not defined' % self.name)
+        raise NameError('Variable "%s" is not defined' % self._name)
 
 
 def _compile(node, source=None, filename=None, lineno=-1):
@@ -202,7 +210,7 @@
     try:
         return obj[key]
     except (KeyError, TypeError):
-        return None
+        return Undefined(key)
 
 def _lookup_item(data, obj, key):
     if type(obj) is Undefined:
@@ -213,10 +221,11 @@
         return obj[key]
     except (KeyError, IndexError, TypeError), e:
         if isinstance(key, basestring):
-            try:
-                return getattr(obj, key)
-            except (AttributeError, TypeError), e:
-                pass
+            val = getattr(obj, key, Undefined)
+            if val is Undefined:
+                val = Undefined(key)
+            return val
+        raise
 
 
 class ASTTransformer(object):
--- a/genshi/tests/eval.py
+++ b/genshi/tests/eval.py
@@ -281,7 +281,8 @@
 
     def test_slice_with_vars(self):
         expr = Expression("numbers[start:end]")
-        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5), 'start': 0, 'end': 2}))
+        self.assertEqual([0, 1], expr.evaluate({'numbers': range(5), 'start': 0,
+                                                'end': 2}))
 
     def test_slice_copy(self):
         expr = Expression("numbers[:]")
@@ -289,7 +290,8 @@
 
     def test_slice_stride(self):
         expr = Expression("numbers[::stride]")
-        self.assertEqual([0, 2, 4], expr.evaluate({'numbers': range(5), 'stride': 2}))
+        self.assertEqual([0, 2, 4], expr.evaluate({'numbers': range(5),
+                                                   'stride': 2}))
 
     def test_slice_negative_start(self):
         expr = Expression("numbers[-1:]")
@@ -315,6 +317,7 @@
             while frame.tb_next:
                 frame = frame.tb_next
                 frames.append(frame)
+            self.assertEqual('Variable "nothing" is not defined', str(e))
             self.assertEqual('<Expression "nothing()">',
                              frames[-3].tb_frame.f_code.co_name)
             self.assertEqual('index.html',
@@ -333,12 +336,48 @@
             while frame.tb_next:
                 frame = frame.tb_next
                 frames.append(frame)
+            self.assertEqual('Variable "nothing" is not defined', str(e))
             self.assertEqual('<Expression "nothing.nil">',
                              frames[-3].tb_frame.f_code.co_name)
             self.assertEqual('index.html',
                              frames[-3].tb_frame.f_code.co_filename)
             self.assertEqual(50, frames[-3].tb_lineno)
 
+    def test_error_getitem_undefined(self):
+        expr = Expression("nothing[0]", filename='index.html', lineno=50)
+        try:
+            expr.evaluate({})
+            self.fail('Expected NameError')
+        except NameError, e:
+            exc_type, exc_value, exc_traceback = sys.exc_info()
+            frame = exc_traceback.tb_next
+            frames = []
+            while frame.tb_next:
+                frame = frame.tb_next
+                frames.append(frame)
+            self.assertEqual('Variable "nothing" is not defined', str(e))
+            self.assertEqual('<Expression "nothing[0]">',
+                             frames[-3].tb_frame.f_code.co_name)
+            self.assertEqual('index.html',
+                             frames[-3].tb_frame.f_code.co_filename)
+            self.assertEqual(50, frames[-3].tb_lineno)
+
+    def test_error_getattr_nested_undefined(self):
+        expr = Expression("nothing.nil", filename='index.html', lineno=50)
+        val = expr.evaluate({'nothing': object()})
+        assert isinstance(val, Undefined)
+        self.assertEqual("nil", val._name)
+
+    def test_error_getitem_nested_undefined_string(self):
+        expr = Expression("nothing['bla']", filename='index.html', lineno=50)
+        val = expr.evaluate({'nothing': object()})
+        assert isinstance(val, Undefined)
+        self.assertEqual("bla", val._name)
+
+    def test_error_getitem_nested_undefined_int(self):
+        expr = Expression("nothing[0]", filename='index.html', lineno=50)
+        self.assertRaises(TypeError, expr.evaluate, {'nothing': object()})
+
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software