# HG changeset patch # User Thierry Florac # Date 1558527186 -7200 # Node ID d4e617c7e37a3bdc59444a582af1e0f3724d31ea # Parent 5aa846ce2a2d7d6ba55812dd035559d30e54cbce Added "boolean_iter" function and TALES extension to check if an iterator returns at least one value, without consuming it diff -r 5aa846ce2a2d -r d4e617c7e37a 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)