import itertools import io class Key(object): def __init__(self, key, **kwargs): """ Constructor for Key class. Autogenerates key properties in object given dict or kwargs :param key: dict of name-values :param kwargs: optional keyword arguments :return: void """ key = key if key is not None else kwargs self.__dict__.update(key) def __repr__(self): return self.__dict__.__repr__() class OrderingDirection(object): def __init__(self, key, reverse): """ A container to hold the lambda key and sorting direction :param key: lambda function :param reverse: boolean. True for reverse sort """ self.key = key self.descending = reverse class RepeatableIterable(object): def __init__(self, data): """ Constructor. Pretty straight forward except for the is_generator check. This is so we can detect when a function that generates data is passed as a datasource. In this case we need to exhaust the values in the function generator """ if data is None: data = [] if not hasattr(data, "__iter__"): raise TypeError( u"RepeatableIterable must be instantiated with an iterable object" ) is_generator = hasattr(data, "gi_running") or isinstance(data, io.TextIOBase) self._data = data if not is_generator else [i for i in data] self._len = None self.cycle = itertools.cycle(self._data) def __len__(self): if self._len is None: self._len = sum(1 for item in self._data) return self._len def __iter__(self): i = 0 while i < len(self): yield next(self.cycle) i += 1 def __next__(self): return self.next() def next(self): return next(self.cycle)