518 lines
19 KiB
Plaintext
518 lines
19 KiB
Plaintext
PEP: 348
|
||
Title: Exception Reorganization for Python 3.0
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Brett Cannon <brett@python.org>
|
||
Status: Rejected
|
||
Type: Standards Track
|
||
Content-Type: text/x-rst
|
||
Created: 28-Jul-2005
|
||
Post-History:
|
||
|
||
.. |2.x| replace:: 2.5
|
||
|
||
.. note:: This PEP has been rejected [#rejected]_.
|
||
|
||
|
||
Abstract
|
||
========
|
||
|
||
Python, as of version 2.4, has 38 exceptions (including warnings) in
|
||
the built-in namespace in a rather shallow hierarchy. These
|
||
classes have come about over the years without a chance to learn from
|
||
experience. This PEP proposes doing a reorganization of the hierarchy
|
||
for Python 3.0 when backwards-compatibility is not as much of an
|
||
issue.
|
||
|
||
Along with this reorganization, adding a requirement that all
|
||
objects passed to a ``raise`` statement must inherit from a specific
|
||
superclass is proposed. This is to have guarantees about the basic
|
||
interface of exceptions and to further enhance the natural hierarchy
|
||
of exceptions.
|
||
|
||
Lastly, bare ``except`` clauses will be changed to be semantically
|
||
equivalent to ``except Exception``. Most people currently use bare
|
||
``except`` clause for this purpose and with the exception hierarchy
|
||
reorganization becomes a viable default.
|
||
|
||
|
||
Rationale For Wanting Change
|
||
============================
|
||
|
||
Exceptions are a critical part of Python. While exceptions are
|
||
traditionally used to signal errors in a program, they have also grown
|
||
to be used for flow control for things such as iterators.
|
||
|
||
While their importance is great, there is a lack of structure to them.
|
||
This stems from the fact that any object can be raised as an
|
||
exception. Because of this you have no guarantee in terms of what
|
||
kind of object will be raised, destroying any possible hierarchy
|
||
raised objects might adhere to.
|
||
|
||
But exceptions do have a hierarchy, showing the severity of the
|
||
exception. The hierarchy also groups related exceptions together to
|
||
simplify catching them in ``except`` clauses. To allow people to
|
||
be able to rely on this hierarchy, a common superclass that all
|
||
raise objects must inherit from is being proposed. It also allows
|
||
guarantees about the interface to raised objects to be made (see
|
||
:pep:`344`). A discussion about all of this has occurred
|
||
before on python-dev [#Summary2004-08-01]_.
|
||
|
||
As bare ``except`` clauses stand now, they catch *all* exceptions.
|
||
While this can be handy, it is rather overreaching for the common
|
||
case. Thanks to having a required superclass, catching all
|
||
exceptions is as easy as catching just one specific exception.
|
||
This allows bare ``except`` clauses to be used for a more useful
|
||
purpose.
|
||
Once again, this has been discussed on python-dev [#python-dev3]_.
|
||
|
||
Finally, slight changes to the exception hierarchy will make it much
|
||
more reasonable in terms of structure. By minor rearranging
|
||
exceptions
|
||
that should not typically be caught can be allowed to propagate to the
|
||
top of the execution stack, terminating the interpreter as intended.
|
||
|
||
|
||
Philosophy of Reorganization
|
||
============================
|
||
|
||
For the reorganization of the hierarchy, there was a general
|
||
philosophy followed that developed from discussion of earlier drafts
|
||
of this PEP [#python-dev-thread1]_, [#python-dev-thread2]_,
|
||
[#python-dev-thread3]_, [#python-dev-thread4]_,
|
||
[#python-dev-thread5]_, [#python-dev-thread6]_.
|
||
First and foremost was to not break anything
|
||
that works. This meant that renaming exceptions was out of the
|
||
question unless the name was deemed severely bad. This
|
||
also meant no removal of exceptions unless they were viewed as
|
||
truly misplaced. The introduction of new exceptions were only done in
|
||
situations where there might be a use for catching a superclass of a
|
||
category of exceptions. Lastly, existing exceptions would have their
|
||
inheritance tree changed only if it was felt they were truly
|
||
misplaced to begin with.
|
||
|
||
For all new exceptions, the proper suffix had to be chosen. For
|
||
those that signal an error, "Error" is to be used. If the exception
|
||
is a warning, then "Warning". "Exception" is to be used when none
|
||
of the other suffixes are proper to use and no specific suffix is
|
||
a better fit.
|
||
|
||
After that it came down to choosing which exceptions should and
|
||
should not inherit from Exception. This was for the purpose of
|
||
making bare ``except`` clauses more useful.
|
||
|
||
Lastly, the entire existing hierarchy had to inherit from the new
|
||
exception meant to act as the required superclass for all exceptions
|
||
to inherit from.
|
||
|
||
|
||
New Hierarchy
|
||
=============
|
||
|
||
.. Note:: Exceptions flagged with "stricter inheritance" will no
|
||
longer inherit from a certain class. A "broader inheritance" flag
|
||
means a class has been added to the exception's inheritance tree.
|
||
All comparisons are against the Python 2.4 exception hierarchy.
|
||
|
||
.. parsed-literal::
|
||
|
||
+-- BaseException (new; broader inheritance for subclasses)
|
||
+-- Exception
|
||
+-- GeneratorExit (defined in :pep:`342`)
|
||
+-- StandardError
|
||
+-- ArithmeticError
|
||
+-- DivideByZeroError
|
||
+-- FloatingPointError
|
||
+-- OverflowError
|
||
+-- AssertionError
|
||
+-- AttributeError
|
||
+-- EnvironmentError
|
||
+-- IOError
|
||
+-- EOFError
|
||
+-- OSError
|
||
+-- ImportError
|
||
+-- LookupError
|
||
+-- IndexError
|
||
+-- KeyError
|
||
+-- MemoryError
|
||
+-- NameError
|
||
+-- UnboundLocalError
|
||
+-- NotImplementedError (stricter inheritance)
|
||
+-- SyntaxError
|
||
+-- IndentationError
|
||
+-- TabError
|
||
+-- TypeError
|
||
+-- RuntimeError
|
||
+-- UnicodeError
|
||
+-- UnicodeDecodeError
|
||
+-- UnicodeEncodeError
|
||
+-- UnicodeTranslateError
|
||
+-- ValueError
|
||
+-- ReferenceError
|
||
+-- StopIteration
|
||
+-- SystemError
|
||
+-- Warning
|
||
+-- DeprecationWarning
|
||
+-- FutureWarning
|
||
+-- PendingDeprecationWarning
|
||
+-- RuntimeWarning
|
||
+-- SyntaxWarning
|
||
+-- UserWarning
|
||
+ -- WindowsError
|
||
+-- KeyboardInterrupt (stricter inheritance)
|
||
+-- SystemExit (stricter inheritance)
|
||
|
||
|
||
Differences Compared to Python 2.4
|
||
==================================
|
||
|
||
A more thorough explanation of terms is needed when discussing
|
||
inheritance changes. Inheritance changes result in either broader or
|
||
more restrictive inheritance. "Broader" is when a class has an
|
||
inheritance tree like ``cls, A`` and then becomes ``cls, B, A``.
|
||
"Stricter" is the reverse.
|
||
|
||
|
||
BaseException
|
||
-------------
|
||
|
||
The superclass that all exceptions must inherit from. It's name was
|
||
chosen to reflect that it is at the base of the exception hierarchy
|
||
while being an exception itself. "Raisable" was considered as a name,
|
||
it was passed on because its name did not properly reflect the fact
|
||
that it is an exception itself.
|
||
|
||
Direct inheritance of BaseException is not expected, and will
|
||
be discouraged for the general case. Most user-defined
|
||
exceptions should inherit from Exception instead. This allows
|
||
catching Exception to continue to work in the common case of catching
|
||
all exceptions that should be caught. Direct inheritance of
|
||
BaseException should only be done in cases where an entirely new
|
||
category of exception is desired.
|
||
|
||
But, for cases where all
|
||
exceptions should be caught blindly, ``except BaseException`` will
|
||
work.
|
||
|
||
|
||
KeyboardInterrupt and SystemExit
|
||
--------------------------------
|
||
|
||
Both exceptions are no longer under Exception. This is to allow bare
|
||
``except`` clauses to act as a more viable default case by catching
|
||
exceptions that inherit from Exception. With both KeyboardInterrupt
|
||
and SystemExit acting as signals that the interpreter is expected to
|
||
exit, catching them in the common case is the wrong semantics.
|
||
|
||
|
||
NotImplementedError
|
||
-------------------
|
||
|
||
Inherits from Exception instead of from RuntimeError.
|
||
|
||
Originally inheriting from RuntimeError, NotImplementedError does not
|
||
have any direct relation to the exception meant for use in user code
|
||
as a quick-and-dirty exception. Thus it now directly inherits from
|
||
Exception.
|
||
|
||
|
||
Required Superclass for ``raise``
|
||
=================================
|
||
|
||
By requiring all objects passed to a ``raise`` statement to inherit
|
||
from a specific superclass, all exceptions are guaranteed to have
|
||
certain attributes. If :pep:`344` is accepted, the attributes
|
||
outlined there will be guaranteed to be on all exceptions raised.
|
||
This should help facilitate debugging by making the querying of
|
||
information from exceptions much easier.
|
||
|
||
The proposed hierarchy has BaseException as the required base class.
|
||
|
||
|
||
Implementation
|
||
--------------
|
||
|
||
Enforcement is straightforward. Modifying ``RAISE_VARARGS`` to do an
|
||
inheritance check first before raising an exception should be enough.
|
||
For the C API, all functions that set an exception will have the same
|
||
inheritance check applied.
|
||
|
||
|
||
Bare ``except`` Clauses Catch Exception
|
||
=======================================
|
||
|
||
In most existing Python 2.4 code, bare ``except`` clauses are too
|
||
broad in the exceptions they catch. Typically only exceptions that
|
||
signal an error are desired to be caught. This means that exceptions
|
||
that are used to signify that the interpreter should exit should not
|
||
be caught in the common case.
|
||
|
||
With KeyboardInterrupt and SystemExit moved to inherit from
|
||
BaseException instead of Exception, changing bare ``except`` clauses
|
||
to act as ``except Exception`` becomes a much more reasonable
|
||
default. This change also will break very little code since these
|
||
semantics are what most people want for bare ``except`` clauses.
|
||
|
||
The complete removal of bare ``except`` clauses has been argued for.
|
||
The case has been made that they violate both Only One Way To Do It
|
||
(OOWTDI) and Explicit Is Better Than Implicit (EIBTI) as listed in the
|
||
:pep:`Zen of Python <20>`. But Practicality Beats Purity (PBP), also in
|
||
the Zen of Python, trumps both of these in this case. The BDFL has
|
||
stated that bare ``except`` clauses will work this way
|
||
[#python-dev8]_.
|
||
|
||
|
||
Implementation
|
||
--------------
|
||
|
||
The compiler will emit the bytecode for ``except Exception`` whenever
|
||
a bare ``except`` clause is reached.
|
||
|
||
|
||
Transition Plan
|
||
===============
|
||
|
||
Because of the complexity and clutter that would be required to add
|
||
all features planned in this PEP, the transition plan is very simple.
|
||
In Python |2.x| BaseException is added. In Python 3.0, all remaining
|
||
features (required superclass, change in inheritance, bare ``except``
|
||
clauses becoming the same as ``except Exception``) will go into
|
||
affect. In order to make all of this work in a backwards-compatible
|
||
way in Python |2.x| would require very deep hacks in the exception
|
||
machinery which could be error-prone and lead to a slowdown in
|
||
performance for little benefit.
|
||
|
||
To help with the transition, the documentation will be changed to
|
||
reflect several programming guidelines:
|
||
|
||
- When one wants to catch *all* exceptions, catch BaseException
|
||
- To catch all exceptions that do not represent the termination of
|
||
the interpreter, catch Exception explicitly
|
||
- Explicitly catch KeyboardInterrupt and SystemExit; don't rely on
|
||
inheritance from Exception to lead to the capture
|
||
- Always catch NotImplementedError explicitly instead of relying on
|
||
the inheritance from RuntimeError
|
||
|
||
The documentation for the 'exceptions' module [#exceptions-stdlib]_,
|
||
tutorial [#tutorial]_, and :pep:`290` will all require
|
||
updating.
|
||
|
||
|
||
Rejected Ideas
|
||
==============
|
||
|
||
DeprecationWarning Inheriting From PendingDeprecationWarning
|
||
------------------------------------------------------------
|
||
|
||
This was originally proposed because a DeprecationWarning can be
|
||
viewed as a PendingDeprecationWarning that is being removed in the
|
||
next version. But since enough people thought the inheritance could
|
||
logically work the other way around, the idea was dropped.
|
||
|
||
|
||
AttributeError Inheriting From TypeError or NameError
|
||
-----------------------------------------------------
|
||
|
||
Viewing attributes as part of the interface of a type caused the idea
|
||
of inheriting from TypeError. But that partially defeats the thinking
|
||
of duck typing and thus the idea was dropped.
|
||
|
||
Inheriting from NameError was suggested because objects can be viewed
|
||
as having their own namespace where the attributes live and when an
|
||
attribute is not found it is a namespace failure. This was also
|
||
dropped as a possibility since not everyone shared this view.
|
||
|
||
|
||
Removal of EnvironmentError
|
||
---------------------------
|
||
|
||
Originally proposed based on the idea that EnvironmentError was an
|
||
unneeded distinction, the BDFL overruled this idea [#python-dev4]_.
|
||
|
||
|
||
Introduction of MacError and UnixError
|
||
--------------------------------------
|
||
|
||
Proposed to add symmetry to WindowsError, the BDFL said they won't be
|
||
used enough [#python-dev4]_. The idea of then removing WindowsError
|
||
was proposed and accepted as reasonable, thus completely negating the
|
||
idea of adding these exceptions.
|
||
|
||
|
||
SystemError Subclassing SystemExit
|
||
----------------------------------
|
||
|
||
Proposed because a SystemError is meant to lead to a system exit, the
|
||
idea was removed since CriticalError indicates this better.
|
||
|
||
|
||
ControlFlowException Under Exception
|
||
------------------------------------
|
||
|
||
It has been suggested that ControlFlowException should inherit from
|
||
Exception. This idea has been rejected based on the thinking that
|
||
control flow exceptions typically do not all need to be caught by a
|
||
single ``except`` clause.
|
||
|
||
Rename NameError to NamespaceError
|
||
----------------------------------
|
||
|
||
NameError is considered more succinct and leaves open no possible
|
||
mistyping of
|
||
the capitalization of "Namespace" [#python-dev5]_.
|
||
|
||
|
||
Renaming RuntimeError or Introducing SimpleError
|
||
''''''''''''''''''''''''''''''''''''''''''''''''
|
||
|
||
The thinking was that RuntimeError was in no way an obvious name for
|
||
an exception meant to be used when a situation did not call for the
|
||
creation of a new exception. The renaming was rejected on the basis
|
||
that the exception is already used throughout the interpreter
|
||
[#python-dev6]_.
|
||
Rejection of SimpleError was founded on the thought that people
|
||
should be free to use whatever exception they choose and not have one
|
||
so blatantly suggested [#python-dev7]_.
|
||
|
||
Renaming Existing Exceptions
|
||
----------------------------
|
||
|
||
Various renamings were suggested but non garnered more than a +0 vote
|
||
(renaming ReferenceError to WeakReferenceError). The thinking was
|
||
that the existing names were fine and no one had actively complained
|
||
about them ever. To minimize backwards-compatibility issues and
|
||
causing existing Python programmers extra pain, the renamings were
|
||
removed.
|
||
|
||
Have EOFError Subclass IOError
|
||
------------------------------
|
||
|
||
The original thought was that since EOFError deals directly with I/O,
|
||
it should
|
||
subclass IOError. But since EOFError is used more as a signal that an
|
||
event
|
||
has occurred (the exhaustion of an I/O port), it should not subclass
|
||
such a specific error exception.
|
||
|
||
|
||
Have MemoryError and SystemError Have a Common Superclass
|
||
---------------------------------------------------------
|
||
|
||
Both classes deal with the interpreter, so why not have them have a
|
||
common
|
||
superclass? Because one of them means that the interpreter is in a
|
||
state that it should not recover from while the other does not.
|
||
|
||
|
||
Common Superclass for PendingDeprecationWarning and DeprecationWarning
|
||
----------------------------------------------------------------------
|
||
|
||
Grouping the deprecation warning exceptions together makes intuitive
|
||
sense.
|
||
But this sensical idea does not extend well when one considers how
|
||
rarely either warning is used, let along at the same time.
|
||
|
||
|
||
Removing WindowsError
|
||
---------------------
|
||
|
||
Originally proposed based on the idea that having such a
|
||
platform-specific exception should not be in the built-in namespace.
|
||
It turns out, though, enough code exists that uses the exception to
|
||
warrant it staying.
|
||
|
||
|
||
Superclass for KeyboardInterrupt and SystemExit
|
||
-----------------------------------------------
|
||
|
||
Proposed to make catching non-Exception inheriting exceptions easier
|
||
along with easing the transition to the new hierarchy, the idea was
|
||
rejected by the BDFL [#python-dev8]_. The argument that existing
|
||
code did not show enough instances of the pair of exceptions being
|
||
caught and thus did not justify cluttering the built-in namespace
|
||
was used.
|
||
|
||
|
||
Acknowledgements
|
||
================
|
||
|
||
Thanks to Robert Brewer, Josiah Carlson, Nick Coghlan, Timothy
|
||
Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing,
|
||
James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull,
|
||
Raymond Hettinger, and everyone else I missed for participating in the
|
||
discussion.
|
||
|
||
|
||
References
|
||
==========
|
||
|
||
.. [#Summary2004-08-01] python-dev Summary (An exception is an
|
||
exception, unless it doesn't inherit from Exception)
|
||
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
|
||
|
||
.. [#python-dev3] python-dev email (PEP, take 2: Exception
|
||
Reorganization for Python 3.0)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055116.html
|
||
|
||
.. [#exceptions-stdlib] exceptions module
|
||
http://docs.python.org/library/exceptions.html
|
||
|
||
.. [#python-dev-thread1] python-dev thread (Pre-PEP: Exception
|
||
Reorganization for Python 3.0)
|
||
https://mail.python.org/pipermail/python-dev/2005-July/055020.html,
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055065.html
|
||
|
||
.. [#python-dev-thread2] python-dev thread (PEP, take 2: Exception
|
||
Reorganization for Python 3.0)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055103.html
|
||
|
||
.. [#python-dev-thread3] python-dev thread (Reorg PEP checked in)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055138.html
|
||
|
||
.. [#python-dev-thread4] python-dev thread (Major revision of PEP 348 committed)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055199.html
|
||
|
||
.. [#python-dev-thread5] python-dev thread (Exception Reorg PEP revised yet again)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055292.html
|
||
|
||
.. [#python-dev-thread6] python-dev thread (PEP 348 (exception reorg) revised again)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055412.html
|
||
|
||
.. [#python-dev4] python-dev email (Pre-PEP: Exception Reorganization
|
||
for Python 3.0)
|
||
https://mail.python.org/pipermail/python-dev/2005-July/055019.html
|
||
|
||
.. [#python-dev5] python-dev email (PEP, take 2: Exception Reorganization for
|
||
Python 3.0)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055159.html
|
||
|
||
.. [#python-dev6] python-dev email (Exception Reorg PEP checked in)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055149.html
|
||
|
||
.. [#python-dev7] python-dev email (Exception Reorg PEP checked in)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055175.html
|
||
|
||
.. [#python-dev8] python-dev email (PEP 348 (exception reorg) revised again)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055423.html
|
||
|
||
.. [#tutorial] Python Tutorial
|
||
http://docs.python.org/tutorial/
|
||
|
||
.. [#rejected] python-dev email (Bare except clauses in PEP 348)
|
||
https://mail.python.org/pipermail/python-dev/2005-August/055676.html
|
||
|
||
|
||
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:
|