314 lines
11 KiB
Plaintext
314 lines
11 KiB
Plaintext
PEP: 352
|
||
Title: Required Superclass for Exceptions
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Brett Cannon <brett@python.org>
|
||
Guido van Rossum <guido@python.org>
|
||
Status: Final
|
||
Type: Standards Track
|
||
Content-Type: text/x-rst
|
||
Created: 27-Oct-2005
|
||
Post-History:
|
||
|
||
|
||
Abstract
|
||
========
|
||
|
||
In Python 2.4 and before, any (classic) class can be raised as an
|
||
exception. The plan is to allow new-style classes starting in Python
|
||
2.5, but this makes the problem worse -- it would mean *any* class (or
|
||
instance) can be raised! (This is not the case in the final version;
|
||
only built-in exceptions can be new-style which means you need to
|
||
inherit from a built-in exception to have user-defined exceptions also
|
||
be new-style) This is a problem because it
|
||
prevents any guarantees from being made about the interface of exceptions.
|
||
This PEP proposes introducing a new superclass that all raised objects
|
||
must inherit from. Imposing the restriction will allow a standard
|
||
interface for exceptions to exist that can be relied upon.
|
||
|
||
One might counter that requiring a specific base class for a
|
||
particular interface is unPythonic. However, in the specific case of
|
||
exceptions there's a good reason (which has generally been agreed to
|
||
on python-dev): requiring hierarchy helps code that wants to *catch*
|
||
exceptions by making it possible to catch *all* exceptions explicitly
|
||
by writing ``except BaseException:`` instead of
|
||
``except *:``. [#hierarchy-good]_
|
||
|
||
Introducing a new superclass for exceptions also gives us the chance
|
||
to rearrange the exception hierarchy slightly for the better. As it
|
||
currently stands, all exceptions in the built-in namespace inherit
|
||
from Exception. This is a problem since this includes two exceptions
|
||
(KeyboardInterrupt and SystemExit) that often need to be excepted from
|
||
the application's exception handling: the default behavior of shutting
|
||
the interpreter down with resp. Without a traceback is usually more
|
||
desirable than whatever the application might do (with the possible
|
||
exception of applications that emulate Python's interactive command
|
||
loop with ``>>>`` prompt). Changing it so that these two exceptions
|
||
inherit from the common superclass instead of Exception will make it
|
||
easy for people to write ``except`` clauses that are not overreaching
|
||
and not catch exceptions that should propagate up.
|
||
|
||
This PEP is based on previous work done for PEP 348 [#pep348]_.
|
||
|
||
|
||
Requiring a Common Superclass
|
||
=============================
|
||
|
||
This PEP proposes introducing a new exception named BaseException that
|
||
is a new-style class and has a single attribute, ``message`` (that
|
||
will cause the deprecation of the existing ``args`` attribute) Below
|
||
is the code as the exception will work in Python 3.0 (how it will
|
||
work in Python 2.x is covered in the `Transition Plan`_ section)::
|
||
|
||
class BaseException(object):
|
||
|
||
"""Superclass representing the base of the exception hierarchy.
|
||
|
||
Provides a 'message' attribute that contains either the single
|
||
argument to the constructor or the empty string. This attribute
|
||
is used in the string representation for the
|
||
exception. This is so that it provides the extra details in the
|
||
traceback.
|
||
|
||
"""
|
||
|
||
def __init__(self, message=''):
|
||
"""Set the 'message' attribute'"""
|
||
self.message = message
|
||
|
||
def __str__(self):
|
||
"""Return the str of 'message'"""
|
||
return str(self.message)
|
||
|
||
def __repr__(self):
|
||
return "%s(%s)" % (self.__class__.__name__, repr(self.message))
|
||
|
||
The ``message`` attribute will contain either the first argument
|
||
passed in at instantiation of the object or the empty string if no
|
||
arguments were passed in. The attribute is meant to act as a common
|
||
location to store any message that is to be passed along
|
||
with the exception that goes beyond the location of where the exception
|
||
occurred and the exception's type.
|
||
|
||
No restriction is placed upon what may be passed in for ``message``
|
||
for backwards-compatibility reasons. In practice, though, only
|
||
strings should be used. This keeps the string representation of the
|
||
exception to be a useful message about the exception that is
|
||
human-readable. Including programmatic information (e.g., an error
|
||
code number) should be stored as a separate attribute in a subclass.
|
||
|
||
The ``args`` attribute is deprecated. While allowing multiple
|
||
arguments to be passed can be helpful, it is in no way essential. It
|
||
also does not make it clear which argument is going to be represented
|
||
by the ``__str__`` method. Restricting initialization to accepting a
|
||
single argument keeps the API simple and clear. This also means
|
||
providing a ``__getitem__`` method is unneeded for exceptions and thus
|
||
will be deprecated as well.
|
||
|
||
The ``raise`` statement will be changed to require that any object
|
||
passed to it must inherit from BaseException. This will make sure
|
||
that all exceptions fall within a single hierarchy that is anchored at
|
||
BaseException [#hierarchy-good]_. This also guarantees a basic
|
||
interface that is inherited from BaseException. The change to
|
||
``raise`` will be enforced starting in Python 3.0 (see the `Transition
|
||
Plan`_ below).
|
||
|
||
With BaseException being the root of the exception hierarchy,
|
||
Exception will now inherit from it.
|
||
|
||
|
||
Exception Hierarchy Changes
|
||
===========================
|
||
|
||
With the exception hierarchy now even more important since it has a
|
||
basic root, a change to the existing hierarchy is called for. As it
|
||
stands now, if one wants to catch all exceptions that signal an error
|
||
*and* do not mean the interpreter should be allowed to exit, you must
|
||
specify all but two exceptions specifically in an ``except`` clause
|
||
or catch the two exceptions separately and then re-raise them and
|
||
have all other exceptions fall through to a bare ``except`` clause::
|
||
|
||
except (KeyboardInterrupt, SystemExit):
|
||
raise
|
||
except:
|
||
...
|
||
|
||
That is needlessly explicit. This PEP proposes moving
|
||
KeyboardInterrupt and SystemExit to inherit directly from
|
||
BaseException.
|
||
|
||
::
|
||
|
||
- BaseException
|
||
|- KeyboardInterrupt
|
||
|- SystemExit
|
||
|- Exception
|
||
|- (all other current built-in exceptions)
|
||
|
||
Doing this makes catching Exception more reasonable. It would catch
|
||
only exceptions that signify errors. Exceptions that signal that the
|
||
interpreter should exit will not be caught and thus be allowed to
|
||
propagate up and allow the interpreter to terminate.
|
||
|
||
KeyboardInterrupt has been moved since users typically expect an
|
||
application to exit when the press the interrupt key (usually Ctrl-C).
|
||
If people have overly broad ``except`` clauses the expected behaviour
|
||
does not occur.
|
||
|
||
SystemExit has been moved for similar reasons. Since the exception is
|
||
raised when ``sys.exit()`` is called the interpreter should normally
|
||
be allowed to terminate. Unfortunately overly broad ``except``
|
||
clauses can prevent the explicitly requested exit from occurring.
|
||
|
||
To make sure that people catch Exception most of the time, various
|
||
parts of the documentation and tutorials will need to be updated to
|
||
strongly suggest that Exception be what programmers want to use. Bare
|
||
``except`` clauses or catching BaseException directly should be
|
||
discouraged based on the fact that KeyboardInterrupt and SystemExit
|
||
almost always should be allowed to propagate up.
|
||
|
||
|
||
Transition Plan
|
||
===============
|
||
|
||
Since semantic changes to Python are being proposed, a transition plan
|
||
is needed. The goal is to end up with the new semantics being used in
|
||
Python 3.0 while providing a smooth transition for 2.x code. All
|
||
deprecations mentioned in the plan will lead to the removal of the
|
||
semantics starting in the version following the initial deprecation.
|
||
|
||
Here is BaseException as implemented in the 2.x series::
|
||
|
||
class BaseException(object):
|
||
|
||
"""Superclass representing the base of the exception hierarchy.
|
||
|
||
Provides a 'message' attribute that contains any single argument
|
||
passed in during instantiation. If more than one argument is
|
||
passed, it is set to the empty string. It is meant to represent
|
||
any message (usually some text) that should be printed out with
|
||
the traceback. Unfortunately, for backwards-compatibility, the
|
||
'args' attribute (discussed below) is used for printing out to
|
||
tracebacks.
|
||
|
||
The 'args' attribute and __getitem__ method are provided for
|
||
backwards-compatibility and will be deprecated at some point.
|
||
|
||
"""
|
||
|
||
def __init__(self, *args):
|
||
"""Set 'message' and 'args' attribute.
|
||
|
||
'args' will eventually be deprecated. But it is still used
|
||
when printing out tracebacks for backwards-compatibility.
|
||
Once 'args' is removed, though, 'message' will be used instead.
|
||
|
||
"""
|
||
self.args = args
|
||
self.message = args[0] if args else ''
|
||
|
||
def __str__(self):
|
||
"""Return the str of args[0] or args, depending on length.
|
||
|
||
Once 'args' has been removed, 'message' will be used
|
||
exclusively for the str representation for exceptions.
|
||
|
||
"""
|
||
return str(self.args[0]
|
||
if len(self.args) <= 1
|
||
else self.args)
|
||
|
||
def __repr__(self):
|
||
func_args = repr(self.args) if self.args else "()"
|
||
return self.__class__.__name__ + func_args
|
||
|
||
def __getitem__(self, index):
|
||
"""Index into arguments passed in during instantiation.
|
||
|
||
Provided for backwards-compatibility and will be
|
||
deprecated.
|
||
|
||
"""
|
||
return self.args[index]
|
||
|
||
|
||
Deprecation of features in Python 2.9 is optional. This is because it
|
||
is not known at this time if Python 2.9 (which is slated to be the
|
||
last version in the 2.x series) will actively deprecate features that
|
||
will not be in 3.0 . It is conceivable that no deprecation warnings
|
||
will be used in 2.9 since there could be such a difference between 2.9
|
||
and 3.0 that it would make 2.9 too "noisy" in terms of warnings. Thus
|
||
the proposed deprecation warnings for Python 2.9 will be revisited
|
||
when development of that version begins to determine if they are still
|
||
desired.
|
||
|
||
* Python 2.5
|
||
|
||
- all standard exceptions become new-style classes
|
||
|
||
- introduce BaseException
|
||
|
||
- Exception, KeyboardInterrupt, and SystemExit inherit from BaseException
|
||
|
||
- deprecate raising string exceptions
|
||
|
||
* Python 2.6
|
||
|
||
- deprecate catching string exceptions
|
||
|
||
* Python 2.7
|
||
|
||
- deprecate raising exceptions that do not inherit from BaseException
|
||
|
||
* Python 2.8
|
||
|
||
- deprecate catching exceptions that do not inherit from BaseException
|
||
|
||
* Python 2.9
|
||
|
||
- deprecate ``args`` and ``__getitem__`` (optional)
|
||
|
||
* Python 3.0
|
||
|
||
- drop everything that was deprecated above:
|
||
|
||
+ string exceptions (both raising and catching)
|
||
|
||
+ all exceptions must inherit from BaseException
|
||
|
||
+ drop ``args`` and ``__getitem__``
|
||
|
||
|
||
Implementation
|
||
==============
|
||
|
||
The initial implementation of this PEP has been checked into Python 2.5 .
|
||
|
||
References
|
||
==========
|
||
|
||
.. [#pep348] PEP 348 (Exception Reorganization for Python 3.0)
|
||
http://www.python.org/peps/pep-0348.html
|
||
|
||
.. [#hierarchy-good] python-dev Summary for 2004-08-01 through 2004-08-15
|
||
http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception
|
||
|
||
.. [#SF_1104669] SF patch #1104669 (new-style exceptions)
|
||
http://www.python.org/sf/1104669
|
||
|
||
|
||
Copyright
|
||
=========
|
||
|
||
This document has been placed in the public domain.
|
||
|
||
|
||
|
||
..
|
||
Local Variables:
|
||
mode: indented-text
|
||
indent-tabs-mode: nil
|
||
sentence-end-double-space: t
|
||
fill-column: 70
|
||
End:
|