Big changes:

- Remove proposal of removing WindowsError
- Change bare 'except' proposal to recommend their removal

Minor changes:

- Flesh out arguments for TerminatingException
- Reorganize discussion of hierarchy difference compared to 2.4
- Strip out unneeded Rejected Idea sections based on other discussions in the
  PEP
This commit is contained in:
Brett Cannon 2005-08-15 04:28:28 +00:00
parent e78fb66230
commit 239f9ff186
1 changed files with 233 additions and 124 deletions

View File

@ -13,25 +13,34 @@ Post-History:
Abstract
========
Python, as 0of version 2.4, has 38 exceptions (including warnings) in
the built-in namespace in a rather shallow hierarchy. This list of
classes has grown over the years without a chance to learn from
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
issue.
Along with this reorganization, adding a requirement that all
objects passed to a ``raise`` statement must inherit from a specific
superclass is proposed. Lastly, bare ``except`` clauses will catch
only exceptions inheriting from Exception.
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 removed. While they had their
usefulness when exceptions could be any object, with the above
proposal
of a required superclass for exceptions bare ``except`` clauses lose
their purpose.
Rationale
=========
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 lack of structure to them.
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
@ -40,26 +49,23 @@ 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 peopele to
be able to rely on this hierarchy, a common superclasse that all
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 [#PEP344]_). A discussion about all of this has occurred
before on python-dev [#Summary2004-08-01]_.
But allowing a guarantee about the hierarchy is not the only place
where exceptions can stand improvement. Bare ``except`` clauses are
often used in an inappropriate manner. Since they catch *all* raised
objects, they can catch exceptions that should have been allowed to
propagate to the top level of the execution stack, leading to the
interpreter terminating execution. Once again, this has been
discussed on python-dev [#python-dev3]_.
With the requirement of a common superclass for all exceptions, bare
``except`` clauses are impacted. Currently used as a catch-all for
raised exceptions, its usefulness is terminally weakened. Now, the
same functionality is possible by catching the required superclass.
Once again, this has been discussed on python-dev [#python-dev3]_.
To fix this over-reaching of bare ``except`` clauses, it is being
proposed that only objects inheriting from Exception be caught by
bare ``except`` clauses. This will allow the exception hierarchy
to be organized in such a way that bare ``except`` clauses can be
more useful by allowing exceptions that should not normally be caught
to lead to the termination of the interpreter.
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
@ -104,9 +110,6 @@ New Hierarchy
.. parsed-literal::
+-- BaseException (new; broader inheritance for subclasses)
+-- TerminatingException (new; stricter inheritance for subclasses)
+-- KeyboardInterrupt
+-- SystemExit
+-- Exception
+-- GeneratorExit (defined in PEP 342 [#PEP342]_)
+-- StandardError
@ -142,12 +145,17 @@ New Hierarchy
+-- StopIteration
+-- SystemError
+-- Warning
+-- PendingDeprecationWarning
+-- DeprecationWarning
+-- FutureWarning
+-- SyntaxWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+ -- WindowsError
+-- TerminatingException (new; stricter inheritance for
subclasses)
+-- KeyboardInterrupt
+-- SystemExit
Differences Compared to Python 2.4
@ -157,49 +165,111 @@ 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.
"Stricter" is the reverse.
New Exceptions
--------------
BaseException
'''''''''''''
-------------
The superclass that all exceptions must inherit from.
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.
TerminatingException
''''''''''''''''''''
--------------------
Superclass for exceptions that are meant to the termination of the
interpreter. It does not inherit from Exception so that
subclasses are not caught by bare ``except`` clauses.
Superclass for exceptions that are meant to symbolize the termination
of
the interpreter. It does not inherit from Exception so that the
common
``except Exception`` statement does not catch the exceptions. This
for ``try`` statements that want to catch all exceptions that are
signals of errors and handle them separately from exceptions that
signify that the interpreter should be terminated::
Naming based on the idea that the interpreter is trying to terminate when these
exceptions are raised. An earlier proposal suggested "TerminalException" but
avoidance of any confusion with an actual terminal along with "terminal" being
try:
...
# Catch all exceptions that are expected to be caught
except Exception:
...
# Catch exceptions expected to terminate the interpreter
except TerminatingException:
...
Compare this to::
try:
...
except Exception:
...
except (KeyboardInterrupt, SystemExit):
...
While more explicit, it is not necessarily obvious why the two
exceptions are being caught directly. By providing a common
superclass with a name that explicitly states the exceptions' typical
usage the reasoning behind the catch becomes more apparent.
Comparing it to an even more general ``except`` clause::
try:
...
except Exception:
...
except BaseException:
...
While this will perform the same action and catch all exceptions,
guaranteed, it is once again not obvious why Exception and
BaseException are being caught separately and not just
BaseException based purely on the code.
TerminatingException will also help with transitioning from Python
2.x to 3.0 . With TerminatingException being a new exception, when
it is used the new inheritance for KeyboardInterrupt and SystemExit
becomes more obvious.
It has been argued that TerminatingException should not be added
because it goes against Flat Is Better Than Nested (FIBTN).
While this new exception will not make the hierarchy as flat is it
could be, the overall depth of the tree is not changed; Exception has
a much deeper inheritance tree below it.
It has also been argued that since KeyboardInterrupt and SystemExit
are not caught together that often in 2.4, there might not be much of
a need for TerminatingException. But with their new position in the
hierarchy catching them separately from other exceptions should
become more prevalent.
Naming is based on the idea that the interpreter is trying to
terminate when KeyboardInterrupt and SystemExit
are raised. An earlier proposal suggested
"TerminalException" but
avoidance of any confusion with an actual terminal along with
"terminal" being
more fatalistic than "terminating" led to the current name choice.
Removed Exceptions
------------------
WindowsError
''''''''''''
Too OS-specific to be kept in the built-in exception hierarchy.
Change of Position in the Exception Hierarchy
---------------------------------------------
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
@ -228,27 +298,54 @@ For the C API, all functions that set an exception will have the same
inheritance check applied.
Bare ``except`` Clauses Catching ``Exception`` Only
===================================================
Removal of Bare ``except`` Clauses
==================================
While Python does have its "explicit is better than implicit" tenant,
it is not necessary if there is a reasonable default behavior.
Changing the behavior of a bare ``except`` clause makes its existance
quite reasonable.
Bare ``except`` clauses serve the purpose of catching all exceptions
in Python 2.x . This is needed thanks to the fact that in 2.x any
exception can be raised as an exception. But if this PEP is accepted
this will no longer be the case because of a required superclass for
all raised objects.
This goes against a part of the Zen of Python;
One Way To Do It (OWTDI) [#zen]_. By having bare ``except`` clauses
keep their semantic meaning, there would be two ways of doing the
same things. It also goes against Exlpicit Is Better Than Implicit
(EIBTI) by implicitly doing something that can easily be covered by
a more explicit statement.
In Python 2.4, a bare ``except`` clause will catch any and all
exceptions. Typically, though, this is not what is truly desired.
More often than not one wants to catch all error exceptions that do
not signify a "bad" interpreter state. In the new exception hierarchy
this is condition is embodied by Exception. Thus bare ``except``
clauses will catch only exceptions inheriting from Exception.
It has also been proposed that bare ``except`` clauses be changed to
semantically be equivalent to ``except Exception``. This has been
proposed since this is what most bare ``except`` clauses are meant to
do. This would make ``except`` clauses have a more reasonable
default behavior.
But this line of reasoning has some issues. First is that it also
goes against the Zen of Python; both OWTDI and EIBTI by providing
an implicit alternative to ``except Exception``. Secondly,
backwards-compatibility becomes more difficult. While removal will
break code, it can mechanically be changed to have the same semantic
meaning as it currently has by finding all occurances of ``except:``
and replacing them with ``except BaseException:``. But going with
this semantic change makes compatibility more subtle. Was a bare
``except`` clause meant to catch all exceptions, or actually meant
to catch all reasonable exceptions (i.e., everything but
TerminatingException)? Every case will need to be carefully
examined, and could easily be incorrectly examined, leading to subtle
bugs.
The shorter typing afforded by bare ``except`` statements also does
not justify its existence. "Exception", the typical exception that\
will be caught, is only nine characters. Add in the needed space to
separate "Exception" from "except" you get only 10 more characters to
type. While this might be a nuisance in quick-and-dirty scripts, the
impact is minimal.
Implementation
--------------
In the compiler, when a bare ``except`` clause is reached, the code
for ``except Exception`` will be emitted.
Changing Grammar/Grammar is all that is needed to remove bare
``except`` clauses.
Transition Plan
@ -280,15 +377,6 @@ would not have under the new hierarchy. This will require hard-coding
in the implementation of the bytecode.
Removed Exceptions
''''''''''''''''''
Exceptions scheduled for removal will be transitioned much like the
old names of renamed exceptions. Upon instantiation a
PendingDeprecationWarning will be raised stating the the exception is
due for removal in Python 3.0.
Required Superclass for ``raise``
---------------------------------
@ -299,8 +387,41 @@ A DeprecationWarning will be raised when an object is passed to
Removal of Bare ``except`` Clauses
----------------------------------
A RuntimeWarning will be raised for all bare ``except`` clauses that
catch an exception that does not inherit from Exception.
A PendingDeprecationWarning will be raised when a bare ``except``
clause is found in code. One release before they are removed the
warning will be changed to DeprecationWarning.
Doing ``from __future__ import exceptions`` will cause bare
``except`` clauses to be considered syntax errors.
Roadmap
-------
Python 2.x is the first version that contains changes. Python 3.0
will be when transition will be complete. Version 3.0-1 represents
one version before 3.0 is released, 3.0-2 two versions before, etc.
* 2.x
- Add BaseException, TerminatingException
- Have KeyboardInterrupt and SystemExit inherit from both
Exception and TerminatingException
- Introduce ``from __future__ import exceptions``
- Provide a script that mechanically changes all bare ``except``
clauses to catch BaseException in a .py file
- PendingDeprecationWarning for bare ``except`` clauses
- PendingDeprecationWarning for all objects raised that do not
inherit from BaseException
- PendingDeprecationWarning raised when KeyboardInterrupt or
SystemExit are caught because of their inheritance of Exception
* 3.0-1
- Turn all introduced PendingDeprecationWarnings into
DeprecationWarning
* 3.0
- Remove DeprecationWarnings
- Have KeyboardInterrupt and SystemExit only inherit from
TerminatingException
- Remove ``exceptions`` __future__ import support
Rejected Ideas
@ -311,26 +432,6 @@ various ideas being rejected [#python-dev-thread1]_,
[#python-dev-thread2]_, [#python-dev-thread3]_.
KeyboardInterrupt inheriting from ControlFlowException
------------------------------------------------------
KeyboardInterrupt has been a contentious point within this hierarchy.
Some view the exception more as control flow being caused by the user.
But with its asynchronous cause (the user is able to trigger the
exception at any point in code) its proper place is inheriting from
CriticalError.
Other Names for BaseException and Exception
-------------------------------------------
Alternative names for BaseException and Exception have been
Raisable/Exception and Exception/StandardError. The former
alternatives were rejected because "Raisable" does not reflect its
exception nature well enough. The latter alternatives were rejected
because they do not reflect current use.
DeprecationWarning Inheriting From PendingDeprecationWarning
------------------------------------------------------------
@ -381,23 +482,14 @@ 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 should not be caught by bare
``except`` clauses, whereas Exception subclasses should be.
Removal of Bare ``except`` Clauses
----------------------------------
The suggestion has been made to remove bare ``except`` clauses
altogether, in the name of "explicit is better than implicit". But
Guido has said this is too weak of an argument since other areas of
Python have default behavior [#python-dev3]_.
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
NameError is considered more succinct and leaves open no possible
mistyping of
the capitalization of "Namespace" [#python-dev5]_.
@ -407,7 +499,8 @@ 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]_.
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 blatently suggested [#python-dev7]_.
@ -425,26 +518,39 @@ removed.
Have EOFError Subclass IOError
------------------------------
The original thought was that sine 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.
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.
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.
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.
Acknowledgements
@ -503,6 +609,9 @@ References
.. [#python-dev7] python-dev email (Exception Reorg PEP checked in)
http://mail.python.org/pipermail/python-dev/2005-August/055175.html
.. [#zen] PEP 20 (The Zen of Python)
http://www.python.org/peps/pep-0020.html
Copyright
=========