gh-90449: Improve accuracy and readability of exceptions tutorial (GH-31899)

This commit is contained in:
Irit Katriel 2022-04-13 21:45:33 +01:00 committed by GitHub
parent 3fc57e8f6f
commit 04f9658c59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 54 additions and 46 deletions

View File

@ -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
@ -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.
@ -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():
@ -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()'
@ -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: