88# v. 2.0. If a copy of the MPL was not distributed with this file, You can
99# obtain one at https://mozilla.org/MPL/2.0/.
1010
11+ import warnings
1112from collections .abc import Hashable
1213from typing import Any , Optional
14+ from weakref import WeakKeyDictionary
1315
16+ from hypothesis .errors import HypothesisWarning
1417from hypothesis .internal .conjecture .data import ConjectureData
1518from hypothesis .strategies ._internal import SearchStrategy
1619from hypothesis .strategies ._internal .strategies import Ex
@@ -21,6 +24,9 @@ def __init__(self, base: SearchStrategy[Ex], key: Optional[Hashable] = None):
2124 super ().__init__ ()
2225 self .key = key
2326 self .base = base
27+ while isinstance (self .base , SharedStrategy ):
28+ # Unwrap nested shares
29+ self .base = self .base .base
2430
2531 def __repr__ (self ) -> str :
2632 if self .key is not None :
@@ -31,27 +37,30 @@ def __repr__(self) -> str:
3137 # Ideally would be -> Ex, but key collisions with different-typed values are
3238 # possible. See https://github.com/HypothesisWorks/hypothesis/issues/4301.
3339 def do_draw (self , data : ConjectureData ) -> Any :
34- if self .key is None or getattr (self .base , "_is_singleton" , False ):
35- strat_label = id (self .base )
36- else :
37- # Assume that uncached strategies are distinguishable by their
38- # label. False negatives (even collisions w/id above) are ok as
39- # long as they are infrequent.
40- strat_label = self .base .label
4140 key = self .key or self
4241 if key not in data ._shared_strategy_draws :
4342 drawn = data .draw (self .base )
44- data ._shared_strategy_draws [key ] = (strat_label , drawn )
43+ data ._shared_strategy_draws [key ] = (drawn , self )
4544 else :
46- drawn_strat_label , drawn = data ._shared_strategy_draws [key ]
47- # Check disabled pending resolution of #4301
48- if drawn_strat_label != strat_label : # pragma: no cover
49- pass
50- # warnings.warn(
51- # f"Different strategies are shared under {key=}. This"
52- # " risks drawing values that are not valid examples for the strategy,"
53- # " or that have a narrower range than expected.",
54- # HypothesisWarning,
55- # stacklevel=1,
56- # )
45+ drawn , other = data ._shared_strategy_draws [key ]
46+
47+ if other .base is not self .base :
48+ # Check that the strategies shared under this key are equivalent,
49+ # approximated as having equal `repr`s.
50+ try :
51+ is_equivalent = self ._equivalent_to [other ]
52+ except (AttributeError , KeyError ) as e :
53+ if isinstance (e , AttributeError ):
54+ self ._equivalent_to = WeakKeyDictionary ()
55+ is_equivalent = repr (self .base ) == repr (other .base )
56+ self ._equivalent_to [other ] = is_equivalent
57+ if not is_equivalent :
58+ warnings .warn (
59+ f"Different strategies are shared under { key = } . This"
60+ " risks drawing values that are not valid examples for the strategy,"
61+ " or that have a narrower range than expected."
62+ f" Conflicting strategies: ({ self .base !r} , { other .base !r} )." ,
63+ HypothesisWarning ,
64+ stacklevel = 1 ,
65+ )
5766 return drawn
0 commit comments