Skip to content

Commit

Permalink
Merge pull request #3971 from Zac-HD/composite-overload-warning
Browse files Browse the repository at this point in the history
Don't warn on typing overloads for a composite function
  • Loading branch information
Zac-HD authored May 4, 2024
2 parents 70a72bb + a0d3487 commit 683e691
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 5 deletions.
5 changes: 5 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RELEASE_TYPE: patch

This patch turns off a warning for functions decorated with
:func:`typing.overload` and then :func:`~hypothesis.strategies.composite`,
although only in that order (:issue:`3970`).
6 changes: 6 additions & 0 deletions hypothesis-python/docs/strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ Or some other custom integration, such as a :ref:`"hypothesis" entry point <entr
Other cool things
-----------------

`Tyche <https://marketplace.visualstudio.com/items?itemName=HarrisonGoldstein.tyche>`__
(`source <https://github.com/tyche-pbt>`__) is a VSCode extension which provides live
insights into your property-based tests, including the distribution of generated inputs
and the resulting code coverage. You can `read the research paper here
<https://harrisongoldste.in/papers/uist23.pdf>`__.

:pypi:`schemathesis` is a tool for testing web applications built with `Open API / Swagger specifications <https://swagger.io/>`_.
It reads the schema and generates test cases which will ensure that the application is compliant with its schema.
The application under test could be written in any language, the only thing you need is a valid API schema in a supported format.
Expand Down
3 changes: 1 addition & 2 deletions hypothesis-python/src/hypothesis/internal/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def pretty_file_name(f):


IN_COVERAGE_TESTS = os.getenv("HYPOTHESIS_INTERNAL_COVERAGE") == "true"
description_stack = []


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

description_stack = []

@contextmanager
def check_block(name, depth):
# We add an extra two callers to the stack: One for the contextmanager
Expand Down
21 changes: 18 additions & 3 deletions hypothesis-python/src/hypothesis/strategies/_internal/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
is_typed_named_tuple,
)
from hypothesis.internal.conjecture.utils import calc_label_from_cls, check_sample
from hypothesis.internal.coverage import IN_COVERAGE_TESTS, description_stack
from hypothesis.internal.entropy import get_seeder_and_restorer
from hypothesis.internal.floats import float_of
from hypothesis.internal.observability import TESTCASE_CALLBACKS
Expand Down Expand Up @@ -1757,8 +1758,22 @@ def __init__(self, definition, args, kwargs):
self.args = args
self.kwargs = kwargs

def do_draw(self, data):
return self.definition(data.draw, *self.args, **self.kwargs)
if IN_COVERAGE_TESTS:
# We do a bit of a dance here to ensure that whatever 'outer' description
# stack we might have, doesn't affect the validation code internal to the
# strategy we're drawing from. Otherwise, we'd get flaky fails like #3968.
def do_draw(self, data):
prev = description_stack[:]
try:
description_stack.clear()
return self.definition(data.draw, *self.args, **self.kwargs)
finally:
description_stack[:] = prev

else: # pragma: no cover

def do_draw(self, data):
return self.definition(data.draw, *self.args, **self.kwargs)

def calc_label(self):
return calc_label_from_cls(self.definition)
Expand Down Expand Up @@ -1810,7 +1825,7 @@ def _composite(f):
)
if params[0].default is not sig.empty:
raise InvalidArgument("A default value for initial argument will never be used")
if not is_first_param_referenced_in_function(f):
if not (f is typing._overload_dummy or is_first_param_referenced_in_function(f)):
note_deprecation(
"There is no reason to use @st.composite on a function which "
"does not call the provided draw() function internally.",
Expand Down
16 changes: 16 additions & 0 deletions hypothesis-python/tests/cover/test_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# obtain one at https://mozilla.org/MPL/2.0/.

import sys
import typing

import pytest

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

assert len(w.list) == 1
assert w.list[0].filename == __file__ # check stacklevel points to user code


def test_composite_allows_overload_without_draw():
# See https://github.com/HypothesisWorks/hypothesis/issues/3970
@st.composite
@typing.overload
def overloaded(draw: st.DrawFn, *, x: int) -> typing.Literal[True]: ...

@st.composite
@typing.overload
def overloaded(draw: st.DrawFn, *, x: str) -> typing.Literal[False]: ...

@st.composite
def overloaded(draw: st.DrawFn, *, x: typing.Union[int, str]) -> bool:
return draw(st.just(isinstance(x, int)))

0 comments on commit 683e691

Please sign in to comment.