Source code for pyquchk.arbitraries.utils

""" Contain utility functions and decorators useful when defining custom
:class:`.Arbitrary` instances. Usually they haven't much use otherwise.
"""

from itertools import cycle, islice
import random
from decorator import decorator
from six import PY3

__all__ = ['roundrobin', 'choice_weighted',
           'filter_possible', 'until_possible',
           'unique']


@decorator
[docs]def filter_possible(method, self, *args, **kwargs): """ Decorator, when applied to a generator method (e.g. :meth:`.gen_serial`) filters generated values to keep only those for which :meth:`.could_generate` returns ``True``. """ for item in method(self, *args, **kwargs): if self.could_generate(item): yield item
@decorator
[docs]def until_possible(method, self, *args, **kwargs): """ Decorator, when applied to a method returning different values each time (e.g. :meth:`.next_random`) runs this method multiple times until a value for which :meth:`.could_generate` returns ``True`` is returned, and finally returns this value. """ func = lambda: method(self, *args, **kwargs) for item in iter(func, object()): if self.could_generate(item): return item
@decorator
[docs]def unique(method, self, x): """ Decorator, when applied to a generator method (e.g. :meth:`.gen_serial`, :meth:`.shrink`) filters generated values to keep only unique values. For the :meth:`.shrink` method the value passed to the method is also considered. """ seen = set() if 'shrink' in method.__name__: seen.add(x) for item in method(self, x): if item not in seen: yield item seen.add(item)
[docs]def roundrobin(iterables): """ Yield items from the given iterables in a round-robin fashion. What it does exactly is better explained with a simple example: >>> list(roundrobin(['ABC', 'D', 'EF'])) ['A', 'D', 'E', 'B', 'F', 'C'] :param iterables: List of finite or infinite iterables. """ # Recipe credited to George Sakkis pending = len(iterables) iterators = (iter(it) for it in iterables) nexts = cycle(it.__next__ if PY3 else it.next for it in iterators) while pending: try: for next_f in nexts: yield next_f() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending))
[docs]def choice_weighted(elements): """ Like :func:`random.choice`, but the probability an item is chosen is proportional to its weight. For convenience, this function is available as ``random.choice_weighted`` if you imported :mod:`.utils`. :param elements: list of tuples ``(item, weight)``, weight is any numeric value """ rnd = random.random() * sum(w for _, w in elements) for el, w in elements: rnd -= w if rnd < 0: return el
random.choice_weighted = choice_weighted

Project Versions