Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,7 @@ jobs:
pip install -r requirements/crosshair.txt
pip install hypothesis-python/
- name: Run tests
# remove this ignore when https://github.com/pschanely/hypothesis-crosshair/issues/40 is fixed
run: python -m pytest -W "ignore:hypothesis.internal.observability.TESTCASE_CALLBACKS is deprecated" --numprocesses auto ${{ matrix.whichtests == 'nocover' && 'hypothesis-python/tests/nocover' || 'hypothesis-python/tests/ --ignore=hypothesis-python/tests/nocover/ --ignore=hypothesis-python/tests/quality/ --ignore=hypothesis-python/tests/ghostwriter/ --ignore=hypothesis-python/tests/patching/' }}
run: python -m pytest --numprocesses auto ${{ matrix.whichtests == 'nocover' && 'hypothesis-python/tests/nocover' || 'hypothesis-python/tests/ --ignore=hypothesis-python/tests/nocover/ --ignore=hypothesis-python/tests/quality/ --ignore=hypothesis-python/tests/ghostwriter/ --ignore=hypothesis-python/tests/patching/' }}

test-osx:
runs-on: macos-latest
Expand Down
3 changes: 3 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RELEASE_TYPE: patch

Fix a bug where we persisted symbolics from solver-based :ref:`alternative backends <alternative-backends>` in |event|.
4 changes: 2 additions & 2 deletions hypothesis-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pandas = ["pandas>=1.1"]
pytest = ["pytest>=4.6"]
dpcontracts = ["dpcontracts>=0.4"]
redis = ["redis>=3.0.0"]
crosshair = ["hypothesis-crosshair>=0.0.24", "crosshair-tool>=0.0.93"]
crosshair = ["hypothesis-crosshair>=0.0.25", "crosshair-tool>=0.0.93"]
# zoneinfo is an odd one: every dependency is platform-conditional.
zoneinfo = ["tzdata>=2025.2; sys_platform == 'win32' or sys_platform == 'emscripten'"]
# We only support Django versions with upstream support - see
Expand All @@ -121,7 +121,7 @@ zoneinfo = ["tzdata>=2025.2; sys_platform == 'win32' or sys_platform == 'emscrip
django = ["django>=4.2"]
watchdog = ["watchdog>=4.0.0"]
# Avoid changing this by hand. This is automatically updated by update_changelog_and_version
all = ["black>=20.8b0", "click>=7.0", "crosshair-tool>=0.0.93", "django>=4.2", "dpcontracts>=0.4", "hypothesis-crosshair>=0.0.24", "lark>=0.10.1", "libcst>=0.3.16", "numpy>=1.19.3", "pandas>=1.1", "pytest>=4.6", "python-dateutil>=1.4", "pytz>=2014.1", "redis>=3.0.0", "rich>=9.0.0", "tzdata>=2025.2; sys_platform == 'win32' or sys_platform == 'emscripten'", "watchdog>=4.0.0"]
all = ["black>=20.8b0", "click>=7.0", "crosshair-tool>=0.0.93", "django>=4.2", "dpcontracts>=0.4", "hypothesis-crosshair>=0.0.25", "lark>=0.10.1", "libcst>=0.3.16", "numpy>=1.19.3", "pandas>=1.1", "pytest>=4.6", "python-dateutil>=1.4", "pytz>=2014.1", "redis>=3.0.0", "rich>=9.0.0", "tzdata>=2025.2; sys_platform == 'win32' or sys_platform == 'emscripten'", "watchdog>=4.0.0"]

[tool.setuptools.dynamic]
version = {attr = "hypothesis.version.__version__"}
Expand Down
20 changes: 16 additions & 4 deletions hypothesis-python/src/hypothesis/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,22 +247,34 @@ def event(value: str, payload: Union[str, int, float] = "") -> None:
"""
context = _current_build_context.value
if context is None:
raise InvalidArgument("Cannot make record events outside of a test")
raise InvalidArgument("Cannot record events outside of a test")

payload = _event_to_string(payload, (str, int, float))
context.data.events[_event_to_string(value)] = payload
avoid_realization = context.data.provider.avoid_realization
payload = _event_to_string(
payload, allowed_types=(str, int, float), avoid_realization=avoid_realization
)
value = _event_to_string(value, avoid_realization=avoid_realization)
context.data.events[value] = payload


_events_to_strings: WeakKeyDictionary = WeakKeyDictionary()


def _event_to_string(event, allowed_types=str):
def _event_to_string(event, *, allowed_types=str, avoid_realization):
if isinstance(event, allowed_types):
return event

# _events_to_strings is a cache which persists across iterations, causing
# problems for symbolic backends. see
# https://github.com/pschanely/hypothesis-crosshair/issues/41
if avoid_realization:
return str(event)

try:
return _events_to_strings[event]
except (KeyError, TypeError):
pass

result = str(event)
try:
_events_to_strings[event] = result
Expand Down
15 changes: 10 additions & 5 deletions hypothesis-python/src/hypothesis/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,16 @@ def _execute_once_for_engine(self, data: ConjectureData) -> None:
finally:
# Conditional here so we can save some time constructing the payload; in
# other cases (without coverage) it's cheap enough to do that regardless.
#
# Note that we have to unconditionally realize data.events, because
# the statistics reported by the pytest plugin use a different flow
# than observability, but still access symbolic events.

try:
data.events = data.provider.realize(data.events)
except BackendCannotProceed:
data.events = {}

if observability_enabled():
if runner := getattr(self, "_runner", None):
phase = runner._current_phase
Expand All @@ -1332,11 +1342,6 @@ def _execute_once_for_engine(self, data: ConjectureData) -> None:
except BackendCannotProceed:
self._string_repr = "<backend failed to realize symbolic arguments>"

try:
data.events = data.provider.realize(data.events)
except BackendCannotProceed:
data.events = {}

data.freeze()
tc = make_testcase(
run_start=self._start_timestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ def test_function(self, data: ConjectureData) -> None:
if not interrupted: # pragma: no branch
assert data.cannot_proceed_scope is None
data.freeze()

if self.settings.backend != "hypothesis":
realize_choices(data, for_failure=data.status is Status.INTERESTING)

call_stats: CallStats = {
"status": data.status.name.lower(),
"runtime": data.finish_time - data.start_time,
Expand All @@ -566,8 +570,6 @@ def test_function(self, data: ConjectureData) -> None:
),
}
self.stats_per_test_case.append(call_stats)
if self.settings.backend != "hypothesis":
realize_choices(data, for_failure=data.status is Status.INTERESTING)

self._cache(data)
if data.misaligned_at is not None: # pragma: no branch # coverage bug?
Expand Down
3 changes: 2 additions & 1 deletion hypothesis-python/tests/cover/test_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,5 @@ def step(self):


def test_can_convert_non_weakref_types_to_event_strings():
_event_to_string(())
_event_to_string((), avoid_realization=True)
_event_to_string((), avoid_realization=False)
5 changes: 3 additions & 2 deletions hypothesis-python/tests/cover/test_statistical_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
strategies as st,
target,
)
from hypothesis.control import current_build_context
from hypothesis.statistics import collector, describe_statistics

from tests.common.utils import Why, xfail_on_crosshair
Expand Down Expand Up @@ -92,6 +93,8 @@ def test_formats_are_evaluated_only_once():

@given(st.integers())
def test(i):
if current_build_context().data.provider.avoid_realization:
pytest.skip("event() cache is disabled under avoid_realization = True")
event(Foo())

stats = call_for_statistics(test)
Expand Down Expand Up @@ -254,8 +257,6 @@ def threshold(error):
assert stats["targets"]["error"] > 10


# describe_statistics causes not-deterministic crosshair errors for some reason?
@xfail_on_crosshair(Why.other)
def test_statistics_with_events_and_target():
@given(st.integers(0, 10_000))
def test(value):
Expand Down
10 changes: 1 addition & 9 deletions hypothesis-python/tests/cover/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import pytest

from hypothesis import find, given, settings, strategies as st
from hypothesis import find, given, strategies as st
from hypothesis.errors import HypothesisWarning, InvalidArgument
from hypothesis.internal.validation import check_type
from hypothesis.strategies import (
Expand Down Expand Up @@ -278,14 +278,6 @@ def test_check_strategy_might_suggest_sampled_from():
check_strategy_(integers(), "passes for our custom coverage check")


@pytest.mark.xfail(
settings._current_profile == "crosshair",
reason=(
"conflicting warning filters. Remove when "
"https://github.com/pschanely/hypothesis-crosshair/issues/42 "
"is fixed"
),
)
@pytest.mark.parametrize("codec", ["ascii", "utf-8"])
def test_warn_on_strings_matching_common_codecs(codec):
with pytest.warns(
Expand Down
8 changes: 8 additions & 0 deletions hypothesis-python/tests/crosshair/test_crosshair.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,11 @@ def test(n):
test()

assert saw_myevent


# see https://github.com/pschanely/hypothesis-crosshair/issues/41
@given(st.integers())
@settings(backend="crosshair")
def test_event_with_realization(value):
event(value)
float(value)
2 changes: 0 additions & 2 deletions hypothesis-python/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,6 @@ allowlist_externals =
bash
setenv=
HYPOTHESIS_PROFILE=crosshair
# remove this ignore when https://github.com/pschanely/hypothesis-crosshair/issues/40 is fixed
PYTEST_ADDOPTS=-W "ignore:hypothesis.internal.observability.TESTCASE_CALLBACKS is deprecated"
commands =
# invoke with `./build.sh check-crosshair-cover -- -x -Wignore`
cover: python -bb -X dev -m pytest -n auto tests/cover/ tests/pytest/ {posargs}
Expand Down
4 changes: 2 additions & 2 deletions requirements/crosshair.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ exceptiongroup==1.2.2 ; python_version < "3.11"
# pytest
execnet==2.1.1
# via pytest-xdist
hypothesis==6.135.14
hypothesis==6.137.0
# via hypothesis-crosshair
hypothesis-crosshair==0.0.24
hypothesis-crosshair==0.0.25
# via -r requirements/crosshair.in
importlib-metadata==8.6.1
# via crosshair-tool
Expand Down
2 changes: 1 addition & 1 deletion tooling/src/hypothesistooling/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ def run_tox(task, version, *args):
"3.12": "3.12.11",
"3.13": "3.13.5",
"3.13t": "3.13t-dev",
"3.14": "3.14.0rc1",
"3.14": "3.14.0rc2",
"3.14t": "3.14t-dev",
"3.15": "3.15-dev",
"3.15t": "3.15t-dev",
Expand Down
Loading