Skip to content

Commit a90c96f

Browse files
authored
Merge pull request #4049 from Zac-HD/more-crosshair-compat
More crosshair compatibility
2 parents dd17cff + 476a7ce commit a90c96f

File tree

9 files changed

+72
-79
lines changed

9 files changed

+72
-79
lines changed

.github/workflows/main.yml

+14-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,12 @@ jobs:
8383
- check-pandas13
8484
- check-py39-pandas12
8585
- check-py39-pandas11
86-
# - check-crosshair-cover
86+
## `-cover` is too slow under crosshair; use a custom split
87+
# - check-crosshair-custom-cover/test_[a-d]*
88+
# - check-crosshair-custom-cover/test_[e-i]*
89+
# - check-crosshair-custom-cover/test_[j-r]*
90+
# - check-crosshair-custom-cover/test_[s-z]*
91+
# - check-crosshair-custom-pytest/test_*
8792
# - check-crosshair-nocover
8893
# - check-crosshair-niche
8994
- check-py38-oldestnumpy
@@ -121,7 +126,14 @@ jobs:
121126
sudo apt-get update && \
122127
sudo apt-get install -y dotnet-sdk-6.0
123128
- name: Run tests
124-
run: TASK=${{ matrix.task }} ./build.sh
129+
run: |
130+
export TASK=${{ matrix.task }}
131+
if [[ $TASK == check-crosshair-custom-* ]]; then
132+
GROUP="${TASK#check-crosshair-custom-}"
133+
./build.sh check-crosshair-custom -- -n auto $(cd hypothesis-python ; echo tests/$GROUP)
134+
else
135+
./build.sh
136+
fi
125137
- name: Upload coverage data
126138
uses: actions/upload-artifact@v2
127139
# Invoke the magic `always` function to run on both success and failure.

hypothesis-python/RELEASE.rst

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch disables :func:`hypothesis.target` on alternative
4+
backends where it would not work.

hypothesis-python/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def local_file(name):
6060
"pytest": ["pytest>=4.6"],
6161
"dpcontracts": ["dpcontracts>=0.4"],
6262
"redis": ["redis>=3.0.0"],
63-
"crosshair": ["hypothesis-crosshair>=0.0.7", "crosshair-tool>=0.0.60"],
63+
"crosshair": ["hypothesis-crosshair>=0.0.7", "crosshair-tool>=0.0.61"],
6464
# zoneinfo is an odd one: every dependency is conditional, because they're
6565
# only necessary on old versions of Python or Windows systems or emscripten.
6666
"zoneinfo": [

hypothesis-python/src/hypothesis/control.py

+5
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,11 @@ def target(observation: Union[int, float], *, label: str = "") -> Union[int, flo
297297
"Calling target() outside of a test is invalid. "
298298
"Consider guarding this call with `if currently_in_test_context(): ...`"
299299
)
300+
elif context.data.provider.avoid_realization:
301+
# We could in principle realize this in the engine, but it seems more
302+
# efficient to have our alternative backend optimize it for us.
303+
# See e.g. https://github.com/pschanely/hypothesis-crosshair/issues/3
304+
return observation # pragma: no cover
300305
verbose_report(f"Saw target({observation!r}, {label=})")
301306

302307
if label in context.data.target_observations:

hypothesis-python/src/hypothesis/internal/conjecture/data.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -2273,10 +2273,9 @@ def draw_boolean(
22732273
#
22742274
# Note that even if we lift this 64 bit restriction in the future, p
22752275
# cannot be 0 (1) when forced is True (False).
2276-
if forced is True:
2277-
assert p > 2 ** (-64)
2278-
if forced is False:
2279-
assert p < (1 - 2 ** (-64))
2276+
eps = 2 ** (-64) if isinstance(self.provider, HypothesisProvider) else 0
2277+
assert (forced is not True) or (0 + eps) < p
2278+
assert (forced is not False) or p < (1 - eps)
22802279

22812280
kwargs: BooleanKWargs = self._pooled_kwargs("boolean", {"p": p})
22822281

hypothesis-python/tests/cover/test_type_lookup.py

+44-41
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@
1717
import pytest
1818

1919
from hypothesis import given, infer, settings, strategies as st
20-
from hypothesis.errors import (
21-
HypothesisDeprecationWarning,
22-
InvalidArgument,
23-
ResolutionFailed,
24-
)
20+
from hypothesis.errors import InvalidArgument, ResolutionFailed
2521
from hypothesis.internal.compat import get_type_hints
2622
from hypothesis.internal.reflection import get_pretty_function_description
2723
from hypothesis.strategies._internal import types
24+
from hypothesis.strategies._internal.lazy import LazyStrategy
2825
from hypothesis.strategies._internal.types import _global_type_lookup
2926
from hypothesis.strategies._internal.utils import _strategies
3027

@@ -36,42 +33,11 @@
3633
)
3734
from tests.common.utils import fails_with, temp_registered
3835

39-
# Build a set of all types output by core strategies
40-
blocklist = {
41-
"builds",
42-
"data",
43-
"deferred",
44-
"from_regex",
45-
"from_type",
46-
"ip_addresses",
47-
"iterables",
48-
"just",
49-
"nothing",
50-
"one_of",
51-
"permutations",
52-
"random_module",
53-
"randoms",
54-
"recursive",
55-
"runner",
56-
"sampled_from",
57-
"shared",
58-
"timezone_keys",
59-
"timezones",
36+
types_with_core_strat = {
37+
type_
38+
for type_, strat in _global_type_lookup.items()
39+
if isinstance(strat, LazyStrategy) and strat.function in vars(st).values()
6040
}
61-
assert set(_strategies).issuperset(blocklist), blocklist.difference(_strategies)
62-
types_with_core_strat = set()
63-
for thing in (
64-
getattr(st, name)
65-
for name in sorted(_strategies)
66-
if name in dir(st) and name not in blocklist
67-
):
68-
for n in range(3):
69-
try:
70-
ex = find_any(thing(*([st.nothing()] * n)))
71-
types_with_core_strat.add(type(ex))
72-
break
73-
except (TypeError, InvalidArgument, HypothesisDeprecationWarning):
74-
continue
7541

7642

7743
@pytest.mark.skipif(sys.version_info[:2] >= (3, 14), reason="FIXME-py314")
@@ -91,7 +57,44 @@ def inner(ex):
9157

9258

9359
def test_lookup_knows_about_all_core_strategies():
94-
cannot_lookup = types_with_core_strat - set(types._global_type_lookup)
60+
# Build a set of all types output by core strategies
61+
blocklist = {
62+
"builds",
63+
"data",
64+
"deferred",
65+
"from_regex",
66+
"from_type",
67+
"ip_addresses",
68+
"iterables",
69+
"just",
70+
"nothing",
71+
"one_of",
72+
"permutations",
73+
"random_module",
74+
"randoms",
75+
"recursive",
76+
"runner",
77+
"sampled_from",
78+
"shared",
79+
"timezone_keys",
80+
"timezones",
81+
}
82+
assert set(_strategies).issuperset(blocklist), blocklist.difference(_strategies)
83+
found = set()
84+
for thing in (
85+
getattr(st, name)
86+
for name in sorted(_strategies)
87+
if name in dir(st) and name not in blocklist
88+
):
89+
for n in range(3):
90+
try:
91+
ex = find_any(thing(*([st.nothing()] * n)))
92+
found.add(type(ex))
93+
break
94+
except Exception:
95+
continue
96+
97+
cannot_lookup = found - set(types._global_type_lookup)
9598
assert not cannot_lookup
9699

97100

hypothesis-python/tests/nocover/test_collective_minimization.py

-6
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,11 @@
1616

1717
from tests.common import standard_types
1818
from tests.common.debug import minimal
19-
from tests.common.utils import flaky
2019

2120

2221
@pytest.mark.parametrize("spec", standard_types, ids=list(map(repr, standard_types)))
23-
@flaky(min_passes=1, max_runs=2)
2422
def test_can_collectively_minimize(spec):
25-
"""This should generally exercise strategies' strictly_simpler heuristic by
26-
putting us in a state where example cloning is required to get to the
27-
answer fast enough."""
2823
n = 10
29-
3024
try:
3125
xs = minimal(
3226
lists(spec, min_size=n, max_size=n),

hypothesis-python/tests/nocover/test_conjecture_engine.py

-24
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from hypothesis import given, settings, strategies as st
1212
from hypothesis.database import InMemoryExampleDatabase
13-
from hypothesis.internal.compat import int_from_bytes
1413
from hypothesis.internal.conjecture.data import ConjectureData
1514
from hypothesis.internal.conjecture.engine import ConjectureRunner
1615
from hypothesis.internal.conjecture.shrinker import Shrinker, node_program
@@ -80,29 +79,6 @@ def x(data):
8079
assert len(x) == n
8180

8281

83-
def test_regression_1():
84-
# This is a really hard to reproduce bug that previously triggered a very
85-
# specific exception inside one of the shrink passes. It's unclear how
86-
# useful this regression test really is, but nothing else caught the
87-
# problem.
88-
#
89-
# update 2024-01-15: we've since changed generation and are about to
90-
# change shrinking, so it's unclear if the failure case this test was aimed
91-
# at (1) is still being covered or (2) even exists anymore.
92-
# we can probably safely remove this once the shrinker is rebuilt.
93-
@run_to_buffer
94-
def x(data):
95-
data.draw_bytes(2, forced=b"\x01\x02")
96-
data.draw_bytes(2, forced=b"\x01\x00")
97-
v = data.draw_integer(0, 2**41 - 1)
98-
if v >= 512 or v == 254:
99-
data.mark_interesting()
100-
101-
assert list(x)[:-2] == [1, 2, 1, 0, 0, 0, 0, 0, 0]
102-
103-
assert int_from_bytes(x[-2:]) in (254, 512)
104-
105-
10682
@given(st.integers(0, 255), st.integers(0, 255))
10783
def test_cached_with_masked_byte_agrees_with_results(byte_a, byte_b):
10884
def f(data):

tooling/src/hypothesistooling/__main__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ def standard_tox_task(name, py=ci_version):
520520
standard_tox_task("py39-pandas11", py="3.9")
521521
standard_tox_task("py39-pandas12", py="3.9")
522522

523-
for kind in ("cover", "nocover", "niche"):
523+
for kind in ("cover", "nocover", "niche", "custom"):
524524
standard_tox_task(f"crosshair-{kind}")
525525

526526
standard_tox_task("py38-oldestnumpy", py="3.8")

0 commit comments

Comments
 (0)