Skip to content

Commit 030ea39

Browse files
committed
Use label only, modify label to discriminate by args
1 parent bb9a9bf commit 030ea39

File tree

6 files changed

+63
-41
lines changed

6 files changed

+63
-41
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
from collections import OrderedDict, abc
1717
from collections.abc import Sequence
1818
from functools import lru_cache
19-
from typing import TYPE_CHECKING, Optional, TypeVar, Union
19+
from types import FunctionType
20+
from typing import TYPE_CHECKING, Callable, Optional, TypeVar, Union
2021

2122
from hypothesis.errors import InvalidArgument
2223
from hypothesis.internal.compat import int_from_bytes
2324
from hypothesis.internal.floats import next_up
25+
from hypothesis.internal.lambda_sources import _function_key
2426

2527
if TYPE_CHECKING:
2628
from hypothesis.internal.conjecture.data import ConjectureData
@@ -34,6 +36,20 @@ def calc_label_from_name(name: str) -> int:
3436
return int_from_bytes(hashed[:8])
3537

3638

39+
def calc_label_from_callable(f: Callable) -> int:
40+
if isinstance(f, FunctionType):
41+
return calc_label_from_hash(_function_key(f, ignore_name=True))
42+
elif isinstance(f, type):
43+
return calc_label_from_cls(f)
44+
else:
45+
# probably an instance defining __call__
46+
try:
47+
return calc_label_from_hash(f)
48+
except Exception:
49+
# not hashable
50+
return calc_label_from_cls(type(f))
51+
52+
3753
def calc_label_from_cls(cls: type) -> int:
3854
return calc_label_from_name(cls.__qualname__)
3955

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def visit_Attribute(self, node):
6565
return attributes
6666

6767

68-
def _function_key(f, *, bounded_size=False):
68+
def _function_key(f, *, bounded_size=False, ignore_name=False):
6969
"""Returns a digest that differentiates functions that have different sources.
7070
7171
Either a function or a code object may be passed. If code object, default
@@ -96,7 +96,7 @@ def _function_key(f, *, bounded_size=False):
9696
code.co_names,
9797
code.co_varnames,
9898
code.co_freevars,
99-
code.co_name,
99+
ignore_name or code.co_name,
100100
)
101101

102102

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@
8282
)
8383
from hypothesis.internal.conjecture.data import ConjectureData
8484
from hypothesis.internal.conjecture.utils import (
85+
calc_label_from_callable,
8586
calc_label_from_cls,
87+
calc_label_from_hash,
8688
check_sample,
89+
combine_labels,
8790
identity,
8891
)
8992
from hypothesis.internal.entropy import get_seeder_and_restorer
@@ -1053,6 +1056,15 @@ def __init__(
10531056
self.args = args
10541057
self.kwargs = kwargs
10551058

1059+
def calc_label(self) -> int:
1060+
return combine_labels(
1061+
self.class_label,
1062+
calc_label_from_callable(self.target),
1063+
*[strat.label for strat in self.args],
1064+
*[calc_label_from_hash(k) for k in self.kwargs.keys()],
1065+
*[strat.label for strat in self.kwargs.values()],
1066+
)
1067+
10561068
def do_draw(self, data: ConjectureData) -> Ex:
10571069
args = [data.draw(s) for s in self.args]
10581070
kwargs = {k: data.draw(v) for k, v in self.kwargs.items()}
@@ -1875,7 +1887,7 @@ def do_draw(self, data):
18751887
return self.definition(data.draw, *self.args, **self.kwargs)
18761888

18771889
def calc_label(self) -> int:
1878-
return calc_label_from_cls(self.definition)
1890+
return calc_label_from_callable(self.definition)
18791891

18801892

18811893
class DrawFn(Protocol):

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from hypothesis.control import reject
1717
from hypothesis.errors import InvalidArgument
18+
from hypothesis.internal.conjecture.utils import calc_label_from_hash, combine_labels
1819
from hypothesis.internal.conjecture.data import ConjectureData
1920
from hypothesis.internal.filtering import (
2021
get_float_predicate_bounds,
@@ -66,6 +67,13 @@ def __repr__(self) -> str:
6667
return f"integers(max_value={self.end})"
6768
return f"integers({self.start}, {self.end})"
6869

70+
def calc_label(self) -> int:
71+
return combine_labels(
72+
self.class_label,
73+
calc_label_from_hash(self.start),
74+
calc_label_from_hash(self.end),
75+
)
76+
6977
def do_draw(self, data: ConjectureData) -> int:
7078
# For bounded integers, make the bounds and near-bounds more likely.
7179
weights = None
@@ -181,6 +189,15 @@ def __repr__(self) -> str:
181189
f"{self.allow_nan=}, {self.smallest_nonzero_magnitude=})"
182190
).replace("self.", "")
183191

192+
def calc_label(self) -> int:
193+
return combine_labels(
194+
self.class_label,
195+
calc_label_from_hash(self.min_value),
196+
calc_label_from_hash(self.max_value),
197+
calc_label_from_hash(self.allow_nan),
198+
calc_label_from_hash(self.smallest_nonzero_magnitude),
199+
)
200+
184201
def do_draw(self, data: ConjectureData) -> float:
185202
return data.draw_float(
186203
min_value=self.min_value,

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

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,14 @@ def do_draw(self, data: ConjectureData) -> Any:
4343
else:
4444
drawn, other = data._shared_strategy_draws[key]
4545

46-
if other.base is not self.base:
47-
# Check that the strategies shared under this key are equivalent,
48-
# approximated as having equal `repr`s. False positives are ok,
49-
# false negatives (erroneous warnings) less so.
50-
if not hasattr(self, "_is_compatible"):
51-
self._is_compatible = repr(self.base) == repr(other.base)
52-
if not self._is_compatible:
53-
warnings.warn(
54-
f"Different strategies are shared under {key=}. This"
55-
" risks drawing values that are not valid examples for the strategy,"
56-
" or that have a narrower range than expected."
57-
f" Conflicting strategies: ({self.base!r}, {other.base!r}).",
58-
HypothesisWarning,
59-
stacklevel=1,
60-
)
46+
# Check that the strategies shared under this key are equivalent
47+
if self.base.label != other.base.label:
48+
warnings.warn(
49+
f"Different strategies are shared under {key=}. This"
50+
" risks drawing values that are not valid examples for the strategy,"
51+
" or that have a narrower range than expected."
52+
f" Conflicting strategies: ({self.base!r}, {other.base!r}).",
53+
HypothesisWarning,
54+
stacklevel=1,
55+
)
6156
return drawn

hypothesis-python/tests/cover/test_direct_strategies.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -592,16 +592,11 @@ def test_builds_error_messages(data):
592592
(st.integers(), st.integers(0)),
593593
(st.builds(int), st.builds(float)),
594594
(st.none(), st.integers()),
595-
pytest.param(
595+
(
596596
st.composite(lambda draw: draw(st.none()))(),
597597
st.composite(lambda draw: draw(st.integers()))(),
598-
marks=pytest.mark.xfail(
599-
# https://github.com/pytest-dev/pytest/issues/8928
600-
raises=pytest.fail.Exception,
601-
strict=True,
602-
reason="same-name incompatible @composite",
603-
),
604598
),
599+
(st.builds(int, st.integers()), st.builds(int, st.integers(0))),
605600
],
606601
)
607602
def test_incompatible_shared_strategies_warns(strat_a, strat_b):
@@ -633,24 +628,11 @@ def _composite2(draw):
633628
(st.floats(allow_nan=False), st.floats(allow_nan=False)),
634629
(st.builds(float), st.builds(float)),
635630
(_composite1(), _composite1()),
636-
pytest.param(
631+
(
637632
st.floats(allow_nan=False, allow_infinity=False),
638633
st.floats(allow_nan=False, allow_infinity=0),
639-
marks=pytest.mark.xfail(
640-
raises=HypothesisWarning,
641-
strict=True,
642-
reason="un-normalized constraint value (issue #4417)",
643-
),
644-
),
645-
pytest.param(
646-
_composite1(),
647-
_composite2(),
648-
marks=pytest.mark.xfail(
649-
raises=HypothesisWarning,
650-
strict=True,
651-
reason="differently named @composites",
652-
),
653634
),
635+
(_composite1(), _composite2()),
654636
pytest.param(
655637
st.integers().flatmap(st.just),
656638
st.integers(),

0 commit comments

Comments
 (0)