Source code for alkali.peekorator

"""
from https://gist.github.com/dmckeone/7518335, slightly modified by Kurt Neufeld

Generic Peekorator, modeled after next(), for "looking into the future" of a generator/iterator.

Acknowledgements:

* "plof" for the name: http://stackoverflow.com/a/10576559/589362
* Ned Batchelder for the buffered __peek__: http://stackoverflow.com/a/1517965/589362
"""

[docs]class PeekoratorDefault(object): pass
[docs]def peek(peekorator, n=0, default=PeekoratorDefault()): """ next()-like function to be used with a Peekorator :param peekorator: Peekorator to use :param n: Number of items to look ahead :type n: int :param default: If the iterator is exhausted then a default is given, raise StopIteration if not given """ try: return peekorator.__peek__(n=n) except StopIteration: if isinstance(default, PeekoratorDefault): raise # StopIteration else: return default
[docs]class Peekorator(object): """ Wrap a generator (or iterator) and allow the ability to peek at the next element in a lazy fashion. If the user never uses peek(), then the only cost over a regular generator is the proxied function call. """ def __init__(self, generator): """ :param generator: a generator or iterator that will be iterated over """ # a generator is an iterator import collections if not isinstance( generator, collections.Iterator ): generator = iter(generator) self.generator = generator self._peek_buffer = [] self._first = None def __peek__(self, n=0): """ Return the peeked element for the generator :param n: how many iterations into the future to peek :type n: int """ while n >= len(self._peek_buffer): item = next(self.generator) self._peek_buffer.append(item) return self._peek_buffer[n] # Allow similar syntax to Python 2's next() peek = __peek__ def __next__(self): """ Get the next result from the generator """ if self._peek_buffer: ret = self._peek_buffer.pop(0) else: ret = next(self.generator) if self._first is None: self._first = True elif self._first is True: self._first = False return ret # Allow Python 2/3 next = __next__ def __iter__(self): """ Allow the Peakorator to be used as a regular iterable """ return self
[docs] def is_first(self): """ if you just got the first element then return True :rtype: bool """ # before first call to next() if self._first is None: return True return self._first
[docs] def is_last(self): """ if you're about to get the last element then return True :rtype: bool """ try: self.peek() except StopIteration: return True return False