[Python-checkins] gh-90449: Improve accuracy and readability of exceptions tutorial (GH-31899)

iritkatriel webhook-mailer at python.org
Wed Apr 13 16:45:58 EDT 2022


https://github.com/python/cpython/commit/04f9658c591f61f0d45ab1a818840a4c4ef82f64
commit: 04f9658c591f61f0d45ab1a818840a4c4ef82f64
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com>
date: 2022-04-13T21:45:33+01:00
summary:

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

files:
M Doc/tutorial/errors.rst

diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst
index 888740cdd0f19..01fd76283556b 100644
--- a/Doc/tutorial/errors.rst
+++ b/Doc/tutorial/errors.rst
@@ -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:



More information about the Python-checkins mailing list