@@ -15,11 +15,11 @@ Abstract
15
15
========
16
16
Exception objects are typically initialized with a message that describes the
17
17
error which has occurred. Because further information may be available when the
18
- exception is caught and re-raised, or included in an ``ExceptionGroup `` , this PEP
18
+ exception is caught and re-raised, or included in an ``ExceptionGroup ``, this PEP
19
19
proposes to add a ``.__note__ `` attribute and update the builtin traceback formatting
20
20
code to include it in the formatted traceback following the exception string.
21
21
22
- This is particularly useful in relation to :pep: `654 ` ``ExceptionGroup `` s, which
22
+ This is particularly useful in relation to :pep: `654 ` ``ExceptionGroup ``\ s, which
23
23
make previous workarounds ineffective or confusing. Use cases have been identified
24
24
in the standard library, Hypothesis package, and common code patterns with retries.
25
25
@@ -32,17 +32,18 @@ where it is useful to add information after the exception was caught.
32
32
For example,
33
33
34
34
- testing libraries may wish to show the values involved in a failing assertion,
35
- or the steps to reproduce a failure (e.g. ``pytest `` and ``hypothesis `` ; example below).
36
- - code with retries may wish to note which iteration or timestamp raised which
37
- of several possible errors - especially if re-raising them in an ``ExceptionGroup ``
35
+ or the steps to reproduce a failure (e.g. ``pytest `` and ``hypothesis ``; example below).
36
+ - code which retries an operation on error may wish to associate an iteration, timestamp,
37
+ or other explanation with each of several errors - especially if re-raising them in
38
+ an ``ExceptionGroup ``.
38
39
- programming environments for novices can provide more detailed descriptions
39
- of various errors, and tips for resolving them (e.g. ``friendly-traceback `` ).
40
+ of various errors, and tips for resolving them (e.g. ``friendly-traceback ``).
40
41
41
42
Existing approaches must pass this additional information around while keeping
42
43
it in sync with the state of raised, and potentially caught or chained, exceptions.
43
- This is already error-prone, and made more difficult by :pep: `654 ` ``ExceptionGroup `` s,
44
+ This is already error-prone, and made more difficult by :pep: `654 ` ``ExceptionGroup ``\ s,
44
45
so the time is right for a built-in solution. We therefore propose to add a mutable
45
- field ``__note__ `` to ``BaseException `` , which can be assigned a string - and
46
+ field ``__note__ `` to ``BaseException ``, which can be assigned a string - and
46
47
if assigned, is automatically displayed in formatted tracebacks.
47
48
48
49
@@ -110,19 +111,17 @@ Non-goals
110
111
---------
111
112
``__note__ `` is *not * intended to carry structured data. If your note is for use by
112
113
a program rather than display to a human, we recommend instead chosing a convention
113
- for an attribute like e.g. ``err._parse_errors = ... `` on the error or ``ExceptionGroup ``
114
- (`discussion <https://discuss.python.org/t/accepting-pep-654-exception-groups-and-except/10813/26 >`__,
115
- `discussion <https://bugs.python.org/issue46431 >`__).
114
+ for an attribute like e.g. ``err._parse_errors = ... `` on the error or ``ExceptionGroup `` [1 ]_ [2 ]_
116
115
117
116
As a rule of thumb, prefer `exception chaining <https://docs.python.org/3/tutorial/errors.html#exception-chaining >`__
118
- when the error is going to be re-raised or handled as an individual error; and prefer
119
- ``__note__ `` when you are collecting multiple exception objects to handle together or later.
117
+ when the error is going to be re-raised or handled as an individual error, and prefer
118
+ ``__note__ `` when you are collecting multiple exception objects to handle together or later. [ 3 ]_
120
119
121
120
122
121
Specification
123
122
=============
124
123
125
- ``BaseException `` gains a new mutable attribute ``__note__ `` , which defaults to
124
+ ``BaseException `` gains a new mutable attribute ``__note__ ``, which defaults to
126
125
``None `` and may have a string assigned. When an exception with a note is displayed,
127
126
the note is displayed immediately after the exception.
128
127
@@ -131,7 +130,7 @@ users are responsible for implementing it with e.g.::
131
130
132
131
e.__note__ = msg if e.__note__ is None else e.__note__ + "\n" + msg
133
132
134
- It is an error to assign a non-string-or-``None `` value to ``__note__ `` ,
133
+ It is an error to assign a non-string-or-``None `` value to ``__note__ ``,
135
134
or to attempt to delete the attribute.
136
135
137
136
``BaseExceptionGroup.subgroup `` and ``BaseExceptionGroup.split ``
@@ -141,11 +140,11 @@ copy the ``__note__`` of the original exception group to the parts.
141
140
Backwards Compatibility
142
141
=======================
143
142
144
- System-defined or "dunder" names (following the pattern ``__*__ `` ) are part of the
143
+ System-defined or "dunder" names (following the pattern ``__*__ ``) are part of the
145
144
language specification, with unassigned names reserved for future use and subject
146
- to breakage without warning [1 ]_.
145
+ to breakage without warning [4 ]_.
147
146
148
- We are also unaware of any code which *would * be broken by adding ``__note__ `` ;
147
+ We are also unaware of any code which *would * be broken by adding ``__note__ ``;
149
148
assigning to a ``.__note__ `` attribute already *works * on current versions of
150
149
Python - the note just won't be displayed with the traceback and exception message.
151
150
@@ -155,23 +154,23 @@ How to Teach This
155
154
=================
156
155
157
156
The ``__note__ `` attribute will be documented as part of the language standard,
158
- and explained as part of the tutorial "Errors and Exceptions" [2 ]_.
157
+ and explained as part of the tutorial "Errors and Exceptions" [5 ]_.
159
158
160
159
161
160
162
161
Reference Implementation
163
162
========================
164
163
165
- ``BaseException.__note__ `` was implemented in [3 ]_ and released in CPython 3.11.0a3,
166
- following discussions related to :pep: `654 `. [4 ]_ [5 ]_ [6 ]_
164
+ ``BaseException.__note__ `` was implemented in [6 ]_ and released in CPython 3.11.0a3,
165
+ following discussions related to :pep: `654 `. [7 ]_ [8 ]_ [9 ]_
167
166
168
167
169
168
170
169
Rejected Ideas
171
170
==============
172
171
173
- Use ``print() `` (or ``logging `` , etc.)
174
- ---------------------------------------
172
+ Use ``print() `` (or ``logging ``, etc.)
173
+ --------------------------------------
175
174
Reporting explanatory or contextual information about an error by printing or logging
176
175
has historically been an acceptable workaround. However, we dislike the way this
177
176
separates the content from the exception object it refers to - which can lead to
@@ -187,19 +186,19 @@ eliminates these problems.
187
186
---------------------------------------
188
187
An alternative pattern is to use exception chaining: by raising a 'wrapper' exception
189
188
containing the context or explanation ``from `` the current exception, we avoid the
190
- separation challenges from ``print() `` . However, this has two key problems.
189
+ separation challenges from ``print() ``. However, this has two key problems.
191
190
192
191
First, it changes the type of the exception, which is often a breaking change for
193
192
downstream code. We consider *always * raising a ``Wrapper `` exception unacceptably
194
193
inelegant; but because custom exception types might have any number of required
195
194
arguments we can't always create an instance of the *same * type with our explanation.
196
195
In cases where the exact exception type is known this can work, such as the standard
197
- library ``http.client `` code [7 ]_, but not for libraries which call user code.
196
+ library ``http.client `` code [10 ]_, but not for libraries which call user code.
198
197
199
198
Second, exception chaining reports several lines of additional detail, which are
200
199
distracting for experienced users and can be very confusing for beginners.
201
200
For example, six of the eleven lines reported for this simple example relate to
202
- exception chaining, and are unnecessary with ``BaseException.__note__ `` :
201
+ exception chaining, and are unnecessary with ``BaseException.__note__ ``:
203
202
204
203
.. code-block :: python
205
204
@@ -228,8 +227,8 @@ exception chaining, and are unnecessary with ``BaseException.__note__`` :
228
227
Explanation: # Hence this PEP!
229
228
You can reproduce this error by ...
230
229
231
- **Where these factors are not problematic , we encourage use of exception chaining
232
- rather than ** ``__note__ `` .
230
+ **In cases where these two problems do not apply , we encourage use
231
+ of exception chaining rather than ** ``__note__ ``.
233
232
234
233
235
234
Subclass Exception and add ``__note__ `` downstream
@@ -245,15 +244,23 @@ proposed ``__note__`` semantics, but this would be rarely and inconsistently
245
244
applicable.
246
245
247
246
248
- Augment the ``raise `` statement
249
- -------------------------------
250
- One discussion proposed ``raise Exception() with "note contents" `` , but this
251
- does not address the original motivation of compatibility with ``ExceptionGroup `` .
247
+ Store notes in ``ExceptionGroup ``\ s
248
+ ------------------------------------
249
+ Initial discussions proposed making a more focussed change by thinking about how to
250
+ associate messages with the nested exceptions in ``ExceptionGroup `` s, such as a list
251
+ of notes or mapping of exceptions to notes. However, this would force a remarkably
252
+ awkward API and retains a lesser form of the cross-referencing problem discussed
253
+ under "use ``print() ``" above; if this PEP is rejected we prefer the status quo.
254
+ Finally, of course, ``__note__ `` is not only useful with ``ExceptionGroup `` s!
252
255
253
- Furthermore, this would be an additional feature over adding the attribute and
254
- modifying the traceback-formatting code, and we do not expect ``__note__ `` to be
255
- so frequently-used as to justify new syntax or even a new ``.with_note() `` method
256
- (analogous to ``.with_traceback() `` ).
256
+
257
+
258
+ Possible Future Enhancements
259
+ ============================
260
+
261
+ In addition to rejected alternatives, there have been a range of suggestions which
262
+ we believe should be deferred to a future version, when we have more experience with
263
+ the uses (and perhaps misuses) of ``__note__ ``.
257
264
258
265
259
266
Allow any object, and cast to string for display
@@ -262,16 +269,18 @@ We have not identified any scenario where libraries would want to do anything bu
262
269
concatenate or replace notes, and so the additional complexity and interoperability
263
270
challenges do not seem justified.
264
271
265
- Variations on this have also been proposed for forward-compatibility with a future
266
- structured API, but in the absence of any proposed use-case (see also "Non-goals")
267
- we strongly prefer to keep things simple - and note that implementing any nontrival
268
- future behavior would then break backwards-compatibility .
272
+ Permitting any object would also force any future structured API to change the behaviour
273
+ of already-legal code, whereas expanding the legal contents of `` __note__ `` from strings
274
+ to include other objects is fully backwards-compatible. In the absence of any proposed
275
+ use-case (see also ` Non-goals `_), we strongly prefer to keep things simple .
269
276
270
277
271
278
Add a helper function ``contexlib.add_exc_note() ``
272
279
--------------------------------------------------
273
280
We don't expect notes to be used frequently enough to justify this either, but
274
- provide the following extensible recipe::
281
+ provide the following extensible recipe:
282
+
283
+ .. code-block :: python
275
284
276
285
@contextlib.contextmanager
277
286
def add_exc_note (note : str ):
@@ -288,27 +297,33 @@ provide the following extensible recipe::
288
297
frobnicate_or_raise(item)
289
298
290
299
291
- Store notes in ``ExceptionGroup `` s
292
- -----------------------------------
293
- Initial discussions proposed making a more focussed change by thinking about how to
294
- associate messages with the nested exceptions in ``ExceptionGroup `` s, such as a list
295
- of notes or mapping of exceptions to notes. However, this would force a remarkably
296
- awkward API and retains a lesser form of the cross-referencing problem discussed
297
- under "use ``print() `` " above; if this PEP is rejected we prefer the status quo.
298
- Finally, of course, ``__note__ `` is not only useful with ``ExceptionGroup `` s!
300
+ Augment the ``raise `` statement
301
+ -------------------------------
302
+ One discussion proposed ``raise Exception() with "note contents" ``, but this
303
+ does not address the original motivation of compatibility with ``ExceptionGroup ``.
304
+
305
+ Furthermore, this would be an additional feature over adding the attribute and
306
+ modifying the traceback-formatting code, and we do not expect ``__note__ `` to be
307
+ so frequently-used as to justify new syntax, or even a new ``.with_note() `` method
308
+ analogous to ``.with_traceback() ``.
299
309
300
310
301
311
302
312
References
303
313
==========
304
314
305
- .. [1 ] https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers
306
- .. [2 ] https://github.com/python/cpython/pull/30158
307
- .. [3 ] https://github.com/python/cpython/pull/29880
308
- .. [4 ] https://discuss.python.org/t/accepting-pep-654-exception-groups-and-except/10813/9
309
- .. [5 ] https://github.com/python/cpython/pull/28569#discussion_r721768348
310
- .. [6 ] https://bugs.python.org/issue45607
311
- .. [7 ] https://github.com/python/cpython/blob/69ef1b59983065ddb0b712dac3b04107c5059735/Lib/http/client.py#L596-L597
315
+ .. [1 ] https://discuss.python.org/t/accepting-pep-654-exception-groups-and-except/10813/26
316
+ .. [2 ] https://bugs.python.org/issue46431
317
+ .. [3 ] this principle was established in the 2003 mail thread which led to :pep: `3134 `,
318
+ and included a proposal for a group-of-exceptions type!
319
+ https://mail.python.org/pipermail/python-dev/2003-January/032492.html
320
+ .. [4 ] https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers
321
+ .. [5 ] https://github.com/python/cpython/pull/30158
322
+ .. [6 ] https://github.com/python/cpython/pull/29880
323
+ .. [7 ] https://discuss.python.org/t/accepting-pep-654-exception-groups-and-except/10813/9
324
+ .. [8 ] https://github.com/python/cpython/pull/28569#discussion_r721768348
325
+ .. [9 ] https://bugs.python.org/issue45607
326
+ .. [10 ] https://github.com/python/cpython/blob/69ef1b59983065ddb0b712dac3b04107c5059735/Lib/http/client.py#L596-L597
312
327
313
328
314
329
0 commit comments