Python "raises in exception group" context manager for tests

Here’s a Python testing utility function for asserting on an expected Exception type from within an ExceptionGroup:

from contextlib import contextmanager


@contextmanager
def raises_in_group(
    expected_exception: type[Exception],
    match_message: str = "",
):
    """
    Like pytest.raises, but for an Exception inside an ExceptionGroup, for
    example coming from asyncio.TaskGroup.
    """

    matched_type = []
    matched_message = []
    try:
        yield
    except* expected_exception as e:
        assert isinstance(e, ExceptionGroup)
        matched_type = [exp for exp in e.exceptions if isinstance(exp, expected_exception)]
        matched_message = [exp for exp in matched_type if match_message in str(exp)]

    assert len(matched_type) >= 1, f"Expected at least one {expected_exception} exception."
    assert (
        len(matched_message) >= 1
    ), f"Expected at least one {expected_exception} exception matching '{match_message}'."

It can be used in test cases in a similar way to pytest.raises, like this:

with raises_in_group(ValueError, "foobar error message"):
  do_something()

Python introduced ExceptionGroup to handle situations where multiple exceptions might be raised during a process and the user wants to be aware of all of them to decide how to proceed. As an example, the asyncio.TaskGroup context manager uses ExceptionGroup to handle multiple exceptions occurring in different tasks.


Tech mentioned