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
This commit is contained in:
Alex Waygood 2023-12-13 18:59:36 +00:00 committed by GitHub
parent 2111795d0c
commit d05a180350
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 62 deletions

View File

@ -541,7 +541,8 @@ Querying the error indicator
.. note::
This function *does not* implicitly set the ``__traceback__``
This function *does not* implicitly set the
:attr:`~BaseException.__traceback__`
attribute on the exception value. If setting the traceback
appropriately is desired, the following additional snippet is needed::
@ -753,7 +754,8 @@ Exception Objects
.. c:function:: PyObject* PyException_GetTraceback(PyObject *ex)
Return the traceback associated with the exception as a new reference, as
accessible from Python through :attr:`__traceback__`. If there is no
accessible from Python through the :attr:`~BaseException.__traceback__`
attribute. If there is no
traceback associated, this returns ``NULL``.
@ -767,8 +769,8 @@ Exception Objects
Return the context (another exception instance during whose handling *ex* was
raised) associated with the exception as a new reference, as accessible from
Python through :attr:`__context__`. If there is no context associated, this
returns ``NULL``.
Python through the :attr:`~BaseException.__context__` attribute.
If there is no context associated, this returns ``NULL``.
.. c:function:: void PyException_SetContext(PyObject *ex, PyObject *ctx)
@ -782,7 +784,8 @@ Exception Objects
Return the cause (either an exception instance, or ``None``,
set by ``raise ... from ...``) associated with the exception as a new
reference, as accessible from Python through :attr:`__cause__`.
reference, as accessible from Python through the
:attr:`~BaseException.__cause__` attribute.
.. c:function:: void PyException_SetCause(PyObject *ex, PyObject *cause)
@ -791,7 +794,8 @@ Exception Objects
it. There is no type check to make sure that *cause* is either an exception
instance or ``None``. This steals a reference to *cause*.
:attr:`__suppress_context__` is implicitly set to ``True`` by this function.
The :attr:`~BaseException.__suppress_context__` attribute is implicitly set
to ``True`` by this function.
.. c:function:: PyObject* PyException_GetArgs(PyObject *ex)

View File

@ -38,36 +38,48 @@ information on defining exceptions is available in the Python Tutorial under
Exception context
-----------------
When raising a new exception while another exception
is already being handled, the new exception's
:attr:`__context__` attribute is automatically set to the handled
exception. An exception may be handled when an :keyword:`except` or
:keyword:`finally` clause, or a :keyword:`with` statement, is used.
.. index:: pair: exception; chaining
__cause__ (exception attribute)
__context__ (exception attribute)
__suppress_context__ (exception attribute)
This implicit exception context can be
supplemented with an explicit cause by using :keyword:`!from` with
:keyword:`raise`::
Three attributes on exception objects provide information about the context in
which an the exception was raised:
raise new_exc from original_exc
.. attribute:: BaseException.__context__
BaseException.__cause__
BaseException.__suppress_context__
The expression following :keyword:`from<raise>` must be an exception or ``None``. It
will be set as :attr:`__cause__` on the raised exception. Setting
:attr:`__cause__` also implicitly sets the :attr:`__suppress_context__`
attribute to ``True``, so that using ``raise new_exc from None``
effectively replaces the old exception with the new one for display
purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while
leaving the old exception available in :attr:`__context__` for introspection
when debugging.
When raising a new exception while another exception
is already being handled, the new exception's
:attr:`!__context__` attribute is automatically set to the handled
exception. An exception may be handled when an :keyword:`except` or
:keyword:`finally` clause, or a :keyword:`with` statement, is used.
The default traceback display code shows these chained exceptions in
addition to the traceback for the exception itself. An explicitly chained
exception in :attr:`__cause__` is always shown when present. An implicitly
chained exception in :attr:`__context__` is shown only if :attr:`__cause__`
is :const:`None` and :attr:`__suppress_context__` is false.
This implicit exception context can be
supplemented with an explicit cause by using :keyword:`!from` with
:keyword:`raise`::
In either case, the exception itself is always shown after any chained
exceptions so that the final line of the traceback always shows the last
exception that was raised.
raise new_exc from original_exc
The expression following :keyword:`from<raise>` must be an exception or ``None``. It
will be set as :attr:`!__cause__` on the raised exception. Setting
:attr:`!__cause__` also implicitly sets the :attr:`!__suppress_context__`
attribute to ``True``, so that using ``raise new_exc from None``
effectively replaces the old exception with the new one for display
purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while
leaving the old exception available in :attr:`!__context__` for introspection
when debugging.
The default traceback display code shows these chained exceptions in
addition to the traceback for the exception itself. An explicitly chained
exception in :attr:`!__cause__` is always shown when present. An implicitly
chained exception in :attr:`!__context__` is shown only if :attr:`!__cause__`
is :const:`None` and :attr:`!__suppress_context__` is false.
In either case, the exception itself is always shown after any chained
exceptions so that the final line of the traceback always shows the last
exception that was raised.
Inheriting from built-in exceptions
@ -126,6 +138,12 @@ The following exceptions are used mostly as base classes for other exceptions.
tb = sys.exception().__traceback__
raise OtherException(...).with_traceback(tb)
.. attribute:: __traceback__
A writable field that holds the
:ref:`traceback object <traceback-objects>` associated with this
exception. See also: :ref:`raise`.
.. method:: add_note(note)
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.
true for the exceptions that should be in the subgroup.
The nesting structure of the current exception is preserved in the result,
as are the values of its :attr:`message`, :attr:`__traceback__`,
:attr:`__cause__`, :attr:`__context__` and :attr:`__notes__` fields.
as are the values of its :attr:`message`,
:attr:`~BaseException.__traceback__`, :attr:`~BaseException.__cause__`,
:attr:`~BaseException.__context__` and
:attr:`~BaseException.__notes__` fields.
Empty nested groups are omitted from the result.
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.
and :meth:`split` return instances of the subclass rather
than :exc:`ExceptionGroup`.
:meth:`subgroup` and :meth:`split` copy the :attr:`__traceback__`,
:attr:`__cause__`, :attr:`__context__` and :attr:`__notes__` fields from
:meth:`subgroup` and :meth:`split` copy the
:attr:`~BaseException.__traceback__`,
:attr:`~BaseException.__cause__`, :attr:`~BaseException.__context__` and
:attr:`~BaseException.__notes__` fields from
the original exception group to the one returned by :meth:`derive`, so
these fields do not need to be updated by :meth:`derive`. ::
these fields do not need to be updated by :meth:`derive`.
.. doctest::
>>> class MyGroup(ExceptionGroup):
... def derive(self, excs):

View File

@ -67,7 +67,8 @@ The module defines the following functions:
The optional *limit* argument has the same meaning as for :func:`print_tb`.
If *chain* is true (the default), then chained exceptions (the
:attr:`__cause__` or :attr:`__context__` attributes of the exception) will be
:attr:`~BaseException.__cause__` or :attr:`~BaseException.__context__`
attributes of the exception) will be
printed as well, like the interpreter itself does when printing an unhandled
exception.
@ -234,10 +235,11 @@ capture data for later printing in a lightweight fashion.
Capture an exception for later rendering. *limit*, *lookup_lines* and
*capture_locals* are as for the :class:`StackSummary` class.
If *compact* is true, only data that is required by :class:`TracebackException`'s
``format`` method is saved in the class attributes. In particular, the
``__context__`` field is calculated only if ``__cause__`` is ``None`` and
``__suppress_context__`` is false.
If *compact* is true, only data that is required by
:class:`!TracebackException`'s :meth:`format` method
is saved in the class attributes. In particular, the
:attr:`__context__` field is calculated only if :attr:`__cause__` is
``None`` and :attr:`__suppress_context__` is false.
Note that when locals are captured, they are also shown in the traceback.
@ -255,27 +257,31 @@ capture data for later printing in a lightweight fashion.
.. attribute:: __cause__
A :class:`TracebackException` of the original ``__cause__``.
A :class:`!TracebackException` of the original
:attr:`~BaseException.__cause__`.
.. attribute:: __context__
A :class:`TracebackException` of the original ``__context__``.
A :class:`!TracebackException` of the original
:attr:`~BaseException.__context__`.
.. attribute:: exceptions
If ``self`` represents an :exc:`ExceptionGroup`, this field holds a list of
:class:`TracebackException` instances representing the nested exceptions.
:class:`!TracebackException` instances representing the nested exceptions.
Otherwise it is ``None``.
.. versionadded:: 3.11
.. attribute:: __suppress_context__
The ``__suppress_context__`` value from the original exception.
The :attr:`~BaseException.__suppress_context__` value from the original
exception.
.. attribute:: __notes__
The ``__notes__`` value from the original exception, or ``None``
The :attr:`~BaseException.__notes__` value from the original exception,
or ``None``
if the exception does not have any notes. If it is not ``None``
is it formatted in the traceback after the exception string.
@ -349,8 +355,8 @@ capture data for later printing in a lightweight fashion.
Format the exception.
If *chain* is not ``True``, ``__cause__`` and ``__context__`` will not
be formatted.
If *chain* is not ``True``, :attr:`__cause__` and :attr:`__context__`
will not be formatted.
The return value is a generator of strings, each ending in a newline and
some containing internal newlines. :func:`~traceback.print_exception`

View File

@ -1390,7 +1390,8 @@ unwinds the execution stack, at each unwound level a traceback object is
inserted in front of the current traceback. When an exception handler is
entered, the stack trace is made available to the program. (See section
:ref:`try`.) It is accessible as the third item of the
tuple returned by :func:`sys.exc_info`, and as the ``__traceback__`` attribute
tuple returned by :func:`sys.exc_info`, and as the
:attr:`~BaseException.__traceback__` attribute
of the caught exception.
When the program contains no suitable

View File

@ -578,7 +578,7 @@ The :dfn:`type` of the exception is the exception instance's class, the
.. index:: pair: object; traceback
A traceback object is normally created automatically when an exception is raised
and attached to it as the :attr:`__traceback__` attribute, which is writable.
and attached to it as the :attr:`~BaseException.__traceback__` attribute.
You can create an exception and set your own traceback in one step using the
:meth:`~BaseException.with_traceback` exception method (which returns the
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::
The ``from`` clause is used for exception chaining: if given, the second
*expression* must be another exception class or instance. If the second
expression is an exception instance, it will be attached to the raised
exception as the :attr:`__cause__` attribute (which is writable). If the
exception as the :attr:`~BaseException.__cause__` attribute (which is writable). If the
expression is an exception class, the class will be instantiated and the
resulting exception instance will be attached to the raised exception as the
:attr:`__cause__` attribute. If the raised exception is not handled, both
exceptions will be printed::
:attr:`!__cause__` attribute. If the raised exception is not handled, both
exceptions will be printed:
.. code-block:: pycon
>>> try:
... print(1 / 0)
@ -605,19 +607,24 @@ exceptions will be printed::
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
print(1 / 0)
~~^~~
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened
A similar mechanism works implicitly if a new exception is raised when
an exception is already being handled. An exception may be handled
when an :keyword:`except` or :keyword:`finally` clause, or a
:keyword:`with` statement, is used. The previous exception is then
attached as the new exception's :attr:`__context__` attribute::
attached as the new exception's :attr:`~BaseException.__context__` attribute:
.. code-block:: pycon
>>> try:
... print(1 / 0)
@ -626,16 +633,21 @@ attached as the new exception's :attr:`__context__` attribute::
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
print(1 / 0)
~~^~~
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened
Exception chaining can be explicitly suppressed by specifying :const:`None` in
the ``from`` clause::
the ``from`` clause:
.. doctest::
>>> try:
... print(1 / 0)
@ -653,8 +665,8 @@ and information about handling exceptions is in section :ref:`try`.
:const:`None` is now permitted as ``Y`` in ``raise X from Y``.
.. versionadded:: 3.3
The ``__suppress_context__`` attribute to suppress automatic display of the
exception context.
The :attr:`~BaseException.__suppress_context__` attribute to suppress
automatic display of the exception context.
.. versionchanged:: 3.11
If the traceback of the active exception is modified in an :keyword:`except`

View File

@ -711,7 +711,7 @@ new powerful features added:
{Exception}({args})` instead of :samp:`raise {Exception}, {args}`.
Additionally, you can no longer explicitly specify a traceback;
instead, if you *have* to do this, you can assign directly to the
:attr:`__traceback__` attribute (see below).
:attr:`~BaseException.__traceback__` attribute (see below).
* :pep:`3110`: Catching exceptions. You must now use
:samp:`except {SomeException} as {variable}` instead
@ -725,7 +725,7 @@ new powerful features added:
handler block. This usually happens due to a bug in the handler
block; we call this a *secondary* exception. In this case, the
original exception (that was being handled) is saved as the
:attr:`__context__` attribute of the secondary exception.
:attr:`~BaseException.__context__` attribute of the secondary exception.
Explicit chaining is invoked with this syntax::
raise SecondaryException() from primary_exception
@ -733,14 +733,15 @@ new powerful features added:
(where *primary_exception* is any expression that produces an
exception object, probably an exception that was previously caught).
In this case, the primary exception is stored on the
:attr:`__cause__` attribute of the secondary exception. The
:attr:`~BaseException.__cause__` attribute of the secondary exception. The
traceback printed when an unhandled exception occurs walks the chain
of :attr:`__cause__` and :attr:`__context__` attributes and prints a
of :attr:`!__cause__` and :attr:`~BaseException.__context__` attributes and
prints a
separate traceback for each component of the chain, with the primary
exception at the top. (Java users may recognize this behavior.)
* :pep:`3134`: Exception objects now store their traceback as the
:attr:`__traceback__` attribute. This means that an exception
:attr:`~BaseException.__traceback__` attribute. This means that an exception
object now contains all the information pertaining to an exception,
and there are fewer reasons to use :func:`sys.exc_info` (though the
latter is not removed).