Skip to content

Commit 26f0da5

Browse files
committed
Make SearchStrategy generic in the type of examples
1 parent b340323 commit 26f0da5

File tree

5 files changed

+178
-63
lines changed

5 files changed

+178
-63
lines changed

.flake8

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ exclude =
77
test_imports.py,
88
hypothesis-python/tests/py2/*,
99
test_lambda_formatting.py
10-
ignore = D1,D205,D209,D213,D400,D401,D999
10+
ignore = F811,D1,D205,D209,D213,D400,D401,D999

hypothesis-python/src/hypothesis/internal/renaming.py

+5
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@
2222
from hypothesis._settings import note_deprecation
2323
from hypothesis.internal.reflection import proxies
2424

25+
if False:
26+
from typing import Callable # noqa
27+
from hypothesis.searchstrategy.strategies import T # noqa
28+
2529

2630
def renamed_arguments(**rename_mapping):
31+
# type: (**str) -> Callable[[T], T]
2732
"""Helper function for deprecating arguments that have been renamed to a
2833
new form.
2934

hypothesis-python/src/hypothesis/searchstrategy/strategies.py

+29-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import hypothesis.internal.conjecture.utils as cu
2323
from hypothesis.errors import NoExamples, NoSuchExample, Unsatisfiable, \
2424
UnsatisfiedAssumption
25-
from hypothesis.control import assume, reject, _current_build_context
25+
from hypothesis.control import assume, _current_build_context
2626
from hypothesis._settings import note_deprecation
2727
from hypothesis.internal.compat import hrange, bit_length
2828
from hypothesis.utils.conventions import UniqueIdentifier
@@ -31,6 +31,18 @@
3131
from hypothesis.internal.conjecture.utils import LABEL_MASK, \
3232
combine_labels, calc_label_from_cls, calc_label_from_name
3333

34+
try:
35+
from random import Random # noqa
36+
from typing import List, Callable, TypeVar, Generic, Optional # noqa
37+
Ex = TypeVar('Ex', covariant=True)
38+
T = TypeVar('T')
39+
40+
from hypothesis.internal.conjecture.data import ConjectureData # noqa
41+
42+
except ImportError: # pragma: no cover
43+
Ex = 'key' # type: ignore
44+
Generic = {Ex: object} # type: ignore
45+
3446
calculating = UniqueIdentifier('calculating')
3547

3648
MAPPED_SEARCH_STRATEGY_DO_DRAW_LABEL = calc_label_from_name(
@@ -45,7 +57,7 @@ def one_of_strategies(xs):
4557
return OneOfStrategy(xs)
4658

4759

48-
class SearchStrategy(object):
60+
class SearchStrategy(Generic[Ex]):
4961
"""A SearchStrategy is an object that knows how to explore data of a given
5062
type.
5163
@@ -228,6 +240,7 @@ def calc_has_reusable_values(self, recur):
228240
return False
229241

230242
def example(self, random=None):
243+
# type: (Random) -> Ex
231244
"""Provide an example of the sort of value that this strategy
232245
generates. This is biased to be slightly simpler than is typical for
233246
values from this strategy, for clarity purposes.
@@ -270,7 +283,7 @@ def example(self, random=None):
270283
# Conjecture will always try the zero example first. This would result
271284
# in us producing the same example each time, which is boring, so we
272285
# deliberately skip the first example it feeds us.
273-
first = []
286+
first = [] # type: list
274287

275288
def condition(x):
276289
if first:
@@ -299,6 +312,7 @@ def condition(x):
299312
)
300313

301314
def map(self, pack):
315+
# type: (Callable[[Ex], T]) -> SearchStrategy[T]
302316
"""Returns a new strategy that generates values by generating a value
303317
from this strategy and then calling pack() on the result, giving that.
304318
@@ -309,6 +323,7 @@ def map(self, pack):
309323
)
310324

311325
def flatmap(self, expand):
326+
# type: (Callable[[Ex], SearchStrategy[T]]) -> SearchStrategy[T]
312327
"""Returns a new strategy that generates values by generating a value
313328
from this strategy, say x, then generating a value from
314329
strategy(expand(x))
@@ -321,6 +336,7 @@ def flatmap(self, expand):
321336
)
322337

323338
def filter(self, condition):
339+
# type: (Callable[[Ex], bool]) -> SearchStrategy[Ex]
324340
"""Returns a new strategy that generates values from this strategy
325341
which satisfy the provided condition. Note that if the condition is too
326342
hard to satisfy this might result in your tests failing with
@@ -335,6 +351,7 @@ def filter(self, condition):
335351

336352
@property
337353
def branches(self):
354+
# type: () -> List[SearchStrategy[Ex]]
338355
return [self]
339356

340357
def __or__(self, other):
@@ -348,6 +365,7 @@ def __or__(self, other):
348365
return one_of_strategies((self, other))
349366

350367
def validate(self):
368+
# type: () -> None
351369
"""Throw an exception if the strategy is not valid.
352370
353371
This can happen due to lazy construction
@@ -392,6 +410,7 @@ def do_validate(self):
392410
pass
393411

394412
def do_draw(self, data):
413+
# type: (ConjectureData) -> Ex
395414
raise NotImplementedError('%s.do_draw' % (type(self).__name__,))
396415

397416
def __init__(self):
@@ -469,6 +488,7 @@ def calc_label(self):
469488
])
470489

471490
def do_draw(self, data):
491+
# type: (ConjectureData) -> Ex
472492
n = len(self.element_strategies)
473493
assert n > 0
474494
if n == 1:
@@ -538,6 +558,7 @@ def pack(self, x): # type: ignore
538558
'%s.pack()' % (self.__class__.__name__))
539559

540560
def do_draw(self, data):
561+
# type: (ConjectureData) -> Ex
541562
for _ in range(3):
542563
i = data.index
543564
try:
@@ -549,10 +570,11 @@ def do_draw(self, data):
549570
data.stop_example(discard=True)
550571
if data.index == i:
551572
raise
552-
reject()
573+
raise UnsatisfiedAssumption()
553574

554575
@property
555576
def branches(self):
577+
# type: () -> List[SearchStrategy[Ex]]
556578
return [
557579
MappedSearchStrategy(pack=self.pack, strategy=strategy)
558580
for strategy in self.mapped_strategy.branches
@@ -584,6 +606,7 @@ def do_validate(self):
584606
self.filtered_strategy.validate()
585607

586608
def do_draw(self, data):
609+
# type: (ConjectureData) -> Ex
587610
for i in hrange(3):
588611
start_index = data.index
589612
value = data.draw(self.filtered_strategy)
@@ -601,9 +624,11 @@ def do_draw(self, data):
601624
self,
602625
))
603626
data.mark_invalid()
627+
raise AssertionError('Unreachable, for Mypy') # pragma: no cover
604628

605629
@property
606630
def branches(self):
631+
# type: () -> List[SearchStrategy[Ex]]
607632
return [
608633
FilteredStrategy(strategy=strategy, condition=self.condition)
609634
for strategy in self.filtered_strategy.branches

0 commit comments

Comments
 (0)