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:
parent
e78fb66230
commit
239f9ff186
357
pep-0348.txt
357
pep-0348.txt
|
@ -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
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue