exceptiongroup

A backport of Python 3.11's BaseExceptionGroup and ExceptionGroup classes, letting older Python versions catch and format grouped exceptions.

Library
PyPI
v1.3.1
49stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
34/100Needs Attention
Development Activity24
Maintenance20
Community24
Maturity56
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
61/100Good
Architecture78
Code Quality85
Innovation45
Learning Curve35

exceptiongroup backports the BaseExceptionGroup and ExceptionGroup classes introduced by PEP 654 in Python 3.11, along with a catch() helper for dispatching handlers across grouped exceptions on versions that lack the native except* syntax. It also ships a backport of Python 3.12.1’s contextlib.suppress() that understands exception groups, and patches traceback.TracebackException plus sys.excepthook so unhandled exception groups print correctly on older interpreters.

On Python 3.11 and later the package steps out of the way entirely: it re-exports the built-in classes and traceback functions instead of the backported versions, and skips the monkey-patching altogether. This makes it safe to depend on unconditionally across a wide version range, which is why it has become a common transitive dependency for libraries like pytest and anyio that need exception-group semantics on Python 3.7–3.10.

What You Get

  • BaseExceptionGroup and ExceptionGroup classes with subgroup(), split(), and derive() methods matching the CPython 3.11 implementation
  • A catch() context manager for dispatching different exception types (even nested inside groups) to separate handler callables without except* syntax
  • A backport of contextlib.suppress() from Python 3.12.1 that correctly suppresses exceptions nested inside exception groups
  • Monkey-patched TracebackException and an installed sys.excepthook so unhandled exception groups format correctly in tracebacks on pre-3.11 Python
  • Drop-in versions of traceback.format_exception(), format_exception_only(), print_exception(), and print_exc() that render exception groups even when monkey patching is blocked

Common Use Cases

  • Libraries that need to raise and match exception groups (e.g. from concurrent task groups) while still supporting Python 3.7–3.10
  • Applications catching multiple unrelated exception types raised by parallel operations and handling each type differently via catch()
  • Test suites and frameworks (pytest, anyio) that rely on exception-group semantics as a transitive dependency across the full supported Python range
  • Code that wants correctly formatted tracebacks for exception groups without waiting for users to upgrade to Python 3.11
  • Selectively suppressing specific exception types that may be wrapped inside an exception group, via the backported suppress()

Under The Hood

Architecture exceptiongroup is a small single-purpose backport structured as flat modules under src/exceptiongroup/: __init__.py conditionally imports either the stdlib’s native BaseExceptionGroup/ExceptionGroup (Python 3.11+) or the pure-Python implementations in _exceptions.py, chooses contextlib.suppress versus the local _suppress.py backport based on a Python 3.12.1 version check, and installs traceback monkey-patching from _formatting.py plus a sys.excepthook only when running on Python <3.11 and no other hook is already present (guarded by the EXCEPTIONGROUP_NO_PATCH env var). The core abstraction is _exceptions.BaseExceptionGroup, which reimplements CPython’s __new__/subgroup/split/derive semantics for grouping and filtering exceptions, and _catch.py builds a context manager (_Catcher) on top of split() to dispatch grouped exceptions to per-type handler callables — every other module is written against that exact return contract, making it the piece that would break everything else if changed.

Tech Stack Pure Python 3.7+ with no runtime dependencies beyond typing_extensions>=4.6.0 (only on Python <3.13, for TypeVar defaults), built with flit_scm as the PEP 517 backend and setuptools_scm for version derivation. Testing uses pytest>=6 matrixed across Python 3.7–3.14 and PyPy3 via tox, with a separate “typing” tox environment running pyright and mypy against tests/check_types.py. Linting and formatting are enforced by ruff (including isort, pyupgrade, and flake8-implicit-str-concat rules) through pre-commit, and releases publish to PyPI via a tag-triggered GitHub Actions workflow using pypa/gh-action-pypi-publish with OIDC trusted publishing.

Code Quality The test suite spans several thousand lines across test_exceptions.py, test_formatting.py, test_catch.py, test_catch_py311.py, and test_suppress.py, exercising both the native (3.11+) and backported code paths, plus subprocess-based tests for apport excepthook integration. Error handling is explicit — the constructor raises TypeError/ValueError with specific messages for invalid arguments (non-string message, empty exception sequence, nesting a BaseException inside an ExceptionGroup) rather than swallowing bad input. Naming is consistent snake_case with underscore-prefixed private modules; type hints are comprehensive and verified in CI via pyright/mypy, and the package ships a py.typed marker.

What Makes It Unique This is a backport, not a novel design — its value is faithfully reproducing PEP 654 semantics on Python versions that predate the stdlib feature, then getting out of the way entirely on 3.11+. Its one genuinely useful addition beyond a plain backport is the monkey-patched TracebackException and installed exception hook that make ungrouped tracebacks print correctly on old Pythons, plus the catch() context-manager sugar for exception dispatch without except* syntax. Not technically novel, but a well-scoped compatibility shim relied on transitively by widely-used libraries.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search