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:

Note

This PEP has been rejected[1].

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[2].

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[3].

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[4],[5], [6],[7], [8],[9]. 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.

+-- 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
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 [10].

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.5 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.5 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[11], tutorial[12], 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[13].

Introduction of MacError and UnixError

Proposed to add symmetry to WindowsError, the BDFL said they won't be
used enough[14]. 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"[15].

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 [16]. 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[17].

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[18]. 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, Alyssa 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

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:

[1] python-dev email (Bare except clauses in PEP 348)
https://mail.python.org/pipermail/python-dev/2005-August/055676.html

[2] 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

[3] python-dev email (PEP, take 2: Exception Reorganization for Python
3.0)
https://mail.python.org/pipermail/python-dev/2005-August/055116.html

[4] 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

[5] python-dev thread (PEP, take 2: Exception Reorganization for Python
3.0)
https://mail.python.org/pipermail/python-dev/2005-August/055103.html

[6] python-dev thread (Reorg PEP checked in)
https://mail.python.org/pipermail/python-dev/2005-August/055138.html

[7] python-dev thread (Major revision of PEP 348 committed)
https://mail.python.org/pipermail/python-dev/2005-August/055199.html

[8] python-dev thread (Exception Reorg PEP revised yet again)
https://mail.python.org/pipermail/python-dev/2005-August/055292.html

[9] python-dev thread (PEP 348 (exception reorg) revised again)
https://mail.python.org/pipermail/python-dev/2005-August/055412.html

[10] python-dev email (PEP 348 (exception reorg) revised again)
https://mail.python.org/pipermail/python-dev/2005-August/055423.html

[11] exceptions module http://docs.python.org/library/exceptions.html

[12] Python Tutorial http://docs.python.org/tutorial/

[13] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0)
https://mail.python.org/pipermail/python-dev/2005-July/055019.html

[14] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0)
https://mail.python.org/pipermail/python-dev/2005-July/055019.html

[15] python-dev email (PEP, take 2: Exception Reorganization for Python
3.0)
https://mail.python.org/pipermail/python-dev/2005-August/055159.html

[16] python-dev email (Exception Reorg PEP checked in)
https://mail.python.org/pipermail/python-dev/2005-August/055149.html

[17] python-dev email (Exception Reorg PEP checked in)
https://mail.python.org/pipermail/python-dev/2005-August/055175.html

[18] python-dev email (PEP 348 (exception reorg) revised again)
https://mail.python.org/pipermail/python-dev/2005-August/055423.html