[Checkins] SVN: Sandbox/malthe/chameleon.core/ Made repeat-variable work with iterables which do not declare their length before execution.

Malthe Borch mborch at gmail.com
Tue Nov 25 16:35:38 EST 2008


Log message for revision 93351:
  Made repeat-variable work with iterables which do not declare their length before execution.

Changed:
  U   Sandbox/malthe/chameleon.core/CHANGES.txt
  U   Sandbox/malthe/chameleon.core/src/chameleon/core/clauses.py
  U   Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py

-=-
Modified: Sandbox/malthe/chameleon.core/CHANGES.txt
===================================================================
--- Sandbox/malthe/chameleon.core/CHANGES.txt	2008-11-25 21:29:48 UTC (rev 93350)
+++ Sandbox/malthe/chameleon.core/CHANGES.txt	2008-11-25 21:35:37 UTC (rev 93351)
@@ -4,6 +4,9 @@
 HEAD
 ~~~~
 
+- Made repeat-variable work with iterables which do not announce their
+  length before execution. [malthe]
+
 - Added namespace attribute support. [malthe]
 
 - Static attributes are now computed such that attributes are omitted

Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/clauses.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/clauses.py	2008-11-25 21:29:48 UTC (rev 93350)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/clauses.py	2008-11-25 21:35:37 UTC (rev 93351)
@@ -721,6 +721,25 @@
     >>> _repeat.end(stream)
     >>> exec stream.getvalue()
 
+    A repeat over an iterable which has no length, renders the generator.
+
+    >>> class iterator(object):
+    ...     def __iter__(self):
+    ...         yield 1; yield 2; yield 3
+    
+    >>> _out, _write, stream = testing.setup_stream()
+    >>> _repeat = Repeat(types.declaration(("i",)), testing.pyexp("iterator()"))
+    >>> _repeat.begin(stream)
+    >>> stream.write("r = repeat['i']")
+    >>> stream.write(
+    ...     "print (i, r.index, r.start, r.end, r.number(), r.odd(), r.even())")
+    >>> _repeat.end(stream)
+    >>> exec stream.getvalue()
+    (1, 0, True, False, 1, False, True)
+    (2, 1, False, False, 2, True, False)
+    (3, 2, False, True, 3, False, True)
+    >>> _repeat.end(stream)
+
     A repeat over a non-iterable raises an exception.
 
     >>> _out, _write, stream = testing.setup_stream()

Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py	2008-11-25 21:29:48 UTC (rev 93350)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/utils.py	2008-11-25 21:35:37 UTC (rev 93351)
@@ -187,12 +187,33 @@
 class repeatdict(dict):
     def insert(self, key, iterable):
         try:
-            length = len(iterable)
+            # this may seem convoluted, but in some cases, looking up
+            # the ``__len__`` attribute may raise an exception, in
+            # which case we'll pretend it isn't there; in this case, a
+            # safe way to get the length is to coerce the iterable
+            # into a tuple from which we can always get the length
+            __len__ = getattr(iterable, '__len__')
+        except AttributeError:
+            # some objects may provide a length but not possess a
+            # ``__len__`` in which case we'll try using the builtin
+            # method to obtain the length
+            try:
+                __len__ = length = len(iterable)
+            except TypeError:
+                # only if this results in a type-error, do we let is
+                # pass through; we do not want to mask any errors
+                # caused by actual code
+                __len__ = None
+        except (KeyboardInterrupt, SystemExit):
+            # these should never be caught; re-raise exception
+            raise
         except:
-            # we catch all exceptions here, because it's not required
-            # for iteration
-            length = None
-            
+            __len__ = None
+        else:
+            # it should be safe to obtain the length from the
+            # ``__len__`` method; remove safety gloves.
+            length = __len__()
+
         try:
             # We used to do iterable.__iter__() but, e.g. BTreeItems
             # objects are iterable (via __getitem__) but don't possess
@@ -203,8 +224,14 @@
             raise TypeError(
                 "Can only repeat over an iterable object (%s)." % iterable)
 
-        if length is not None:
-            self[key] = (iterator, length)
+        # if no length was obtained, coerce iterable to a tuple first
+        if __len__ is None:
+            generated = tuple(iterator)
+            iterator = iter(generated)
+            length = len(generated)            
+
+        # insert iterable into repeat structure
+        self[key] = (iterator, length)
             
         return iterator
         



More information about the Checkins mailing list