Skip to content

Commit

Permalink
just(x) as a sampled_from strategy
Browse files Browse the repository at this point in the history
While kinda weird on the face of it, this makes generating unique collections a *lot* more efficient in certain unusual cases, and the overhead is roughly nil.
  • Loading branch information
Zac-HD committed Oct 9, 2019
1 parent 8c4db65 commit 61091b7
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 49 deletions.
6 changes: 6 additions & 0 deletions hypothesis-python/RELEASE.rst
@@ -0,0 +1,6 @@
RELEASE_TYPE: patch

This patch improves the performance of unique collections such as
:func:`~hypothesis.strategies.sets` of :func:`~hypothesis.strategies.just`
or :func:`~hypothesis.strategies.booleans` strategies. They were already
pretty good though, so you're unlikely to notice much!
8 changes: 2 additions & 6 deletions hypothesis-python/src/hypothesis/_strategies.py
Expand Up @@ -96,11 +96,7 @@
from hypothesis.searchstrategy.deferred import DeferredStrategy
from hypothesis.searchstrategy.functions import FunctionStrategy
from hypothesis.searchstrategy.lazy import LazyStrategy
from hypothesis.searchstrategy.misc import (
BoolStrategy,
JustStrategy,
SampledFromStrategy,
)
from hypothesis.searchstrategy.misc import JustStrategy, SampledFromStrategy
from hypothesis.searchstrategy.numbers import (
BoundedIntStrategy,
FixedBoundedFloatStrategy,
Expand Down Expand Up @@ -425,7 +421,7 @@ def booleans():
Examples from this strategy will shrink towards False (i.e.
shrinking will try to replace True with False where possible).
"""
return BoolStrategy()
return sampled_from([False, True])


@cacheable
Expand Down
61 changes: 27 additions & 34 deletions hypothesis-python/src/hypothesis/searchstrategy/misc.py
Expand Up @@ -22,20 +22,6 @@
from hypothesis.searchstrategy.strategies import SearchStrategy, filter_not_satisfied


class BoolStrategy(SearchStrategy):
"""A strategy that produces Booleans with a Bernoulli conditional
distribution."""

def __repr__(self):
return "BoolStrategy()"

def calc_has_reusable_values(self, recur):
return True

def do_draw(self, data):
return d.boolean(data)


def is_simple_data(value):
try:
hash(value)
Expand All @@ -44,26 +30,6 @@ def is_simple_data(value):
return False


class JustStrategy(SearchStrategy):
"""A strategy which always returns a single fixed value."""

def __init__(self, value):
SearchStrategy.__init__(self)
self.value = value

def __repr__(self):
return "just(%r)" % (self.value,)

def calc_has_reusable_values(self, recur):
return True

def calc_is_cacheable(self, recur):
return is_simple_data(self.value)

def do_draw(self, data):
return self.value


class SampledFromStrategy(SearchStrategy):
"""A strategy which samples from a set of elements. This is essentially
equivalent to using a OneOfStrategy over Just strategies but may be more
Expand Down Expand Up @@ -154,3 +120,30 @@ def check_index(i):
# If there are no allowed indices, the filter couldn't be satisfied.

return filter_not_satisfied


class JustStrategy(SampledFromStrategy):
"""A strategy which always returns a single fixed value.
It's implemented as a length-one SampledFromStrategy so that all our
special-case logic for filtering and sets applies also to just(x).
"""

def __init__(self, value):
SampledFromStrategy.__init__(self, [value])

@property
def value(self):
return self.elements[0]

def __repr__(self):
return "just(%r)" % (self.value,)

def calc_has_reusable_values(self, recur):
return True

def calc_is_cacheable(self, recur):
return is_simple_data(self.value)

def do_draw(self, data):
return self.value
8 changes: 3 additions & 5 deletions hypothesis-python/src/hypothesis/stateful.py
Expand Up @@ -42,7 +42,7 @@
)
from hypothesis.control import current_build_context
from hypothesis.core import given
from hypothesis.errors import HypothesisException, InvalidArgument, InvalidDefinition
from hypothesis.errors import InvalidArgument, InvalidDefinition
from hypothesis.internal.compat import quiet_raise, string_types
from hypothesis.internal.reflection import function_digest, nicerepr, proxies
from hypothesis.internal.validation import check_type
Expand Down Expand Up @@ -606,12 +606,10 @@ def __init__(self, machine):
)

def do_draw(self, data):
try:
rule = data.draw(st.sampled_from(self.rules).filter(self.is_valid))
except HypothesisException:
# FailedHealthCheck or UnsatisfiedAssumption depending on user settings.
if not any(self.is_valid(rule) for rule in self.rules):
msg = u"No progress can be made from state %r" % (self.machine,)
quiet_raise(InvalidDefinition(msg))
rule = data.draw(st.sampled_from(self.rules).filter(self.is_valid))
return (rule, data.draw(rule.arguments_strategy))

def is_valid(self, rule):
Expand Down
5 changes: 1 addition & 4 deletions hypothesis-python/tests/cover/test_simple_collections.py
Expand Up @@ -155,10 +155,7 @@ def test_can_draw_empty_set_from_unsatisfiable_strategy():
assert find_any(sets(integers().filter(lambda s: False))) == set()


small_set = sets(none())


@given(lists(small_set, min_size=10))
@given(lists(sets(none()), min_size=10))
def test_small_sized_sets(x):
pass

Expand Down

0 comments on commit 61091b7

Please sign in to comment.