pytest-dev / pytest-dev/pytest
__tracebackhide__ can retain hidden frames sharing a source location in ExceptionGroup tracebacks
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 14.5k
- Forks
- 3.4k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 35
Description
Description
When two traceback frames come from the same filename and line number, pytest can keep a frame marked with __tracebackhide__ = True in an ExceptionGroup traceback.
This can happen whenever distinct traceback entries share the same source location but have different __tracebackhide__ values. Recursion provides a simple reproducer.
Minimal example
def fail(number):
__tracebackhide__ = number == 1
if number == 0:
raise ValueError("boom")
fail(number - 1)
def test_failure():
try:
fail(2)
except ValueError as error:
raise ExceptionGroup("failure", [error]) from None
Run:
pytest -q test_example.py
Actual result
The relevant part of the nested traceback contains the recursive line twice:
File "test_example.py", line 7, in fail
fail(number - 1)
File "test_example.py", line 7, in fail
fail(number - 1)
File "test_example.py", line 5, in fail
raise ValueError("boom")
ValueError: boom
Expected result
The calls have different __tracebackhide__ values:
fail(2) -> visible
fail(1) -> hidden
fail(0) -> raises ValueError
The fail(1) frame should be removed, so fail(number - 1) should appear only once. The equivalent traceback without the ExceptionGroup wrapper already behaves correctly.
Cause
_filter_tracebackexception() currently identifies filtered frames using (filename, line number). These values identify a source location, but not an individual occurrence in a traceback. A visible frame can therefore also retain a hidden frame from the same location.
This matching was added in #14649 for #14036. This report covers the remaining case where separate traceback frames share one source location.
Environment
- pytest
mainat28549a5f6b82bc916bb2ec5cb9fbfffe9b79fc66 - Python 3.12.13
- Red Hat Enterprise Linux 9.8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating _filter_tracebackexception() and reproduce the issue with the test_example.py example using pytest -q test_example.py. Trace how filtered frames are matched in ExceptionGroup tracebacks, then add coverage showing that the hidden recursive frame is removed while the visible frame at the same source location remains; the existing non-ExceptionGroup behavior should continue to pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- testing
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100