Skip to content

Commit

Permalink
change strict_exception_groups default to True
Browse files Browse the repository at this point in the history
  • Loading branch information
jakkdl committed Nov 24, 2023
1 parent 9c496fa commit cfc0755
Show file tree
Hide file tree
Showing 15 changed files with 585 additions and 195 deletions.
6 changes: 3 additions & 3 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -802,9 +802,9 @@ to set the default behavior for any nursery in your program that doesn't overrid
wrapping, so you'll get maximum compatibility with code that was written to
support older versions of Trio.

To maintain backwards compatibility, the default is ``strict_exception_groups=False``.
The default will eventually change to ``True`` in a future version of Trio, once
Python 3.11 and later versions are in wide use.
The default is set to ``strict_exception_groups=True`` in line with the default behaviour
of ``TaskGroup`` in asyncio and anyio. This is also to avoid any bugs caused by only
catching one type of exceptions/exceptiongroups.

.. _exceptiongroup: https://pypi.org/project/exceptiongroup/

Expand Down
2 changes: 2 additions & 0 deletions newsfragments/2786.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
``strict_exception_groups`` now defaults to True in ``trio.run`` and ``trio.start_guest_run``, as well as ``trio.open_nursery`` as a result of that.
This is unfortunately very tricky to change with a deprecation period, as raising a ``DeprecationWarning`` whenever ``strict_exception_groups`` is not specified would raise a lot of unnecessary warnings.
16 changes: 8 additions & 8 deletions src/trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ class NurseryManager:
"""

strict_exception_groups: bool = attr.ib(default=False)
strict_exception_groups: bool = attr.ib(default=True)

@enable_ki_protection
async def __aenter__(self) -> Nursery:
Expand Down Expand Up @@ -985,8 +985,8 @@ def open_nursery(
Args:
strict_exception_groups (bool): If true, even a single raised exception will be
wrapped in an exception group. This will eventually become the default
behavior. If not specified, uses the value passed to :func:`run`.
wrapped in an exception group. If not specified, uses the value passed to
:func:`run`, which defaults to true.
"""
if strict_exception_groups is None:
Expand Down Expand Up @@ -2150,7 +2150,7 @@ def run(
clock: Clock | None = None,
instruments: Sequence[Instrument] = (),
restrict_keyboard_interrupt_to_checkpoints: bool = False,
strict_exception_groups: bool = False,
strict_exception_groups: bool = True,
) -> RetT:
"""Run a Trio-flavored async function, and return the result.
Expand Down Expand Up @@ -2207,9 +2207,9 @@ def run(
main thread (this is a Python limitation), or if you use
:func:`open_signal_receiver` to catch SIGINT.
strict_exception_groups (bool): If true, nurseries will always wrap even a single
raised exception in an exception group. This can be overridden on the level of
individual nurseries. This will eventually become the default behavior.
strict_exception_groups (bool): Unless set to false, nurseries will always wrap
even a single raised exception in an exception group. This can be overridden
on the level of individual nurseries.
Returns:
Whatever ``async_fn`` returns.
Expand Down Expand Up @@ -2267,7 +2267,7 @@ def start_guest_run(
clock: Clock | None = None,
instruments: Sequence[Instrument] = (),
restrict_keyboard_interrupt_to_checkpoints: bool = False,
strict_exception_groups: bool = False,
strict_exception_groups: bool = True,
) -> None:
"""Start a "guest" run of Trio on top of some other "host" event loop.
Expand Down
11 changes: 8 additions & 3 deletions src/trio/_core/_tests/test_ki.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import outcome
import pytest

from trio import testing
from trio.testing import ExpectedExceptionGroup

try:
from async_generator import async_generator, yield_
except ImportError: # pragma: no cover
Expand Down Expand Up @@ -293,7 +296,7 @@ async def check_unprotected_kill() -> None:
nursery.start_soon(sleeper, "s2", record_set)
nursery.start_soon(raiser, "r1", record_set)

with pytest.raises(KeyboardInterrupt):
with testing.raises(ExpectedExceptionGroup(KeyboardInterrupt)):
_core.run(check_unprotected_kill)
assert record_set == {"s1 ok", "s2 ok", "r1 raise ok"}

Expand All @@ -309,7 +312,7 @@ async def check_protected_kill() -> None:
nursery.start_soon(_core.enable_ki_protection(raiser), "r1", record_set)
# __aexit__ blocks, and then receives the KI

with pytest.raises(KeyboardInterrupt):
with testing.raises(ExpectedExceptionGroup(KeyboardInterrupt)):
_core.run(check_protected_kill)
assert record_set == {"s1 ok", "s2 ok", "r1 cancel ok"}

Expand All @@ -331,7 +334,8 @@ def kill_during_shutdown() -> None:

token.run_sync_soon(kill_during_shutdown)

with pytest.raises(KeyboardInterrupt):
# Does not wrap in an ExceptionGroup(!!)
with testing.raises(KeyboardInterrupt):
_core.run(check_kill_during_shutdown)

# KI arrives very early, before main is even spawned
Expand All @@ -344,6 +348,7 @@ def before_run(self) -> None:
async def main_1() -> None:
await _core.checkpoint()

# Does not wrap in an ExceptionGroup(!!)
with pytest.raises(KeyboardInterrupt):
_core.run(main_1, instruments=[InstrumentOfDeath()])

Expand Down
2 changes: 1 addition & 1 deletion src/trio/_core/_tests/test_multierror.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ async def test_MultiErrorNotHashable() -> None:
assert exc1 != exc2
assert exc1 != exc3

with pytest.raises(MultiError):
with pytest.raises(ExceptionGroup):
async with open_nursery() as nursery:
nursery.start_soon(raise_nothashable, 42)
nursery.start_soon(raise_nothashable, 4242)
Expand Down
Loading

0 comments on commit cfc0755

Please sign in to comment.