BUG: `AttributeError` raised on a simple test case with hypothesis>=6.145 + Python 3.10.0
envgap__HypothesisWorks__hypothesis-4602
01 / FAILURE SIGNATURE
As reported upstream
E AttributeError: 'TreeNode' object has no attribute 'is_exhausted'. Did you mean: 'check_exhausted'?
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
584c6b9cd2744fbd705e1900557ad952677dc15d- Manifest
hypothesis-python/pyproject.toml- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / ORIGINAL ISSUE TEXT
HypothesisWorks/hypothesis #4602 · read the original issue
Caught this while upgrading from version 6.144.0 (https://github.com/neutrinoceros/inifix/pull/470)
bisected to https://github.com/HypothesisWorks/hypothesis/commit/ce984d05c61c51b469e919b0b16276be8e291b52 (https://github.com/HypothesisWorks/hypothesis/pull/4577)
note that using Python 3.10.0 is crucial to the reproducer. The bug isn't present on 3.10.2 (but I couldn't easily check 3.10.1)
reproducer
```py
from hypothesis import given
from hypothesis import strategies as st
@given(st.integers())
def test_foo(i):
...
```
<details><summary> detailed output from pytest </summary>
```
======================================== test session starts =========================================
platform darwin -- Python 3.10.0, pytest-9.0.1, pluggy-1.6.0
rootdir: /Users/clm/dev/orgs/me/inifix
configfile: pyproject.toml
plugins: hypothesis-6.148.1
collected 1 item
t.py F [100%]
============================================== FAILURES ==============================================
______________________________________________ test_foo ______________________________________________
@given(st.integers())
> def test_foo(i):
t.py:5:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/engine.py:933: in run
self._run()
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/engine.py:1503: in _run
self.generate_new_examples()
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/engine.py:1109: in generate_new_examples
zero_data = self.cached_test_function((ChoiceTemplate("simplest", count=None),))
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/engine.py:514: in cached_test_function
self.test_function(data)
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/engine.py:565: in test_function
data.freeze()
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/data.py:1309: in freeze
self.observer.conclude_test(self.status, self.interesting_origin)
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/datatree.py:1175: in conclude_test
node.check_exhausted()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <[AttributeError("'TreeNode' object has no attribute '_TreeNode__forced'") raised in repr()] TreeNode object at 0x10a620ae0>
def check_exhausted(self) -> bool:
"""
Recalculates is_exhausted if necessary, and then returns it.
A node is exhausted if:
- Its transition is Conclusion or Killed
- It has the maximum number of children (i.e. we have found all of its
possible children), and all its children are exhausted
Therefore, we only need to compute this for a node when:
- We first create it in split_at
- We set its transition to either Conclusion or Killed
(TreeRecordingObserver.conclude_test or TreeRecordingObserver.kill_branch)
- We exhaust any of its children
"""
if (
# a node cannot go from is_exhausted -> not is_exhausted.
> not self.is_exhausted
# if we don't know what happens after this node, we don't have
# enough information to tell if it's exhausted.
and self.transition is not None
# if there are still any nodes left which are the only child of their
# parent (len(self.values) > 0), then this TreeNode must be not
# exhausted, unless all of those nodes were forced.
#
# This is because we maintain an invariant of only adding nodes to
# DataTree which have at least 2 possible values, so we know that if
# they do not have any siblings that we still have more choices to
# discover.
#
# (We actually *do* currently add single-valued nodes to the tree,
# but immediately split them into a transition to avoid falsifying
# this check. this is a bit of a hack.)
and len(self.forced) == len(self.values)
):
E AttributeError: 'TreeNode' object has no attribute 'is_exhausted'. Did you mean: 'check_exhausted'?
.venv/lib/python3.10/site-packages/hypothesis/internal/conjecture/datatree.py:498: AttributeError
--------------------------------------------- Hypothesis ---------------------------------------------
You can add @seed(277356604012138926287998509023427006162) to this test or run pytest with --hypothesis-seed=277356604012138926287998509023427006162 to reproduce this failure.
====================================== short test summary info =======================================
FAILED t.py::test_foo - AttributeError: 'TreeNode' object has no attribute 'is_exhausted'. Did you mean: 'check_exhausted'?
========================================= 1 failed in 1.10s ==========================================
```
</details>04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]