Skip to content

Commit d05a180

Browse files
authored
gh-101100: Improve docs on exception attributes (GH-113057)
* Improve docs on exception attributes * thanks sphinx-lint * fix doctests * argh, okay, give up on doctests * Various improvements
1 parent 2111795 commit d05a180

File tree

6 files changed

+115
-67
lines changed

6 files changed

+115
-67
lines changed

Doc/c-api/exceptions.rst

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,8 @@ Querying the error indicator
541541
542542
.. note::
543543
544-
This function *does not* implicitly set the ``__traceback__``
544+
This function *does not* implicitly set the
545+
:attr:`~BaseException.__traceback__`
545546
attribute on the exception value. If setting the traceback
546547
appropriately is desired, the following additional snippet is needed::
547548
@@ -753,7 +754,8 @@ Exception Objects
753754
.. c:function:: PyObject* PyException_GetTraceback(PyObject *ex)
754755
755756
Return the traceback associated with the exception as a new reference, as
756-
accessible from Python through :attr:`__traceback__`. If there is no
757+
accessible from Python through the :attr:`~BaseException.__traceback__`
758+
attribute. If there is no
757759
traceback associated, this returns ``NULL``.
758760
759761
@@ -767,8 +769,8 @@ Exception Objects
767769
768770
Return the context (another exception instance during whose handling *ex* was
769771
raised) associated with the exception as a new reference, as accessible from
770-
Python through :attr:`__context__`. If there is no context associated, this
771-
returns ``NULL``.
772+
Python through the :attr:`~BaseException.__context__` attribute.
773+
If there is no context associated, this returns ``NULL``.
772774
773775
774776
.. c:function:: void PyException_SetContext(PyObject *ex, PyObject *ctx)
@@ -782,7 +784,8 @@ Exception Objects
782784
783785
Return the cause (either an exception instance, or ``None``,
784786
set by ``raise ... from ...``) associated with the exception as a new
785-
reference, as accessible from Python through :attr:`__cause__`.
787+
reference, as accessible from Python through the
788+
:attr:`~BaseException.__cause__` attribute.
786789
787790
788791
.. c:function:: void PyException_SetCause(PyObject *ex, PyObject *cause)
@@ -791,7 +794,8 @@ Exception Objects
791794
it. There is no type check to make sure that *cause* is either an exception
792795
instance or ``None``. This steals a reference to *cause*.
793796
794-
:attr:`__suppress_context__` is implicitly set to ``True`` by this function.
797+
The :attr:`~BaseException.__suppress_context__` attribute is implicitly set
798+
to ``True`` by this function.
795799
796800
797801
.. c:function:: PyObject* PyException_GetArgs(PyObject *ex)

Doc/library/exceptions.rst

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,36 +38,48 @@ information on defining exceptions is available in the Python Tutorial under
3838
Exception context
3939
-----------------
4040

41-
When raising a new exception while another exception
42-
is already being handled, the new exception's
43-
:attr:`__context__` attribute is automatically set to the handled
44-
exception. An exception may be handled when an :keyword:`except` or
45-
:keyword:`finally` clause, or a :keyword:`with` statement, is used.
46-
47-
This implicit exception context can be
48-
supplemented with an explicit cause by using :keyword:`!from` with
49-
:keyword:`raise`::
50-
51-
raise new_exc from original_exc
52-
53-
The expression following :keyword:`from<raise>` must be an exception or ``None``. It
54-
will be set as :attr:`__cause__` on the raised exception. Setting
55-
:attr:`__cause__` also implicitly sets the :attr:`__suppress_context__`
56-
attribute to ``True``, so that using ``raise new_exc from None``
57-
effectively replaces the old exception with the new one for display
58-
purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while
59-
leaving the old exception available in :attr:`__context__` for introspection
60-
when debugging.
61-
62-
The default traceback display code shows these chained exceptions in
63-
addition to the traceback for the exception itself. An explicitly chained
64-
exception in :attr:`__cause__` is always shown when present. An implicitly
65-
chained exception in :attr:`__context__` is shown only if :attr:`__cause__`
66-
is :const:`None` and :attr:`__suppress_context__` is false.
67-
68-
In either case, the exception itself is always shown after any chained
69-
exceptions so that the final line of the traceback always shows the last
70-
exception that was raised.
41+
.. index:: pair: exception; chaining
42+
__cause__ (exception attribute)
43+
__context__ (exception attribute)
44+
__suppress_context__ (exception attribute)
45+
46+
Three attributes on exception objects provide information about the context in
47+
which an the exception was raised:
48+
49+
.. attribute:: BaseException.__context__
50+
BaseException.__cause__
51+
BaseException.__suppress_context__
52+
53+
When raising a new exception while another exception
54+
is already being handled, the new exception's
55+
:attr:`!__context__` attribute is automatically set to the handled
56+
exception. An exception may be handled when an :keyword:`except` or
57+
:keyword:`finally` clause, or a :keyword:`with` statement, is used.
58+
59+
This implicit exception context can be
60+
supplemented with an explicit cause by using :keyword:`!from` with
61+
:keyword:`raise`::
62+
63+
raise new_exc from original_exc
64+
65+
The expression following :keyword:`from<raise>` must be an exception or ``None``. It
66+
will be set as :attr:`!__cause__` on the raised exception. Setting
67+
:attr:`!__cause__` also implicitly sets the :attr:`!__suppress_context__`
68+
attribute to ``True``, so that using ``raise new_exc from None``
69+
effectively replaces the old exception with the new one for display
70+
purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while
71+
leaving the old exception available in :attr:`!__context__` for introspection
72+
when debugging.
73+
74+
The default traceback display code shows these chained exceptions in
75+
addition to the traceback for the exception itself. An explicitly chained
76+
exception in :attr:`!__cause__` is always shown when present. An implicitly
77+
chained exception in :attr:`!__context__` is shown only if :attr:`!__cause__`
78+
is :const:`None` and :attr:`!__suppress_context__` is false.
79+
80+
In either case, the exception itself is always shown after any chained
81+
exceptions so that the final line of the traceback always shows the last
82+
exception that was raised.
7183

7284

7385
Inheriting from built-in exceptions
@@ -126,6 +138,12 @@ The following exceptions are used mostly as base classes for other exceptions.
126138
tb = sys.exception().__traceback__
127139
raise OtherException(...).with_traceback(tb)
128140

141+
.. attribute:: __traceback__
142+
143+
A writable field that holds the
144+
:ref:`traceback object <traceback-objects>` associated with this
145+
exception. See also: :ref:`raise`.
146+
129147
.. method:: add_note(note)
130148

131149
Add the string ``note`` to the exception's notes which appear in the standard
@@ -929,8 +947,10 @@ their subgroups based on the types of the contained exceptions.
929947
true for the exceptions that should be in the subgroup.
930948

931949
The nesting structure of the current exception is preserved in the result,
932-
as are the values of its :attr:`message`, :attr:`__traceback__`,
933-
:attr:`__cause__`, :attr:`__context__` and :attr:`__notes__` fields.
950+
as are the values of its :attr:`message`,
951+
:attr:`~BaseException.__traceback__`, :attr:`~BaseException.__cause__`,
952+
:attr:`~BaseException.__context__` and
953+
:attr:`~BaseException.__notes__` fields.
934954
Empty nested groups are omitted from the result.
935955

936956
The condition is checked for all exceptions in the nested exception group,
@@ -956,10 +976,14 @@ their subgroups based on the types of the contained exceptions.
956976
and :meth:`split` return instances of the subclass rather
957977
than :exc:`ExceptionGroup`.
958978

959-
:meth:`subgroup` and :meth:`split` copy the :attr:`__traceback__`,
960-
:attr:`__cause__`, :attr:`__context__` and :attr:`__notes__` fields from
979+
:meth:`subgroup` and :meth:`split` copy the
980+
:attr:`~BaseException.__traceback__`,
981+
:attr:`~BaseException.__cause__`, :attr:`~BaseException.__context__` and
982+
:attr:`~BaseException.__notes__` fields from
961983
the original exception group to the one returned by :meth:`derive`, so
962-
these fields do not need to be updated by :meth:`derive`. ::
984+
these fields do not need to be updated by :meth:`derive`.
985+
986+
.. doctest::
963987

964988
>>> class MyGroup(ExceptionGroup):
965989
... def derive(self, excs):

Doc/library/traceback.rst

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ The module defines the following functions:
6767

6868
The optional *limit* argument has the same meaning as for :func:`print_tb`.
6969
If *chain* is true (the default), then chained exceptions (the
70-
:attr:`__cause__` or :attr:`__context__` attributes of the exception) will be
70+
:attr:`~BaseException.__cause__` or :attr:`~BaseException.__context__`
71+
attributes of the exception) will be
7172
printed as well, like the interpreter itself does when printing an unhandled
7273
exception.
7374

@@ -234,10 +235,11 @@ capture data for later printing in a lightweight fashion.
234235
Capture an exception for later rendering. *limit*, *lookup_lines* and
235236
*capture_locals* are as for the :class:`StackSummary` class.
236237

237-
If *compact* is true, only data that is required by :class:`TracebackException`'s
238-
``format`` method is saved in the class attributes. In particular, the
239-
``__context__`` field is calculated only if ``__cause__`` is ``None`` and
240-
``__suppress_context__`` is false.
238+
If *compact* is true, only data that is required by
239+
:class:`!TracebackException`'s :meth:`format` method
240+
is saved in the class attributes. In particular, the
241+
:attr:`__context__` field is calculated only if :attr:`__cause__` is
242+
``None`` and :attr:`__suppress_context__` is false.
241243

242244
Note that when locals are captured, they are also shown in the traceback.
243245

@@ -255,27 +257,31 @@ capture data for later printing in a lightweight fashion.
255257

256258
.. attribute:: __cause__
257259

258-
A :class:`TracebackException` of the original ``__cause__``.
260+
A :class:`!TracebackException` of the original
261+
:attr:`~BaseException.__cause__`.
259262

260263
.. attribute:: __context__
261264

262-
A :class:`TracebackException` of the original ``__context__``.
265+
A :class:`!TracebackException` of the original
266+
:attr:`~BaseException.__context__`.
263267

264268
.. attribute:: exceptions
265269

266270
If ``self`` represents an :exc:`ExceptionGroup`, this field holds a list of
267-
:class:`TracebackException` instances representing the nested exceptions.
271+
:class:`!TracebackException` instances representing the nested exceptions.
268272
Otherwise it is ``None``.
269273

270274
.. versionadded:: 3.11
271275

272276
.. attribute:: __suppress_context__
273277

274-
The ``__suppress_context__`` value from the original exception.
278+
The :attr:`~BaseException.__suppress_context__` value from the original
279+
exception.
275280

276281
.. attribute:: __notes__
277282

278-
The ``__notes__`` value from the original exception, or ``None``
283+
The :attr:`~BaseException.__notes__` value from the original exception,
284+
or ``None``
279285
if the exception does not have any notes. If it is not ``None``
280286
is it formatted in the traceback after the exception string.
281287

@@ -349,8 +355,8 @@ capture data for later printing in a lightweight fashion.
349355

350356
Format the exception.
351357

352-
If *chain* is not ``True``, ``__cause__`` and ``__context__`` will not
353-
be formatted.
358+
If *chain* is not ``True``, :attr:`__cause__` and :attr:`__context__`
359+
will not be formatted.
354360

355361
The return value is a generator of strings, each ending in a newline and
356362
some containing internal newlines. :func:`~traceback.print_exception`

Doc/reference/datamodel.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,8 @@ unwinds the execution stack, at each unwound level a traceback object is
13901390
inserted in front of the current traceback. When an exception handler is
13911391
entered, the stack trace is made available to the program. (See section
13921392
:ref:`try`.) It is accessible as the third item of the
1393-
tuple returned by :func:`sys.exc_info`, and as the ``__traceback__`` attribute
1393+
tuple returned by :func:`sys.exc_info`, and as the
1394+
:attr:`~BaseException.__traceback__` attribute
13941395
of the caught exception.
13951396

13961397
When the program contains no suitable

Doc/reference/simple_stmts.rst

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ The :dfn:`type` of the exception is the exception instance's class, the
578578
.. index:: pair: object; traceback
579579

580580
A traceback object is normally created automatically when an exception is raised
581-
and attached to it as the :attr:`__traceback__` attribute, which is writable.
581+
and attached to it as the :attr:`~BaseException.__traceback__` attribute.
582582
You can create an exception and set your own traceback in one step using the
583583
:meth:`~BaseException.with_traceback` exception method (which returns the
584584
same exception instance, with its traceback set to its argument), like so::
@@ -592,11 +592,13 @@ same exception instance, with its traceback set to its argument), like so::
592592
The ``from`` clause is used for exception chaining: if given, the second
593593
*expression* must be another exception class or instance. If the second
594594
expression is an exception instance, it will be attached to the raised
595-
exception as the :attr:`__cause__` attribute (which is writable). If the
595+
exception as the :attr:`~BaseException.__cause__` attribute (which is writable). If the
596596
expression is an exception class, the class will be instantiated and the
597597
resulting exception instance will be attached to the raised exception as the
598-
:attr:`__cause__` attribute. If the raised exception is not handled, both
599-
exceptions will be printed::
598+
:attr:`!__cause__` attribute. If the raised exception is not handled, both
599+
exceptions will be printed:
600+
601+
.. code-block:: pycon
600602
601603
>>> try:
602604
... print(1 / 0)
@@ -605,19 +607,24 @@ exceptions will be printed::
605607
...
606608
Traceback (most recent call last):
607609
File "<stdin>", line 2, in <module>
610+
print(1 / 0)
611+
~~^~~
608612
ZeroDivisionError: division by zero
609613
610614
The above exception was the direct cause of the following exception:
611615
612616
Traceback (most recent call last):
613617
File "<stdin>", line 4, in <module>
618+
raise RuntimeError("Something bad happened") from exc
614619
RuntimeError: Something bad happened
615620
616621
A similar mechanism works implicitly if a new exception is raised when
617622
an exception is already being handled. An exception may be handled
618623
when an :keyword:`except` or :keyword:`finally` clause, or a
619624
:keyword:`with` statement, is used. The previous exception is then
620-
attached as the new exception's :attr:`__context__` attribute::
625+
attached as the new exception's :attr:`~BaseException.__context__` attribute:
626+
627+
.. code-block:: pycon
621628
622629
>>> try:
623630
... print(1 / 0)
@@ -626,16 +633,21 @@ attached as the new exception's :attr:`__context__` attribute::
626633
...
627634
Traceback (most recent call last):
628635
File "<stdin>", line 2, in <module>
636+
print(1 / 0)
637+
~~^~~
629638
ZeroDivisionError: division by zero
630639
631640
During handling of the above exception, another exception occurred:
632641
633642
Traceback (most recent call last):
634643
File "<stdin>", line 4, in <module>
644+
raise RuntimeError("Something bad happened")
635645
RuntimeError: Something bad happened
636646
637647
Exception chaining can be explicitly suppressed by specifying :const:`None` in
638-
the ``from`` clause::
648+
the ``from`` clause:
649+
650+
.. doctest::
639651

640652
>>> try:
641653
... print(1 / 0)
@@ -653,8 +665,8 @@ and information about handling exceptions is in section :ref:`try`.
653665
:const:`None` is now permitted as ``Y`` in ``raise X from Y``.
654666

655667
.. versionadded:: 3.3
656-
The ``__suppress_context__`` attribute to suppress automatic display of the
657-
exception context.
668+
The :attr:`~BaseException.__suppress_context__` attribute to suppress
669+
automatic display of the exception context.
658670

659671
.. versionchanged:: 3.11
660672
If the traceback of the active exception is modified in an :keyword:`except`

Doc/whatsnew/3.0.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ new powerful features added:
711711
{Exception}({args})` instead of :samp:`raise {Exception}, {args}`.
712712
Additionally, you can no longer explicitly specify a traceback;
713713
instead, if you *have* to do this, you can assign directly to the
714-
:attr:`__traceback__` attribute (see below).
714+
:attr:`~BaseException.__traceback__` attribute (see below).
715715

716716
* :pep:`3110`: Catching exceptions. You must now use
717717
:samp:`except {SomeException} as {variable}` instead
@@ -725,22 +725,23 @@ new powerful features added:
725725
handler block. This usually happens due to a bug in the handler
726726
block; we call this a *secondary* exception. In this case, the
727727
original exception (that was being handled) is saved as the
728-
:attr:`__context__` attribute of the secondary exception.
728+
:attr:`~BaseException.__context__` attribute of the secondary exception.
729729
Explicit chaining is invoked with this syntax::
730730

731731
raise SecondaryException() from primary_exception
732732

733733
(where *primary_exception* is any expression that produces an
734734
exception object, probably an exception that was previously caught).
735735
In this case, the primary exception is stored on the
736-
:attr:`__cause__` attribute of the secondary exception. The
736+
:attr:`~BaseException.__cause__` attribute of the secondary exception. The
737737
traceback printed when an unhandled exception occurs walks the chain
738-
of :attr:`__cause__` and :attr:`__context__` attributes and prints a
738+
of :attr:`!__cause__` and :attr:`~BaseException.__context__` attributes and
739+
prints a
739740
separate traceback for each component of the chain, with the primary
740741
exception at the top. (Java users may recognize this behavior.)
741742

742743
* :pep:`3134`: Exception objects now store their traceback as the
743-
:attr:`__traceback__` attribute. This means that an exception
744+
:attr:`~BaseException.__traceback__` attribute. This means that an exception
744745
object now contains all the information pertaining to an exception,
745746
and there are fewer reasons to use :func:`sys.exc_info` (though the
746747
latter is not removed).

0 commit comments

Comments
 (0)