def test_composite_parameter_name_clashes(): with pytest.raises(ValueError) as e: params.CompositeParameter( params.BiasedCoin(0.5), arg0=params.BiasedCoin(0.5), ) assert 'duplicate' in e.value.args[0].lower() with pytest.raises(ValueError) as e: params.CompositeParameter(__init__=params.BiasedCoin(0.5), ) assert 'invalid' in e.value.args[0].lower()
class GaussianFloatStrategy(FloatStrategy): """A float strategy such that every conditional distribution is drawn from a gaussian.""" parameter = params.CompositeParameter(mean=params.NormalParameter(0, 1), ) def produce(self, random, pv): return random.normalvariate(pv.mean, 1)
def __init__(self, float_strategy): super(ComplexStrategy, self).__init__() self.parameter = params.CompositeParameter( real=float_strategy.parameter, imaginary=float_strategy.parameter, ) self.float_strategy = float_strategy
class OneCharStringStrategy(SearchStrategy): """A strategy which generates single character strings of text type.""" descriptor = text_type ascii_characters = (text_type('0123456789') + text_type(string.ascii_letters) + text_type(' \t\n')) parameter = params.CompositeParameter( ascii_chance=params.UniformFloatParameter(0, 1)) def produce(self, random, pv): if dist.biased_coin(random, pv.ascii_chance): return random.choice(self.ascii_characters) else: while True: result = hunichr(random.randint(0, sys.maxunicode)) if unicodedata.category(result) != 'Cs': return result def simplify(self, x): if x in self.ascii_characters: for i in hrange(self.ascii_characters.index(x), -1, -1): yield self.ascii_characters[i] else: o = ord(x) for c in reversed(self.ascii_characters): yield text_type(c) if o > 0: yield hunichr(o // 2) yield hunichr(o - 1)
class FixedBoundedFloatStrategy(SearchStrategy): """A strategy for floats distributed between two endpoints. The conditional distribution tries to produce values clustered closer to one of the ends. """ descriptor = float parameter = params.CompositeParameter( cut=params.UniformFloatParameter(0, 1), leftwards=params.BiasedCoin(0.5), ) def __init__(self, lower_bound, upper_bound): SearchStrategy.__init__(self) self.lower_bound = float(lower_bound) self.upper_bound = float(upper_bound) def produce(self, random, pv): if pv.leftwards: left = self.lower_bound right = pv.cut else: left = pv.cut right = self.upper_bound return left + random.random() * (right - left) def simplify(self, value): yield self.lower_bound yield self.upper_bound yield (self.lower_bound + self.upper_bound) * 0.5
class FooStrategy(strat.SearchStrategy): descriptor = Foo parameter = params.CompositeParameter() has_immutable_data = False def produce(self, random, pv): return Foo()
def __init__(self, characters=None): SearchStrategy.__init__(self) if characters is not None and not isinstance(characters, text_type): raise ValueError('Invalid characters %r: Not a %s' % (characters, text_type)) self.characters = characters or (text_type('0123456789') + text_type(string.ascii_letters)) self.parameter = params.CompositeParameter()
def __init__(self): super(BoundedFloatStrategy, self).__init__() self.inner_strategy = FixedBoundedFloatStrategy(0, 1) self.parameter = params.CompositeParameter( left=params.NormalParameter(0, 1), length=params.ExponentialParameter(1), spread=self.inner_strategy.parameter, )
def __init__(self, strategies, tuple_type): SearchStrategy.__init__(self) strategies = tuple(strategies) self.tuple_type = tuple_type self.descriptor = self.newtuple([s.descriptor for s in strategies]) self.element_strategies = strategies self.parameter = params.CompositeParameter( x.parameter for x in self.element_strategies) self.has_immutable_data = all(s.has_immutable_data for s in strategies)
def __init__(self, strategies, average_length=50.0): SearchStrategy.__init__(self) self.descriptor = _unique(x.descriptor for x in strategies) self.element_strategy = one_of_strategies(strategies) self.parameter = params.CompositeParameter( average_length=params.ExponentialParameter(1.0 / average_length), child_parameter=self.element_strategy.parameter, )
def __init__(self, main_strategy, examples): assert examples assert all(main_strategy.could_have_produced(e) for e in examples) self.examples = tuple(examples) self.main_strategy = main_strategy self.descriptor = main_strategy.descriptor self.parameter = params.CompositeParameter( examples=params.NonEmptySubset(examples), example_probability=params.UniformFloatParameter(0.0, 0.5), main=main_strategy.parameter) self.has_immutable_data = main_strategy.has_immutable_data if hasattr(main_strategy, 'element_strategy'): self.element_strategy = main_strategy.element_strategy
def __init__(self, strategies): SearchStrategy.__init__(self) flattened_strategies = [] for s in strategies: if isinstance(s, OneOfStrategy): flattened_strategies += s.element_strategies else: flattened_strategies.append(s) strategies = tuple(flattened_strategies) if len(strategies) <= 1: raise ValueError('Need at least 2 strategies to choose amongst') descriptor = descriptors.one_of( _unique(s.descriptor for s in strategies)) self.descriptor = descriptor self.element_strategies = list(strategies) n = len(self.element_strategies) self.parameter = params.CompositeParameter( enabled_children=params.NonEmptySubset(range(n)), child_parameters=params.CompositeParameter( e.parameter for e in self.element_strategies)) self.has_immutable_data = all(s.has_immutable_data for s in self.element_strategies)
class RandomStrategy(SearchStrategy): """A strategy which produces Random objects. The conditional distribution is simply a RandomWithSeed seeded with a 128 bits of data chosen uniformly at random. """ descriptor = Random parameter = params.CompositeParameter() has_immutable_data = False def produce(self, random, pv): return RandomWithSeed(random.getrandbits(128)) def could_have_produced(self, value): return isinstance(value, RandomWithSeed)
class ExponentialFloatStrategy(FloatStrategy): """ A float strategy such that every conditional distribution is of the form aX + b where a = +/- 1 and X is an exponentially distributed random variable. """ parameter = params.CompositeParameter( lambd=params.GammaParameter(2, 50), zero_point=params.NormalParameter(0, 1), negative=params.BiasedCoin(0.5), ) def produce(self, random, pv): value = random.expovariate(pv.lambd) if pv.negative: value = -value return pv.zero_point + value
class FullRangeFloats(FloatStrategy): parameter = params.CompositeParameter( negative_probability=params.UniformFloatParameter(0, 1), subnormal_probability=params.UniformFloatParameter(0, 0.5), ) def produce(self, random, pv): sign = int(dist.biased_coin(random, pv.negative_probability)) if dist.biased_coin(random, pv.subnormal_probability): exponent = 0 else: exponent = random.getrandbits(11) return compose_float(sign, exponent, random.getrandbits(52)) def could_have_produced(self, value): return isinstance(value, float)
class RandomGeometricIntStrategy(IntStrategy): """A strategy that produces integers whose magnitudes are a geometric distribution and whose sign is randomized with some probability. It will tend to be biased towards mostly negative or mostly positive, and the size of the integers tends to be biased towards the small. """ parameter = params.CompositeParameter( negative_probability=params.BetaFloatParameter(0.5, 0.5), p=params.BetaFloatParameter(alpha=0.2, beta=1.8), ) def produce(self, random, parameter): value = dist.geometric(random, parameter.p) if dist.biased_coin(random, parameter.negative_probability): value = -value return value
class JustStrategy(SearchStrategy): """ A strategy which simply returns a single fixed value with probability 1. """ # We could do better here but it's probably not worth it # deepcopy has optimisations that will probably work just as well as # our check has_immutable_data = False def __init__(self, value): SearchStrategy.__init__(self) self.descriptor = descriptors.Just(value) def __repr__(self): return 'JustStrategy(value=%r)' % (self.descriptor.value, ) parameter = params.CompositeParameter() def produce(self, random, pv): return self.descriptor.value def could_have_produced(self, value): return actually_equal(self.descriptor.value, value)
class BoundedIntStrategy(SearchStrategy): """A strategy for providing integers in some interval with inclusive endpoints.""" descriptor = int parameter = params.CompositeParameter() def __init__(self, start, end): SearchStrategy.__init__(self) self.start = start self.end = end if start > end: raise ValueError('Invalid range [%d, %d]' % (start, end)) self.parameter = params.NonEmptySubset(tuple(range(start, end + 1)), activation_chance=min( 0.5, 3.0 / (end - start + 1))) def produce(self, random, parameter): if self.start == self.end: return self.start return random.choice(parameter) def simplify(self, x): if x == self.start: return for t in hrange(x - 1, self.start - 1, -1): yield t mid = (self.start + self.end) // 2 if x > mid: yield self.start + (self.end - x) for t in hrange(x + 1, self.end + 1): yield t def could_have_produced(self, i): return isinstance(i, integer_types) and (self.start <= i <= self.end)
class BrokenFloatStrategy(SearchStrategy): descriptor = float parameter = params.CompositeParameter() def produce(self, random, pv): return random.random()
class FooStrategy(SearchStrategy): descriptor = Foo parameter = params.CompositeParameter() def produce(self, random, pv): return Foo()
class DatetimeStrategy(SearchStrategy): descriptor = datetime parameter = params.CompositeParameter( p_hour=params.UniformFloatParameter(0, 1), p_minute=params.UniformFloatParameter(0, 1), p_second=params.UniformFloatParameter(0, 1), month=params.NonEmptySubset(list(range(1, 13))), naive_chance=params.UniformFloatParameter(0, 0.5), utc_chance=params.UniformFloatParameter(0, 1), timezones=params.NonEmptySubset( list(map(pytz.timezone, pytz.all_timezones)))) def produce(self, random, pv): year = random.randint(MINYEAR, MAXYEAR) month = random.choice(pv.month) base = datetime( year=year, month=month, day=draw_day_for_month(random, year, month), hour=maybe_zero_or(random, pv.p_hour, random.randint(0, 23)), minute=maybe_zero_or(random, pv.p_minute, random.randint(0, 59)), second=maybe_zero_or(random, pv.p_second, random.randint(0, 59)), microsecond=random.randint(0, 1000000 - 1), ) if random.random() <= pv.naive_chance: return base if random.random() <= pv.utc_chance: return pytz.UTC.localize(base) return random.choice(pv.timezones).localize(base) def simplify(self, value): if not value.tzinfo: yield pytz.UTC.localize(value) elif value.tzinfo != pytz.UTC: yield pytz.UTC.normalize(value.astimezone(pytz.UTC)) s = {value} s.add(value.replace(microsecond=0)) s.add(value.replace(second=0)) s.add(value.replace(minute=0)) s.add(value.replace(hour=0)) s.add(value.replace(day=1)) s.add(value.replace(month=1)) s.remove(value) for t in s: yield t year = value.year if year == 2000: return yield value.replace(year=2000) # We swallow a bunch of value errors here. # These can happen if the original value was february 29 on a # leap year and the current year is not a leap year. # Note that 2000 was a leap year which is why we didn't need one above. mid = (year + 2000) // 2 if mid != 2000 and mid != year: try: yield value.replace(year=mid) except ValueError: pass years = hrange(year, 2000, -1 if year > 2000 else 1) for year in years: if year == mid: continue try: yield value.replace(year) except ValueError: pass
class AwkwardStrategy(SearchStrategy): descriptor = Awkward parameter = params.CompositeParameter() def produce(self, random, pv): return Awkward()