8000 PEP 678: simplify specification to use a list and create the __notes_… · python/peps@25fa660 · GitHub
[go: up one dir, main page]

Skip to content

Commit 25fa660

Browse files
authored
PEP 678: simplify specification to use a list and create the __notes__ attr when it is first populated (GH-2456)
1 parent 4370f8f commit 25fa660

File tree

1 file changed

+33
-64
lines changed

1 file changed

+33
-64
lines changed

pep-0678.rst

Lines changed: 33 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Exception objects are typically initialized with a message that describes the
1818
error which has occurred. Because further information may be available when
1919
the exception is caught and re-raised, or included in an ``ExceptionGroup``,
2020
this PEP proposes to add ``BaseException.add_note(note)``, a
21-
``.__notes__`` attribute holding a tuple of zero or more notes so added, and to
21+
``.__notes__`` attribute holding a list of notes so added, and to
2222
update the builtin traceback formatting code to include notes in the formatted
2323
traceback following the exception string.
2424

@@ -50,9 +50,9 @@ exceptions. This is already error-prone, and made more difficult by :pep:`654`
5050
``ExceptionGroup``\ s, so the time is right for a built-in solution. We
5151
therefore propose to add:
5252

53-
- a new method ``BaseException.add_note(note)``,
54-
- ``BaseException.__notes__``, a read-only attribute which is a tuple of zero or
55-
more note strings, and
53+
- a new method ``BaseException.add_note(note: str)``,
54+
- ``BaseException.__notes__``, a list of note strings added using
55+
``.add_note()``, and
5656
- support in the builtin traceback formatting code such that notes are
5757
displayed in the formatted traceback following the exception string.
5858

@@ -120,7 +120,7 @@ includes a note of the minimal failing example::
120120

121121
Non-goals
122122
---------
123-
Tracking multiple notes as a tuple, rather than by concatenating strings when
123+
Tracking multiple notes as a list, rather than by concatenating strings when
124124
notes are added, is intended to maintain the distinction between the
125125
individual notes. This might be required in specialized use cases, such
126126
as translation of the notes by packages like ``friendly-traceback``.
@@ -141,24 +141,29 @@ are collecting multiple exception objects to handle together. [1]_
141141
Specification
142142
=============
143143

144-
``BaseException`` gains a new new method ``.add_note(note: str)``. Notes are
145-
exposed as a tuple via the read-only attribute ``__notes__``, and appear in
146-
the standard traceback after the exception string. ``.add_note(note)`` replaces
147-
``__notes__`` with a new tuple equal to ``__notes__ + (note,)``, if ``note``
148-
is a string, and raises ``TypeError`` otherwise.
144+
``BaseException`` gains a new method ``.add_note(note: str)``. If ``note`` is
145+
a string, ``.add_note(note)`` appends it to the ``__notes__`` list, creating
146+
the attribute if it does not already exist. If ``note`` is not a string,
147+
``.add_note()`` raises ``TypeError``.
149148

150-
``del err.__notes__`` clears the contents of the ``__notes__`` attribute,
151-
leaving it an empty tuple as if ``.add_note()`` had never been called. This
152-
allows libraries full control over the attached notes, by re-adding whatever
153-
they wish, without overly complicating the API or adding multiple names to
149+
Libraries may clear existing notes by modifying or deleting the ``__notes__``
150+
list, if it has been created, including clearing all notes with
151+
``del err.__notes__``. This allows full control over the attached notes,
152+
without overly complicating the API or adding multiple names to
154153
``BaseException.__dict__``.
155154

156155
When an exception is displayed by the interpreter's builtin traceback-rendering code,
157156
its notes (if there are any) appear immediately after the exception message, in the order
158157
in which they were added, with each note starting on a new line.
159158

160-
``BaseExceptionGroup.subgroup`` and ``BaseExceptionGroup.split`` copy the
161-
``__notes__`` of the original exception group to the parts.
159+
If ``__notes__`` has been created, ``BaseExceptionGroup.subgroup`` and
160+
``BaseExceptionGroup.split`` create new lists on each new instance containing
161+
the same contents as the original exception group.
162+
163+
We *do not* specify the expected behaviour when users have assigned a non-list
164+
value to ``__notes__``, or a list which contains non-string elements.
165+
Implementations might choose to emit warnings, discard or ignore bad values,
166+
convert them to strings, raise an exception, or do something else entirely.
162167

163168

164169
Backwards Compatibility
@@ -279,44 +284,13 @@ libraries such as ``friendly-traceback``, without resorting to dubious parsing
279284
heuristics, we therefore settled on the ``.add_note()``-and-``__notes__`` API.
280285

281286

282-
Allow any object, and convert to string for display
283-
---------------------------------------------------
284-
We have not identified any scenario where libraries would want to do anything
285-
but either concatenate or replace notes, and so the additional complexity and
286-
interoperability challenges do not seem justified.
287-
288-
We also note that converting an object to a string may raise an exception.
289-
It's more helpful for the traceback to point to the location where the note is
290-
attached to the exception, rather than where the exception and note are being
291-
formatted for display after propagation.
292-
293-
294-
Just make ``__notes__`` mutable
295-
-------------------------------
296-
If ``__notes__`` was mutable (e.g. a list) or even assignable, there would be
297-
no need for explicit methods to add and remove notes separate from e.g.
298-
``ex.__notes__.append(note)`` or ``ex.__notes__.clear()``. While we like the
299-
simplicity of this approach, it cannot guarantee that ``__notes__`` is a
300-
sequence of strings, and thus risks failing at traceback-display time like
301-
the proposal above.
302-
303-
304-
Separate methods for e.g. ``.clear_notes()``
305-
--------------------------------------------
306-
We expect that clearing or replacing notes will be extremely rare, so while
307-
we wish to make this *possible*, we do not consider these APIs worth the cost.
308-
The ``del err.__notes__`` pattern has precedent in e.g. invalidation of an
309-
``@functools.cached_property``, or any other descriptor with a deleter, and
310-
is sufficient for library code to implement whatever pattern is desired.
311-
312-
313287
Subclass Exception and add note support downstream
314288
--------------------------------------------------
315289
Traceback printing is built into the C code, and reimplemented in pure Python
316290
in ``traceback.py``. To get ``err.__notes__`` printed from a downstream
317291
implementation 6D40 would *also* require writing custom traceback-printing code;
318292
while this could be shared between projects and reuse some pieces of
319-
traceback.py we prefer to implement this once, upstream.
293+
traceback.py [3]_ we prefer to implement this once, upstream.
320294

321295
Custom exception types could implement their ``__str__`` method to include our
322296
proposed ``__notes__`` semantics, but this would be rarely and inconsistently
@@ -335,22 +309,13 @@ We believe that the cleaner interface, and other use-cases described above,
335309
are sufficient to justify the more general feature proposed by this PEP.
336310

337311

338-
339-
Possible Future Enhancements
340-
============================
341-
342-
In addition to rejected alternatives, there have been a range of suggestions
343-
which we believe should be deferred to a future version, when we have more
344-
experience with the uses (and perhaps misuses) of ``__notes__``.
345-
346-
347312
Add a helper function ``contextlib.add_exc_note()``
348313
---------------------------------------------------
349314
It `was suggested
350315
<https://www.reddit.com/r/Python/comments/rmrvxv/pep_678_enriching_exceptions_with_notes/hptbul1/>`__
351-
that we add a utility such as the one below to the standard library. We are
352-
open to this idea, but do not see it as a core part of the proposal of this PEP
353-
as it can be added as an enhancement later.
316+
that we add a utility such as the one below to the standard library. We do not
317+
see this idea as core to the proposal of this PEP, and thus leave it for later
318+
or downstream implementation - perhaps based on this example code:
354319

355320
.. code-block:: python
356321
@@ -381,9 +346,9 @@ Acknowledgements
381346
We wish to thank the many people who have assisted us through conversation,
382347
code review, design advice, and implementation: Adam Turner, Alex Grönholm,
383348
André Roberge, Barry Warsaw, Brett Cannon, CAM Gerlach, Carol Willing, Damian,
384-
Erlend Aasland, Gregory Smith, Guido van Rossum, Irit Katriel, Jelle Zijlstra,
385-
Ken Jin, Kumar Aditya, Mark Shannon, Matti Picus, Petr Viktorin,
386-
and pseudonymous commenters on Discord and Reddit.
349+
Erlend Aasland, Etienne Pot, Gregory Smith, Guido van Rossum, Irit Katriel,
350+
Jelle Zijlstra, Ken Jin, Kumar Aditya, Mark Shannon, Matti Picus, Petr
351+
Viktorin, Will McGugan, and pseudonymous commenters on Discord and Reddit.
387352

388353

389354
References
@@ -395,7 +360,11 @@ References
395360
.. [2] particularly those at https://bugs.python.org/issue45607,
396361
https://discuss.python.org/t/accepting-pep-654-exception-groups-and-except/10813/9,
397362
https://github.com/python/cpython/pull/28569#discussion_r721768348, and
398-
363+
.. [3] We note that the ``exceptiongroup`` backport package maintains an exception
364+
hook and monkeypatch for ``TracebackException`` for Pythons older than 3.11,
365+
and encourage library authors to avoid creating additional and incompatible
366+
backports. We also reiterate our preference for builtin support which
367+
makes such measures unnecessary.
399368
400369
401370
Copyright

0 commit comments

Comments
 (0)
0