8000 Merge pull request #3009 from Zac-HD/tuple-annotations · HypothesisWorks/hypothesis@2b7d74e · GitHub
[go: up one dir, main page]

Skip to content

Commit 2b7d74e

Browse files
authored
Merge pull request #3009 from Zac-HD/tuple-annotations
Annotate `tuples()` elements using overloads
2 parents 765af2b + 098d39d commit 2b7d74e

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

hypothesis-python/RELEASE.rst

+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch improves the :func:`~hypothesis.strategies.tuples` strategy
4+
type annotations, to preserve the element types for up to length-five
5+
tuples (:issue:`3005`).
6+
7+
As for :func:`~hypothesis.strategies.one_of`, this is the best we can do
8+
before a `planned extension <https://mail.python.org/archives/list/typing-sig@python.org/thread/LOQFV3IIWGFDB7F5BDX746EZJG4VVBI3/>`__
9+
to :pep:`646` is released, hopefully in Python 3.11.

hypothesis-python/src/hypothesis/strategies/_internal/collections.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,20 @@
1313
#
1414
# END HEADER
1515

16+
from typing import Any, Tuple, overload
17+
1618
from hypothesis.errors import InvalidArgument
1719
from hypothesis.internal.conjecture import utils as cu
1820
from hypothesis.internal.conjecture.junkdrawer import LazySequenceCopy
1921
from hypothesis.internal.conjecture.utils import combine_labels
2022
from hypothesis.strategies._internal.strategies import (
23+
T3,
24+
T4,
25+
T5,
26+
Ex,
2127
MappedSearchStrategy,
2228
SearchStrategy,
29+
T,
2330
check_strategy,
2431
filter_not_satisfied,
2532
)
@@ -44,10 +51,7 @@ def calc_label(self):
4451
)
4552

4653
def __repr__(self):
47-
if len(self.element_strategies) == 1:
48-
tuple_string = f"{self.element_strategies[0]!r},"
49-
else:
50-
tuple_string = ", ".join(map(repr, self.element_strategies))
54+
tuple_string = ", ".join(map(repr, self.element_strategies))
5155
return f"TupleStrategy(({tuple_string}))"
5256

5357
def calc_has_reusable_values(self, recur):
@@ -60,9 +64,59 @@ def calc_is_empty(self, recur):
6064
return any(recur(e) for e in self.element_strategies)
6165

6266

67+
@overload
68+
def tuples() -> SearchStrategy[Tuple[()]]:
69+
raise NotImplementedError
70+
71+
72+
@overload # noqa: F811
73+
def tuples(a1: SearchStrategy[Ex]) -> SearchStrategy[Tuple[Ex]]:
74+
raise NotImplementedError
75+
76+
77+
@overload # noqa: F811
78+
def tuples(
79+
a1: SearchStrategy[Ex], a2: SearchStrategy[T]
80+
) -> SearchStrategy[Tuple[Ex, T]]:
81+
raise NotImplementedError
82+
83+
84+
@overload # noqa: F811
85+
def tuples(
86+
a1: SearchStrategy[Ex], a2: SearchStrategy[T], a3: SearchStrategy[T3]
87+
) -> SearchStrategy[Tuple[Ex, T, T3]]:
88+
raise NotImplementedError
89+
90+
91+
@overload # noqa: F811
92+
def tuples(
93+
a1: SearchStrategy[Ex],
94+
a2: SearchStrategy[T],
95+
a3: SearchStrategy[T3],
96+
a4: SearchStrategy[T4],
97+
) -> SearchStrategy[Tuple[Ex, T, T3, T4]]:
98+
raise NotImplementedError
99+
100+
101+
@overload # noqa: F811
102+
def tuples(
103+
a1: SearchStrategy[Ex],
104+
a2: SearchStrategy[T],
105+
a3: SearchStrategy[T3],
106+
a4: SearchStrategy[T4],
107+
a5: SearchStrategy[T5],
108+
) -> SearchStrategy[Tuple[Ex, T, T3, T4, T5]]:
109+
raise NotImplementedError
110+
111+
112+
@overload # noqa: F811
113+
def tuples(*args: SearchStrategy[Any]) -> SearchStrategy[Tuple]:
114+
raise NotImplementedError
115+
116+
63117
@cacheable
64118
@defines_strategy()
65-
def tuples(*args: SearchStrategy) -> SearchStrategy[tuple]:
119+
def tuples(*args): # noqa: F811
66120
"""Return a strategy which generates a tuple of the same length as args by
67121
generating the value at index i from args[i].
68122

whole-repo-tests/test_type_hints.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ def get_mypy_analysed_type(fname, val):
7272
"one_of(integers(), text(), none(), binary(), builds(list), builds(dict))",
7373
"Any",
7474
),
75+
("tuples()", "Tuple[]"), # Should be `Tuple[()]`, but this is what mypy prints
76+
("tuples(integers())", "Tuple[int]"),
77+
("tuples(integers(), text())", "Tuple[int, str]"),
78+
(
79+
"tuples(integers(), text(), integers(), text(), integers())",
80+
"Tuple[int, str, int, str, int]",
81+
),
82+
(
83+
"tuples(text(), text(), text(), text(), text(), text())",
84+
"tuple[Any]", # note lower-case; this is the arbitrary-length *args case
85+
),
7586
],
7687
)
7788
def test_revealed_types(tmpdir, val, expect):

0 commit comments

Comments
 (0)
0