Added "boolean_iter" function and TALES extension to check if an iterator returns at least one value, without consuming it
authorThierry Florac <tflorac@ulthar.net>
Wed, 22 May 2019 14:13:06 +0200
changeset 337 d4e617c7e37a
parent 336 5aa846ce2a2d
child 338 76a03dc88712
Added "boolean_iter" function and TALES extension to check if an iterator returns at least one value, without consuming it
src/pyams_utils/list.py
--- a/src/pyams_utils/list.py	Fri Mar 01 14:33:09 2019 +0100
+++ b/src/pyams_utils/list.py	Wed May 22 14:13:06 2019 +0200
@@ -12,14 +12,13 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
-from itertools import filterfalse
+from itertools import filterfalse, tee
 from random import random, shuffle
 
-# import interfaces
+from zope.interface import Interface
 
-# import packages
+from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config
+from pyams_utils.interfaces.tales import ITALESExtension
 
 
 def unique(seq, key=None):
@@ -123,3 +122,56 @@
                 selected[selected_index] = item
     shuffle(selected)
     return iter(selected)
+
+
+def boolean_iter(iterable):
+    """Check if an iterable returns at least one value, without consuming it.
+
+    The function returns a tuple containing a boolean flag indicating if the original iterator
+    is empty or not, and the original un-consumed iterator.
+
+    >>> from pyams_utils.list import boolean_iter
+    >>> def empty(input):
+    ...     yield from input
+    >>> mylist = empty(())
+    >>> check, myiter = boolean_iter(mylist)
+    >>> check
+    False
+    >>> list(myiter)
+    []
+    >>> mylist = empty((1,2,3))
+    >>> check, myiter = boolean_iter(mylist)
+    >>> check
+    True
+    >>> list(myiter)
+    [1, 2, 3]
+    >>> list(myiter)
+    []
+    """
+
+    def inner_check():
+        check, items = tee(iterable)
+        try:
+            next(check)
+        except StopIteration:
+            yield False
+        else:
+            yield True
+            yield from items
+
+    values = inner_check()
+    return next(values), values
+
+
+@adapter_config(name='boolean_iter', context=(Interface, Interface, Interface), provides=ITALESExtension)
+class IterValuesCheckerExpression(ContextRequestViewAdapter):
+    """TALES expression used to handle iterators
+
+    The expression returns a tuple containing a boolean flag indicating if the original iterator
+    is empty or not, and the original un-consumed iterator.
+    """
+
+    def render(self, context=None):
+        if context is None:
+            context = self.context
+        return boolean_iter(context)