"""
Arbitrary value generators and decorators for random value testing
"""

import functools
import itertools
import random
import string

# The maximum size for generated test cases
MAX_SIZE = 100

# The maximum number of successful tests before succeeding
MAX_SUCCESS = 250

def items(xs):
  """Yield an infinite sequence of randomly-selected items from a list."""
  while True:
    yield random.choice(xs)

def bools():
  """Yield an infinite sequence of randomly-generated bools."""
  return items([True, False])

def ints(lower=-MAX_SIZE, upper=MAX_SIZE):
  """Yield an infinite sequence of randomly-generated ints."""
  while True:
    yield random.randint(lower, upper)

def lists(xs, minLen=0, maxLen=MAX_SIZE):
  """Yield an infinite sequence of randomly-generated lists containing values
  generated by xs."""
  for len_ in ints(minLen, maxLen):
    yield list(itertools.islice(xs, len_))

def tuples(*xss):
  """Yield an infinite sequence of randomly-generated tuples containing values
  generated by each of the generators in xss."""
  return itertools.izip(*xss)

def strs(chars=string.printable, minLen=0, maxLen=MAX_SIZE):
  """Yield an infinite sequence of randomly-generated strings containing
  characters in chars."""
  for list_ in lists(items(string.printable), minLen, maxLen):
    yield "".join(list_)

def _kwargss(kwxss):
  """Yield an infinite sequence of keyword argument dicts populated by random
  values from each of the keyword, generator entries in kwxss."""
  while True:
    yield dict((kw, next(xs)) for kw, xs in kwxss.iteritems())

def arbitrary(**kwxss):
  """A decorator for creating generators that yield randomly-generated objects
  returned by a function"""
  def decorate(f):
    @functools.wraps(f)
    def fs():
      for kwargs in _kwargss(kwxss):
        yield f(**kwargs)

    return fs

  return decorate

def forall(ncases=MAX_SUCCESS, **kwxss):
  """A decorator for creating a test function that runs with randomly-generated
  cases"""
  def decorate(test):
    @functools.wraps(test)
    def tests(self):
      for kwargs in itertools.islice(_kwargss(kwxss), ncases):
        test(self, **kwargs)

    return tests

  return decorate

Add the arbitrary module