Skip to content

Commit 683e691

Browse files
authored
Merge pull request #3971 from Zac-HD/composite-overload-warning
Don't warn on typing overloads for a composite function
2 parents 70a72bb + a0d3487 commit 683e691

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

hypothesis-python/RELEASE.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch turns off a warning for functions decorated with
4+
:func:`typing.overload` and then :func:`~hypothesis.strategies.composite`,
5+
although only in that order (:issue:`3970`).

hypothesis-python/docs/strategies.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ Or some other custom integration, such as a :ref:`"hypothesis" entry point <entr
5757
Other cool things
5858
-----------------
5959

60+
`Tyche <https://marketplace.visualstudio.com/items?itemName=HarrisonGoldstein.tyche>`__
61+
(`source <https://github.com/tyche-pbt>`__) is a VSCode extension which provides live
62+
insights into your property-based tests, including the distribution of generated inputs
63+
and the resulting code coverage. You can `read the research paper here
64+
<https://harrisongoldste.in/papers/uist23.pdf>`__.
65+
6066
:pypi:`schemathesis` is a tool for testing web applications built with `Open API / Swagger specifications <https://swagger.io/>`_.
6167
It reads the schema and generates test cases which will ensure that the application is compliant with its schema.
6268
The application under test could be written in any language, the only thing you need is a valid API schema in a supported format.

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def pretty_file_name(f):
4848

4949

5050
IN_COVERAGE_TESTS = os.getenv("HYPOTHESIS_INTERNAL_COVERAGE") == "true"
51+
description_stack = []
5152

5253

5354
if IN_COVERAGE_TESTS:
@@ -64,8 +65,6 @@ def record_branch(name, value):
6465
with open(f"branch-check-{os.getpid()}", mode="a", encoding="utf-8") as log:
6566
log.write(json.dumps({"name": name, "value": value}) + "\n")
6667

67-
description_stack = []
68-
6968
@contextmanager
7069
def check_block(name, depth):
7170
# We add an extra two callers to the stack: One for the contextmanager

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
is_typed_named_tuple,
8484
)
8585
from hypothesis.internal.conjecture.utils import calc_label_from_cls, check_sample
86+
from hypothesis.internal.coverage import IN_COVERAGE_TESTS, description_stack
8687
from hypothesis.internal.entropy import get_seeder_and_restorer
8788
from hypothesis.internal.floats import float_of
8889
from hypothesis.internal.observability import TESTCASE_CALLBACKS
@@ -1757,8 +1758,22 @@ def __init__(self, definition, args, kwargs):
17571758
self.args = args
17581759
self.kwargs = kwargs
17591760

1760-
def do_draw(self, data):
1761-
return self.definition(data.draw, *self.args, **self.kwargs)
1761+
if IN_COVERAGE_TESTS:
1762+
# We do a bit of a dance here to ensure that whatever 'outer' description
1763+
# stack we might have, doesn't affect the validation code internal to the
1764+
# strategy we're drawing from. Otherwise, we'd get flaky fails like #3968.
1765+
def do_draw(self, data):
1766+
prev = description_stack[:]
1767+
try:
1768+
description_stack.clear()
1769+
return self.definition(data.draw, *self.args, **self.kwargs)
1770+
finally:
1771+
description_stack[:] = prev
1772+
1773+
else: # pragma: no cover
1774+
1775+
def do_draw(self, data):
1776+
return self.definition(data.draw, *self.args, **self.kwargs)
17621777

17631778
def calc_label(self):
17641779
return calc_label_from_cls(self.definition)
@@ -1810,7 +1825,7 @@ def _composite(f):
18101825
)
18111826
if params[0].default is not sig.empty:
18121827
raise InvalidArgument("A default value for initial argument will never be used")
1813-
if not is_first_param_referenced_in_function(f):
1828+
if not (f is typing._overload_dummy or is_first_param_referenced_in_function(f)):
18141829
note_deprecation(
18151830
"There is no reason to use @st.composite on a function which "
18161831
"does not call the provided draw() function internally.",

hypothesis-python/tests/cover/test_composite.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# obtain one at https://mozilla.org/MPL/2.0/.
1010

1111
import sys
12+
import typing
1213

1314
import pytest
1415

@@ -204,3 +205,18 @@ def my_integers(draw: st.DrawFn) -> st.SearchStrategy[int]:
204205

205206
assert len(w.list) == 1
206207
assert w.list[0].filename == __file__ # check stacklevel points to user code
208+
209+
210+
def test_composite_allows_overload_without_draw():
211+
# See https://github.com/HypothesisWorks/hypothesis/issues/3970
212+
@st.composite
213+
@typing.overload
214+
def overloaded(draw: st.DrawFn, *, x: int) -> typing.Literal[True]: ...
215+
216+
@st.composite
217+
@typing.overload
218+
def overloaded(draw: st.DrawFn, *, x: str) -> typing.Literal[False]: ...
219+
220+
@st.composite
221+
def overloaded(draw: st.DrawFn, *, x: typing.Union[int, str]) -> bool:
222+
return draw(st.just(isinstance(x, int)))

0 commit comments

Comments
 (0)