diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..2384844e68 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +:func:`hypothesis.event` now works for hashable objects which do not +support weakrefs, such as integers and tuples. diff --git a/hypothesis-python/src/_hypothesis_pytestplugin.py b/hypothesis-python/src/_hypothesis_pytestplugin.py index bc91b60302..0155c9f232 100644 --- a/hypothesis-python/src/_hypothesis_pytestplugin.py +++ b/hypothesis-python/src/_hypothesis_pytestplugin.py @@ -180,6 +180,7 @@ def pytest_configure(config): @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): + __tracebackhide__ = True if not (hasattr(item, "obj") and "hypothesis" in sys.modules): yield return @@ -192,10 +193,14 @@ def pytest_runtest_call(item): if not is_hypothesis_test(item.obj): # If @given was not applied, check whether other hypothesis # decorators were applied, and raise an error if they were. + # We add this frame of indirection to enable __tracebackhide__. + def raise_hypothesis_usage_error(msg): + raise InvalidArgument(msg) + if getattr(item.obj, "is_hypothesis_strategy_function", False): from hypothesis.errors import InvalidArgument - raise InvalidArgument( + raise_hypothesis_usage_error( f"{item.nodeid} is a function that returns a Hypothesis strategy, " "but pytest has collected it as a test function. This is useless " "as the function body will never be executed. To define a test " @@ -211,7 +216,7 @@ def pytest_runtest_call(item): if hasattr(item.obj, attribute): from hypothesis.errors import InvalidArgument - raise InvalidArgument(message % (name,)) + raise_hypothesis_usage_error(message % (name,)) yield else: from hypothesis import HealthCheck, settings diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/engine.py b/hypothesis-python/src/hypothesis/internal/conjecture/engine.py index b3fad161ee..750e68edd4 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/engine.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/engine.py @@ -1067,10 +1067,13 @@ def event_to_string(self, event): return event try: return self.events_to_strings[event] - except KeyError: + except (KeyError, TypeError): pass result = str(event) - self.events_to_strings[event] = result + try: + self.events_to_strings[event] = result + except TypeError: + pass return result diff --git a/hypothesis-python/tests/conjecture/test_engine.py b/hypothesis-python/tests/conjecture/test_engine.py index 34f76856d4..99c4ce549d 100644 --- a/hypothesis-python/tests/conjecture/test_engine.py +++ b/hypothesis-python/tests/conjecture/test_engine.py @@ -1587,3 +1587,8 @@ def test(data): runner.cached_test_function([c]) assert runner.tree.is_exhausted + + +def test_can_convert_non_weakref_types_to_event_strings(): + runner = ConjectureRunner(lambda data: None) + runner.event_to_string(()) diff --git a/hypothesis-python/tests/cover/test_statistical_events.py b/hypothesis-python/tests/cover/test_statistical_events.py index 34ca34439e..2eaa1287e1 100644 --- a/hypothesis-python/tests/cover/test_statistical_events.py +++ b/hypothesis-python/tests/cover/test_statistical_events.py @@ -254,3 +254,8 @@ def test(value): stats = describe_statistics(call_for_statistics(test)) assert "- Events:" in stats assert "- Highest target score: " in stats + + +@given(st.booleans()) +def test_event_with_non_weakrefable_keys(b): + event((b,)) diff --git a/hypothesis-python/tests/pytest/test_checks.py b/hypothesis-python/tests/pytest/test_checks.py index 48b8e629e3..911b142b38 100644 --- a/hypothesis-python/tests/pytest/test_checks.py +++ b/hypothesis-python/tests/pytest/test_checks.py @@ -33,4 +33,6 @@ def test_repro_without_given_fails(): def test_decorators_without_given_should_fail(testdir): script = testdir.makepyfile(TEST_DECORATORS_ALONE) - testdir.runpytest(script).assert_outcomes(failed=4) + result = testdir.runpytest(script) + result.assert_outcomes(failed=4) + assert "pytest_runtest_call" not in "\n".join(result.outlines)