Skip to content

Commit 2e688b1

Browse files
committed
respect forced for ir tree nodes
1 parent fc9c009 commit 2e688b1

File tree

2 files changed

+35
-60
lines changed

2 files changed

+35
-60
lines changed

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

Lines changed: 26 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,10 +1909,11 @@ def draw_integer(
19091909
)
19101910

19111911
if self.ir_tree_nodes is not None and observe:
1912-
node = self._pop_ir_tree_node("integer", kwargs, forced=forced)
1913-
assert isinstance(node.value, int)
1914-
forced = node.value
1915-
fake_forced = not node.was_forced
1912+
node = self._pop_ir_tree_node("integer", kwargs)
1913+
if forced is None:
1914+
assert isinstance(node.value, int)
1915+
forced = node.value
1916+
fake_forced = True
19161917

19171918
value = self.provider.draw_integer(
19181919
**kwargs, forced=forced, fake_forced=fake_forced
@@ -1965,10 +1966,11 @@ def draw_float(
19651966
)
19661967

19671968
if self.ir_tree_nodes is not None and observe:
1968-
node = self._pop_ir_tree_node("float", kwargs, forced=forced)
1969-
assert isinstance(node.value, float)
1970-
forced = node.value
1971-
fake_forced = not node.was_forced
1969+
node = self._pop_ir_tree_node("float", kwargs)
1970+
if forced is None:
1971+
assert isinstance(node.value, float)
1972+
forced = node.value
1973+
fake_forced = True
19721974

19731975
value = self.provider.draw_float(
19741976
**kwargs, forced=forced, fake_forced=fake_forced
@@ -2006,10 +2008,11 @@ def draw_string(
20062008
},
20072009
)
20082010
if self.ir_tree_nodes is not None and observe:
2009-
node = self._pop_ir_tree_node("string", kwargs, forced=forced)
2010-
assert isinstance(node.value, str)
2011-
forced = node.value
2012-
fake_forced = not node.was_forced
2011+
node = self._pop_ir_tree_node("string", kwargs)
2012+
if forced is None:
2013+
assert isinstance(node.value, str)
2014+
forced = node.value
2015+
fake_forced = True
20132016

20142017
value = self.provider.draw_string(
20152018
**kwargs, forced=forced, fake_forced=fake_forced
@@ -2041,10 +2044,11 @@ def draw_bytes(
20412044
kwargs: BytesKWargs = self._pooled_kwargs("bytes", {"size": size})
20422045

20432046
if self.ir_tree_nodes is not None and observe:
2044-
node = self._pop_ir_tree_node("bytes", kwargs, forced=forced)
2045-
assert isinstance(node.value, bytes)
2046-
forced = node.value
2047-
fake_forced = not node.was_forced
2047+
node = self._pop_ir_tree_node("bytes", kwargs)
2048+
if forced is None:
2049+
assert isinstance(node.value, bytes)
2050+
forced = node.value
2051+
fake_forced = True
20482052

20492053
value = self.provider.draw_bytes(
20502054
**kwargs, forced=forced, fake_forced=fake_forced
@@ -2082,10 +2086,11 @@ def draw_boolean(
20822086
kwargs: BooleanKWargs = self._pooled_kwargs("boolean", {"p": p})
20832087

20842088
if self.ir_tree_nodes is not None and observe:
2085-
node = self._pop_ir_tree_node("boolean", kwargs, forced=forced)
2086-
assert isinstance(node.value, bool)
2087-
forced = node.value
2088-
fake_forced = not node.was_forced
2089+
node = self._pop_ir_tree_node("boolean", kwargs)
2090+
if forced is None:
2091+
assert isinstance(node.value, bool)
2092+
forced = node.value
2093+
fake_forced = True
20892094

20902095
value = self.provider.draw_boolean(
20912096
**kwargs, forced=forced, fake_forced=fake_forced
@@ -2122,9 +2127,7 @@ def _pooled_kwargs(self, ir_type, kwargs):
21222127
POOLED_KWARGS_CACHE[key] = kwargs
21232128
return kwargs
21242129

2125-
def _pop_ir_tree_node(
2126-
self, ir_type: IRTypeName, kwargs: IRKWargsType, *, forced: Optional[IRType]
2127-
) -> IRNode:
2130+
def _pop_ir_tree_node(self, ir_type: IRTypeName, kwargs: IRKWargsType) -> IRNode:
21282131
assert self.ir_tree_nodes is not None
21292132

21302133
if self.ir_tree_nodes == []:
@@ -2151,32 +2154,6 @@ def _pop_ir_tree_node(
21512154
if not ir_value_permitted(node.value, node.ir_type, kwargs):
21522155
self.mark_invalid() # pragma: no cover # FIXME @tybug
21532156

2154-
if forced is not None:
2155-
# if we expected a forced node but are instead returning a non-forced
2156-
# node, something has gone terribly wrong. If we allowed this combination,
2157-
# we risk violating core invariants that rely on forced draws being,
2158-
# well, forced to a particular value.
2159-
#
2160-
# In particular, this can manifest while shrinking. Consider the tree
2161-
# [boolean True [forced] {"p": 0.5}]
2162-
# [boolean False {"p": 0.5}]
2163-
#
2164-
# and the shrinker tries to reorder these to
2165-
# [boolean False {"p": 0.5}]
2166-
# [boolean True [forced] {"p": 0.5}].
2167-
#
2168-
# However, maybe we got lucky and the non-forced node is returning
2169-
# the same value that was expected from the forced draw. We lucked
2170-
# into an aligned tree in this case and can let it slide.
2171-
if not node.was_forced and not ir_value_equal(ir_type, forced, node.value):
2172-
self.mark_invalid()
2173-
2174-
# similarly, if we expected a forced node with a certain value, and
2175-
# are returning a forced node with a different value, this is an
2176-
# equally bad misalignment.
2177-
if node.was_forced and not ir_value_equal(ir_type, forced, node.value):
2178-
self.mark_invalid()
2179-
21802157
return node
21812158

21822159
def as_result(self) -> Union[ConjectureResult, _Overrun]:

hypothesis-python/tests/conjecture/test_ir.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ def test_data_with_misaligned_ir_tree_is_invalid(data):
386386

387387

388388
@given(st.data())
389-
def test_data_with_changed_was_forced_is_invalid(data):
389+
def test_data_with_changed_was_forced(data):
390390
# we had a normal node and then tried to draw a different forced value from it.
391391
# ir tree: v1 [was_forced=False]
392392
# drawing: [forced=v2]
@@ -398,18 +398,19 @@ def test_data_with_changed_was_forced_is_invalid(data):
398398
kwargs["forced"] = draw_value(node.ir_type, node.kwargs)
399399
assume(not ir_value_equal(node.ir_type, kwargs["forced"], node.value))
400400

401-
with pytest.raises(StopTest):
402-
draw_func(**kwargs)
403-
404-
assert data.status is Status.INVALID
401+
assert ir_value_equal(node.ir_type, draw_func(**kwargs), kwargs["forced"])
405402

406403

407404
@given(st.data())
408405
@settings(suppress_health_check=[HealthCheck.too_slow])
409-
def test_data_with_changed_forced_value_is_invalid(data):
406+
def test_data_with_changed_forced_value(data):
410407
# we had a forced node and then tried to draw a different forced value from it.
411408
# ir tree: v1 [was_forced=True]
412409
# drawing: [forced=v2]
410+
#
411+
# This is actually fine; we'll just ignore the forced node (v1) and return
412+
# what the draw expects (v2).
413+
413414
node = data.draw(ir_nodes(was_forced=True))
414415
data = ConjectureData.for_ir_tree([node])
415416

@@ -418,10 +419,7 @@ def test_data_with_changed_forced_value_is_invalid(data):
418419
kwargs["forced"] = draw_value(node.ir_type, node.kwargs)
419420
assume(not ir_value_equal(node.ir_type, kwargs["forced"], node.value))
420421

421-
with pytest.raises(StopTest):
422-
draw_func(**kwargs)
423-
424-
assert data.status is Status.INVALID
422+
assert ir_value_equal(node.ir_type, draw_func(**kwargs), kwargs["forced"])
425423

426424

427425
@given(st.data())
@@ -436,7 +434,7 @@ def test_data_with_same_forced_value_is_valid(data):
436434

437435
kwargs = deepcopy(node.kwargs)
438436
kwargs["forced"] = node.value
439-
draw_func(**kwargs)
437+
assert ir_value_equal(node.ir_type, draw_func(**kwargs), kwargs["forced"])
440438

441439

442440
@given(ir_types_and_kwargs())

0 commit comments

Comments
 (0)