Added "boolean_iter" function and TALES extension to check if an iterator returns at least one value, without consuming it
--- 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)