Skip to content

Commit

Permalink
gh-90449: Improve accuracy and readability of exceptions tutorial (GH…
Browse files Browse the repository at this point in the history
  • Loading branch information
iritkatriel committed Apr 13, 2022
1 parent 3fc57e8 commit 04f9658
Showing 1 changed file with 54 additions and 46 deletions.
100 changes: 54 additions & 46 deletions Doc/tutorial/errors.rst
Expand Up @@ -147,10 +147,52 @@ For example, the following code will print B, C, D in that order::
Note that if the *except clauses* were reversed (with ``except B`` first), it
would have printed B, B, B --- the first matching *except clause* is triggered.

All exceptions inherit from :exc:`BaseException`, and so it can be used to serve
as a wildcard. Use this with extreme caution, since it is easy to mask a real
programming error in this way! It can also be used to print an error message and
then re-raise the exception (allowing a caller to handle the exception as well)::
When an exception occurs, it may have associated values, also known as the
exception's *arguments*. The presence and types of the arguments depend on the
exception type.

The *except clause* may specify a variable after the exception name. The
variable is bound to the exception instance which typically has an ``args``
attribute that stores the arguments. For convenience, builtin exception
types define :meth:`__str__` to print all the arguments without explicitly
accessing ``.args``. ::

>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst)) # the exception instance
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed directly,
... # but may be overridden in exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

The exception's :meth:`__str__` output is printed as the last part ('detail')
of the message for unhandled exceptions.

:exc:`BaseException` is the common base class of all exceptions. One of its
subclasses, :exc:`Exception`, is the base class of all the non-fatal exceptions.
Exceptions which are not subclasses of :exc:`Exception` are not typically
handled, because they are used to indicate that the program should terminate.
They include :exc:`SystemExit` which is raised by :meth:`sys.exit` and
:exc:`KeyboardInterrupt` which is raised when a user wishes to interrupt
the program.

:exc:`Exception` can be used as a wildcard that catches (almost) everything.
However, it is good practice to be as specific as possible with the types
of exceptions that we intend to handle, and to allow any unexpected
exceptions to propagate on.

The most common pattern for handling :exc:`Exception` is to print or log
the exception and then re-raise it (allowing a caller to handle the
exception as well)::

import sys

Expand All @@ -159,16 +201,13 @@ then re-raise the exception (allowing a caller to handle the exception as well):
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
print("OS error:", err)
except ValueError:
print("Could not convert data to an integer.")
except BaseException as err:
except Exception as err:
print(f"Unexpected {err=}, {type(err)=}")
raise

Alternatively the last except clause may omit the exception name(s), however the exception
value must then be retrieved with ``sys.exception()``.

The :keyword:`try` ... :keyword:`except` statement has an optional *else
clause*, which, when present, must follow all *except clauses*. It is useful
for code that must be executed if the *try clause* does not raise an exception.
Expand All @@ -188,39 +227,8 @@ the :keyword:`try` clause because it avoids accidentally catching an exception
that wasn't raised by the code being protected by the :keyword:`!try` ...
:keyword:`!except` statement.

When an exception occurs, it may have an associated value, also known as the
exception's *argument*. The presence and type of the argument depend on the
exception type.

The *except clause* may specify a variable after the exception name. The
variable is bound to an exception instance with the arguments stored in
``instance.args``. For convenience, the exception instance defines
:meth:`__str__` so the arguments can be printed directly without having to
reference ``.args``. One may also instantiate an exception first before
raising it and add any attributes to it as desired. ::

>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst)) # the exception instance
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed directly,
... # but may be overridden in exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

If an exception has arguments, they are printed as the last part ('detail') of
the message for unhandled exceptions.

Exception handlers don't just handle exceptions if they occur immediately in the
*try clause*, but also if they occur inside functions that are called (even
Exception handlers do not handle only exceptions that occur immediately in the
*try clause*, but also those that occur inside functions that are called (even
indirectly) in the *try clause*. For example::

>>> def this_fails():
Expand Down Expand Up @@ -249,8 +257,9 @@ exception to occur. For example::

The sole argument to :keyword:`raise` indicates the exception to be raised.
This must be either an exception instance or an exception class (a class that
derives from :class:`Exception`). If an exception class is passed, it will
be implicitly instantiated by calling its constructor with no arguments::
derives from :class:`BaseException`, such as :exc:`Exception` or one of its
subclasses). If an exception class is passed, it will be implicitly
instantiated by calling its constructor with no arguments::

raise ValueError # shorthand for 'raise ValueError()'

Expand Down Expand Up @@ -335,8 +344,7 @@ Most exceptions are defined with names that end in "Error", similar to the
naming of the standard exceptions.

Many standard modules define their own exceptions to report errors that may
occur in functions they define. More information on classes is presented in
chapter :ref:`tut-classes`.
occur in functions they define.


.. _tut-cleanup:
Expand Down

0 comments on commit 04f9658

Please sign in to comment.