pytest: Allow pytest.raises cooperate with ExceptionGroups
What’s the problem this feature will solve?
Let pytest.raises() handle ExceptionGroup unwrapping, similar to what an “except*” clause would do. The following is valid python code that will catch the ValueError exception:
try:
raise ExceptionGroup("", [ValueError])
except* ValueError:
pass
However pytest.raises will not manage this, and will fail with catching the ExceptionGroup instead.
with pytest.raises(ValueError):
raise ExceptionGroup("", [ValueError])
Describe the solution you’d like
Assert some code throws an exception or an exception as part of an exception group.
anyio/trio will now always wrap exception in ExceptionGroups when raised from taskgroups. This leads to silly checks like: https://github.com/agronholm/anyio/blob/3f1eca1addcd782e2347350a6ddb2ad2b80c6354/tests/test_taskgroups.py#L279C1-L287C31 to unwrap the exceptiongroup.
A basic solution to handle this (without the bells and whistles) would be:
@contextlib.contextmanager
def raises_nested(exc):
try:
yield
except* exc:
pass
else:
raise AssertionError("Did not raise expected exception")
Alternative Solutions
Additional context
About this issue
- Original URL
- State: open
- Created 8 months ago
- Reactions: 4
- Comments: 27 (20 by maintainers)
Seems pytest 8 solves this issue. Looks to have taken my preferred route of matching as old raises. https://docs.pytest.org/en/stable/how-to/assert.html#assert-matching-exception-groups
I’m not sure whether this should go in here, or in #10441 (which was maybe closed prematurely), or in a new issue - but when writing a helper for
TrioI went through a couple ideas for ways to solve this - and I think my favorite one is introducing a classExpectedExceptionGroupthatpytest.raisescan accept an instance of, in place of atype[Exception].Example usage:
supports nested structures:
and works with the current feature of passing a tuple to
pytest.raiseswhen you expect one of several errors - though maybe not in a super elegant way if you’re nested several layers deep:Can additionally work in keyword args for matching message and/or note.
and possibly also accept instances of exceptions, to be able to match the message in sub-exceptions
This would fulfill all of the requested bullet points, be a minimal change to the API, and shouldn’t be very complicated to implement. To experiment outside of pytest (as requested in https://github.com/pytest-dev/pytest/issues/10441#issuecomment-1293080834) should be somewhat doable - if a bit messy*
*can duplicate the functionality of
pytest.raises, or monkey patch_pytest.python_api.RaisesContext.__exit__, or possibly with https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__ to trick https://github.com/pytest-dev/pytest/blob/fdb8bbf15499fc3dfe3547822df7a38fcf103538/src/_pytest/python_api.py#L1017C3-L1017C3 )