Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions newsfragments/3261.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
``Nursery.start()`` now preserves the ``__cause__`` and ``__context__`` of
exceptions raised before ``task_status.started()`` is called.
4 changes: 2 additions & 2 deletions src/trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .. import _core
from .._abc import Clock, Instrument
from .._deprecate import warn_deprecated
from .._util import NoPublicConstructor, coroutine_or_error, final
from .._util import NoPublicConstructor, coroutine_or_error, final, raise_saving_context
from ._asyncgens import AsyncGenerators
from ._concat_tb import concat_tb
from ._entry_queue import EntryQueue, TrioToken
Expand Down Expand Up @@ -1466,7 +1466,7 @@ async def async_fn(arg1, arg2, *, task_status=trio.TASK_STATUS_IGNORED):
# cancel this nursery:
except BaseExceptionGroup as exc:
if len(exc.exceptions) == 1:
raise exc.exceptions[0] from None
raise_saving_context(exc.exceptions[0])
raise TrioInternalError(
"Internal nursery should not have multiple tasks. This can be "
'caused by the user managing to access the "old" nursery in '
Expand Down
21 changes: 21 additions & 0 deletions src/trio/_core/_tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2920,6 +2920,27 @@ async def start_raiser() -> None:
assert should_be_raiser_exc.exceptions == raiser_exc.exceptions


async def test_start_exception_preserves_cause_and_context() -> None:
"""Regression test for #3261: exceptions raised before task_status.started()
should preserve __cause__ and __context__."""

async def task(*, task_status: _core.TaskStatus[None]) -> None:
e = ValueError("foo")
e.__cause__ = SyntaxError("bar")
e.__context__ = TypeError("baz")
raise e

with pytest.RaisesGroup(
pytest.RaisesExc(
ValueError,
check=lambda exc: isinstance(exc.__cause__, SyntaxError)
and isinstance(exc.__context__, TypeError),
),
):
async with _core.open_nursery() as nursery:
await nursery.start(task)


async def test_internal_error_old_nursery_multiple_tasks() -> None:
async def error_func() -> None:
raise ValueError
Expand Down