python-peps/pep-0352.txt

247 lines
8.3 KiB
Plaintext
Raw Normal View History

PEP: 352
Title: Required Superclass for Exceptions
Version: $Revision: 1.5 $
Last-Modified: $Date: 2005/06/07 13:17:37 $
Author: Brett Cannon <brett@python.org>, Guido van Rossum <guido@python.org>
Status: Draft
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 a problem since it prevents any
guarantees to be 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 are usually meant to signal
that the interpreter should be shut down. 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 and
terminate the interpreter.
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)::
class BaseException(object):
"""Superclass representing the base of the exception hierarchy.
Provides a 'message' attribute that contains any argument
passed in during instantiation.
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"""
self.args = args
self.message = args[0] if args else ''
def __str__(self):
"""Return the str of 'message'"""
return str(self.message
if not self.args
else self.args)
def __unicode__(self):
"""Return the unicode of 'message'"""
return unicode(self.message
if not self.args
else self.args)
def __repr__(self):
args_repr = (repr(self.message)
if not self.args
else "*%r" % self.args)
return "%s(%s)" % (self.__class__.__name__, args_repr)
def __getitem__(self, index):
"""Index into arguments passed in during instantiation.
Provided for backwards-compatibility and will be
deprecated.
"""
return self.args[index]
The ``message`` attribute will contain either the argument passed in
at instantiation of the object or the empty string. The attribute is
meant to act as a common location to store any extra information that
is to be passed along with the exception that goes beyond the location
of the exception within the exception hierarchy and the exception's
type.
No restriction is placed upon what may be passed in for ``messsage``.
This provides backwards-compatibility with how the argument passed
into Exception has no restrictions.
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
intepreter 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 exit to occur which had been explicitly
requested.
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 introduction of the
deprecation and the raising of a DeprecationWarning for the version
specifically listed.
* Python 2.5
- introduce BaseException
+ allow exceptions to be new-style classes
+ all standard exceptions become new-style classes
- 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__``
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
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: