diff --git a/CHANGES.rst b/CHANGES.rst
index 8b415ef..b75db4d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,8 @@ This library adheres to `Semantic Versioning 2.0 `_.
- Added special monkeypatching if `Apport `_ has
overridden ``sys.excepthook`` so it will format exception groups correctly
(PR by John Litborn)
+- Added a backport of ``contextlib.suppress()`` from Python 3.12.1 which also handles
+ suppressing exceptions inside exception groups
- Fixed bare ``raise`` in a handler reraising the original naked exception rather than
an exception group which is what is raised when you do a ``raise`` in an ``except*``
handler
diff --git a/README.rst b/README.rst
index 48178d9..d5203fd 100644
--- a/README.rst
+++ b/README.rst
@@ -26,6 +26,8 @@ It contains the following:
* ``traceback.format_exception_only()``
* ``traceback.print_exception()``
* ``traceback.print_exc()``
+* A backported version of ``contextlib.suppress()`` from Python 3.12.1 which also
+ handles suppressing exceptions inside exception groups
If this package is imported on Python 3.11 or later, the built-in implementations of the
exception group classes are used instead, ``TracebackException`` is not monkey patched
@@ -84,6 +86,18 @@ would be written with this backport like this:
**NOTE**: Just like with ``except*``, you cannot handle ``BaseExceptionGroup`` or
``ExceptionGroup`` with ``catch()``.
+Suppressing exceptions
+======================
+
+This library contains a backport of the ``contextlib.suppress()`` context manager from
+Python 3.12.1. It allows you to selectively ignore certain exceptions, even when they're
+inside exception groups::
+
+ from exceptiongroup import suppress
+
+ with suppress(RuntimeError):
+ raise ExceptionGroup("", [RuntimeError("boo")])
+
Notes on monkey patching
========================
diff --git a/src/exceptiongroup/__init__.py b/src/exceptiongroup/__init__.py
index 0e7e02b..d8e36b2 100644
--- a/src/exceptiongroup/__init__.py
+++ b/src/exceptiongroup/__init__.py
@@ -6,6 +6,7 @@
"format_exception_only",
"print_exception",
"print_exc",
+ "suppress",
]
import os
@@ -38,3 +39,8 @@
BaseExceptionGroup = BaseExceptionGroup
ExceptionGroup = ExceptionGroup
+
+if sys.version_info < (3, 12, 1):
+ from ._suppress import suppress
+else:
+ from contextlib import suppress
diff --git a/src/exceptiongroup/_suppress.py b/src/exceptiongroup/_suppress.py
new file mode 100644
index 0000000..6741563
--- /dev/null
+++ b/src/exceptiongroup/_suppress.py
@@ -0,0 +1,40 @@
+import sys
+from contextlib import AbstractContextManager
+
+if sys.version_info < (3, 11):
+ from ._exceptions import BaseExceptionGroup
+
+
+class suppress(AbstractContextManager):
+ """Backport of :class:`contextlib.suppress` from Python 3.12.1."""
+
+ def __init__(self, *exceptions):
+ self._exceptions = exceptions
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exctype, excinst, exctb):
+ # Unlike isinstance and issubclass, CPython exception handling
+ # currently only looks at the concrete type hierarchy (ignoring
+ # the instance and subclass checking hooks). While Guido considers
+ # that a bug rather than a feature, it's a fairly hard one to fix
+ # due to various internal implementation details. suppress provides
+ # the simpler issubclass based semantics, rather than trying to
+ # exactly reproduce the limitations of the CPython interpreter.
+ #
+ # See http://bugs.python.org/issue12029 for more details
+ if exctype is None:
+ return
+
+ if issubclass(exctype, self._exceptions):
+ return True
+
+ if issubclass(exctype, BaseExceptionGroup):
+ match, rest = excinst.split(self._exceptions)
+ if rest is None:
+ return True
+
+ raise rest
+
+ return False
diff --git a/tests/test_suppress.py b/tests/test_suppress.py
new file mode 100644
index 0000000..289bb33
--- /dev/null
+++ b/tests/test_suppress.py
@@ -0,0 +1,16 @@
+import sys
+
+import pytest
+
+from exceptiongroup import suppress
+
+if sys.version_info < (3, 11):
+ from exceptiongroup import BaseExceptionGroup, ExceptionGroup
+
+
+def test_suppress_exception():
+ with pytest.raises(ExceptionGroup) as exc, suppress(SystemExit):
+ raise BaseExceptionGroup("", [SystemExit(1), RuntimeError("boo")])
+
+ assert len(exc.value.exceptions) == 1
+ assert isinstance(exc.value.exceptions[0], RuntimeError)