@@ -14,20 +14,63 @@ group of unrelated exceptions being propagated together.
14
14
## Motivation
15
15
16
16
The interpreter is currently able to propagate at most one exception at a
17
- time. The chaining features introduced in PEP3134 [ reference] link together
18
- exceptions that are related to each other as the cause or context, but there
19
- are situations where there are multiple unrelated exceptions that need to be
20
- propagated together as the stack unwinds. Several real world use cases are
21
- listed below.
17
+ time. The chaining features introduced in
18
+ [ PEP 3134] ( https://www.python.org/dev/peps/pep-3134/ ) link together exceptions that are
19
+ related to each other as the cause or context, but there are situations where
20
+ multiple unrelated exceptions need to be propagated together as the stack
21
+ unwinds. Several real world use cases are listed below.
22
+
23
+ * ** Concurrent errors** . Libraries for async concurrency provide APIs to invoke
24
+ multiple tasks and return their results in aggregate. There isn't currently
25
+ a good way for such libraries to handle situations where multiple tasks
26
+ raise exceptions. The Python standard library's
27
+ [ ` asyncio.gather() ` ] ( https://docs.python.org/3/library/asyncio-task.html#asyncio.gather )
28
+ function provides two options: raise the first exception, or return the
29
+ exceptions in the results list. The [ Trio] ( https://trio.readthedocs.io/en/stable/ )
30
+ library has a ` MultiError ` exception type which it raises to report a
31
+ collection of errors. Work on this PEP was initially motivated by the
32
+ difficulties in handling ` MultiError ` s, which are detailed in a design
33
+ document for an
34
+ [ improved version, ` MultiError2 ` ] ( [https://github.com/python-trio/trio/issues/611 ) .
35
+ That document demonstrates how difficult it is to create an effective API
36
+ for reporting and handling multiple errors without the language changes we
37
+ are proposing.
38
+
39
+ * ** Multiple failures when retrying an operation.** The Python standard library's
40
+ ` socket.create_connection ` may attempt to connect to different addresses,
41
+ and if all attempts fail it needs to report that to the user. It is an open
42
+ issue how to aggregate these errors, particularly when they are different
43
+ [[ Python issue 29980] ( https://bugs.python.org/issue29980 )] .
44
+
45
+ * ** Multiple user callbacks fail.** The pytest library allows users to register
46
+ finalizers which are executed at teardown. If more than one of these
47
+ finalizers raises an exception, only the first is reported to the user. This
48
+ can be improved with ` ExceptionGroup ` s, as explained in this issue by pytest
49
+ developer Ran Benita [[ Pytest issue 8217] ( https://github.com/pytest-dev/pytest/issues/8217 )]
50
+
51
+ * ** Multiple errors in a complex calculation.** The Hypothesis library performs
52
+ automatic bug reduction (simplifying code that demonstrates a bug). In the
53
+ process it may find variations that generate different errors, and
54
+ (optionally) reports all of them
55
+ [[ Hypothesis documentation] ( https://hypothesis.readthedocs.io/en/latest/settings.html#hypothesis.settings.report_multiple_bugs )] .
56
+ An ` ExceptionGroup ` mechanism as we are proposing here can resolve some of
57
+ the difficulties with debugging that are mentioned in the link above, and
58
+ which are due to the loss of context/cause information (communicated
59
+ by Hypothesis Core Developer Zac Hatfield-Dodds).
60
+
61
+ * ** Errors in wrapper code.** The Python standard library's
62
+ ` tempfile.TemporaryDirectory ` context manager
63
+ had an issue where an exception raised during cleanup in ` __exit__ `
64
+ effectively masked an exception that the user's code raised inside the context
65
+ manager scope. While the user's exception was chained as the context of the
66
+ cleanup error, it was not caught by the user's except clause
67
+ [[ Python issue 40857] ( https://bugs.python.org/issue40857 )] .
68
+ The issue was resolved by making the cleanup code ignore errors, thus
69
+ sidestepping the multiple exception problem. With the features we propose
70
+ here, it would be possible for ` __exit__ ` to raise an ` ExceptionGroup `
71
+ containing its own errors as well as the user's errors as unrelated errors,
72
+ and this would allow the user to catch their own exceptions by their types.
22
73
23
- [ TODO: flesh these out]
24
- * asyncio programs, trio, etc
25
-
26
- * Multiple errors from separate retries of an operation [ https://bugs.python.org/issue29980 ]
27
-
28
- * Situations where multiple unrelated exceptions may be of interest to calling code [ https://bugs.python.org/issue40857 ]
29
-
30
- * Multiple teardowns in pytest raising exceptions [ https://github.com/pytest-dev/pytest/issues/8217 ]
31
74
32
75
## Rationale
33
76
0 commit comments