Eliminate Implicit Exception Instantiation, by Steven Taschuk
This commit is contained in:
parent
613e70e3b8
commit
9981a257a3
|
@ -0,0 +1,350 @@
|
|||
PEP: 317
|
||||
Title: Eliminate Implicit Exception Instantiation
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Steven Taschuk <staschuk@telusplanet.net>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 06-May-2003
|
||||
Python-Version: 2.4
|
||||
Post-History:
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
"For clarity in new code, the form ``raise class(argument, ...)``
|
||||
is recommended (i.e. make an explicit call to the constructor)."
|
||||
|
||||
-- Guido van Rossum, in 1997 [1]_
|
||||
|
||||
This PEP proposes the formal deprecation and eventual elimination of
|
||||
forms of the ``raise`` statement which implicitly instantiate an
|
||||
exception. For example, statements such as ::
|
||||
|
||||
raise HullBreachError
|
||||
raise KitchenError, 'all out of baked beans'
|
||||
|
||||
must under this proposal be replaced with their synonyms ::
|
||||
|
||||
raise HullBreachError()
|
||||
raise KitchenError('all out of baked beans')
|
||||
|
||||
Note that these latter statements are already legal, and that this PEP
|
||||
does not change their meaning.
|
||||
|
||||
Eliminating these forms of ``raise`` makes it impossible to use string
|
||||
exceptions; accordingly, this PEP also proposes the formal deprecation
|
||||
and eventual elimination of string exceptions.
|
||||
|
||||
Adoption of this proposal breaks backwards compatibility. Under the
|
||||
proposed implementation schedule, Python 2.4 will introduce warnings
|
||||
about uses of ``raise`` which will eventually become incorrect, and
|
||||
Python 3.0 will eliminate them entirely. (It is assumed that this
|
||||
transition period -- 2.4 to 3.0 -- will be at least one year long, to
|
||||
comply with the guidelines of PEP 5 [2]_.)
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
|
||||
String Exceptions
|
||||
-----------------
|
||||
|
||||
It is assumed that removing string exceptions will be uncontroversial,
|
||||
since it has been intended since at least Python 1.5, when the standard
|
||||
exception types were changed to classes [1]_.
|
||||
|
||||
For the record: string exceptions should be removed because the
|
||||
presence of two kinds of exception complicates the language without any
|
||||
compensation. Instance exceptions are superior because, for example,
|
||||
|
||||
* the class-instance relationship more naturally expresses the
|
||||
relationship between the exception type and value,
|
||||
|
||||
* they can be organized naturally using superclass-subclass
|
||||
relationships, and
|
||||
|
||||
* they can encapsulate error-reporting behaviour (for example).
|
||||
|
||||
|
||||
Implicit Instantiation
|
||||
----------------------
|
||||
|
||||
Guido's 1997 essay [1]_ on changing the standard exceptions into
|
||||
classes makes clear why ``raise`` can instantiate implicitly:
|
||||
|
||||
"The raise statement has been extended to allow raising a class
|
||||
exception without explicit instantiation. The following forms,
|
||||
called the "compatibility forms" of the raise statement [...]
|
||||
The motivation for introducing the compatibility forms was to allow
|
||||
backward compatibility with old code that raised a standard
|
||||
exception."
|
||||
|
||||
For example, it was desired that pre-1.5 code which used string
|
||||
exception syntax such as ::
|
||||
|
||||
raise TypeError, 'not an int'
|
||||
|
||||
would work both on versions of Python in which ``TypeError`` was a
|
||||
string, and on versions in which it was a class.
|
||||
|
||||
When no such consideration obtains -- that is, when the desired
|
||||
exception type is not a string in any version of the software which the
|
||||
code must support -- there is no good reason to instantiate implicitly,
|
||||
and it is clearer not to. For example:
|
||||
|
||||
1. In the code ::
|
||||
|
||||
try:
|
||||
raise MyError, raised
|
||||
except MyError, caught:
|
||||
pass
|
||||
|
||||
the syntactic parallel between the ``raise`` and ``except``
|
||||
statements strongly suggests that ``raised`` and ``caught`` refer
|
||||
to the same object. For string exceptions this actually is the
|
||||
case, but for instance exceptions it is not.
|
||||
|
||||
2. When instantiation is implicit, it is not obvious when it occurs,
|
||||
for example, whether it occurs when the exception is raised or when
|
||||
it is caught. Since it actually happens at the ``raise``, the code
|
||||
should say so.
|
||||
|
||||
(Note that at the level of the C API, an exception can be "raised"
|
||||
and "caught" without being instantiated; this is used as an
|
||||
optimization by, for example, ``PyIter_Next``. But in Python, no
|
||||
such optimization is or should be available.)
|
||||
|
||||
3. An implicitly instantiating ``raise`` statement with no arguments,
|
||||
such as ::
|
||||
|
||||
raise MyError
|
||||
|
||||
simply does not do what it says: it does not raise the named
|
||||
object.
|
||||
|
||||
4. The equivalence of ::
|
||||
|
||||
raise MyError
|
||||
raise MyError()
|
||||
|
||||
conflates classes and instances, creating a possible source of
|
||||
confusion for beginners. (Moreover, it is not clear that the
|
||||
interpreter could distinguish between a new-style class and an
|
||||
instance of such a class, so implicit instantiation may be an
|
||||
obstacle to any future plan to let exceptions be new-style
|
||||
objects.)
|
||||
|
||||
In short, implicit instantiation has no advantages other than backwards
|
||||
compatibility, and so should be phased out along with what it exists to
|
||||
ensure compatibility with, namely, string exceptions.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
The syntax of ``raise_stmt`` [3]_ is to be changed from ::
|
||||
|
||||
raise_stmt ::= "raise" [expression ["," expression ["," expression]]]
|
||||
|
||||
to ::
|
||||
|
||||
raise_stmt ::= "raise" [expression ["," expression]]
|
||||
|
||||
If no expressions are present, the ``raise`` statement behaves as it
|
||||
does presently: it re-raises the last exception that was active in the
|
||||
current scope, and if no exception has been active in the current
|
||||
scope, a ``TypeError`` is raised indicating that this is the problem.
|
||||
|
||||
Otherwise, the first expression is evaluated, producing the *raised
|
||||
object*. Then the second expression is evaluated, if present,
|
||||
producing the *substituted traceback*. If no second expression is
|
||||
present, the substituted traceback is ``None``.
|
||||
|
||||
The raised object must be an instance. The class of the instance is
|
||||
the exception type, and the instance itself is the exception value. If
|
||||
the raised object is not an instance -- for example, if it is a class
|
||||
or string -- a ``TypeError`` is raised.
|
||||
|
||||
If the substituted traceback is not ``None``, it must be a traceback
|
||||
object, and it is substituted instead of the current location as the
|
||||
place where the exception occurred. If it is neither a traceback
|
||||
object nor ``None``, a ``TypeError`` is raised.
|
||||
|
||||
|
||||
Backwards Compatibility
|
||||
=======================
|
||||
|
||||
|
||||
Migration Plan
|
||||
--------------
|
||||
|
||||
|
||||
Future Statement
|
||||
''''''''''''''''
|
||||
|
||||
Under the future statement [4]_ ::
|
||||
|
||||
from __future__ import raise_with_two_args
|
||||
|
||||
the syntax and semantics of the ``raise`` statement will be as
|
||||
described above. This future feature is to appear in Python 2.4; its
|
||||
effect is to become standard in Python 3.0.
|
||||
|
||||
As the examples below illustrate, this future statement is only needed
|
||||
for code which uses the substituted traceback argument to ``raise``;
|
||||
simple exception raising does not require it.
|
||||
|
||||
|
||||
Warnings
|
||||
''''''''
|
||||
|
||||
Three new warnings [5]_, all of category ``DeprecationWarning``, are
|
||||
to be issued to point out uses of ``raise`` which will become incorrect
|
||||
under the proposed changes.
|
||||
|
||||
The first warning is issued when a ``raise`` statement is executed in
|
||||
which the first expression evaluates to a string. The message for this
|
||||
warning is::
|
||||
|
||||
raising strings will be impossible in the future
|
||||
|
||||
The second warning is issued when a ``raise`` statement is executed in
|
||||
which the first expression evaluates to a class. The message for this
|
||||
warning is::
|
||||
|
||||
raising classes will be impossible in the future
|
||||
|
||||
The third warning is issued when a ``raise`` statement with three
|
||||
expressions is compiled. (Not, note, when it is executed; this is
|
||||
important because the ``SyntaxError`` which this warning presages will
|
||||
occur at compile-time.) The message for this warning is::
|
||||
|
||||
raising with three arguments will be impossible in the future
|
||||
|
||||
These warnings are to appear in Python 2.4, and disappear in Python
|
||||
3.0, when the conditions which cause them are simply errors.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
|
||||
Code Using Implicit Instantiation
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
Code such as ::
|
||||
|
||||
class MyError(Exception):
|
||||
pass
|
||||
|
||||
raise MyError, 'spam'
|
||||
|
||||
will issue a warning when the ``raise`` statement is executed. The
|
||||
``raise`` statement should be changed to instantiate explicitly::
|
||||
|
||||
raise MyError('spam')
|
||||
|
||||
|
||||
Code Using String Exceptions
|
||||
''''''''''''''''''''''''''''
|
||||
|
||||
Code such as ::
|
||||
|
||||
MyError = 'spam'
|
||||
raise MyError, 'eggs'
|
||||
|
||||
will issue a warning when the ``raise`` statement is executed. The
|
||||
exception type should be changed to a class::
|
||||
|
||||
class MyError(Exception):
|
||||
pass
|
||||
|
||||
and, as in the previous example, the ``raise`` statement should be
|
||||
changed to instantiate explicitly ::
|
||||
|
||||
raise MyError('eggs')
|
||||
|
||||
|
||||
Code Supplying a Traceback Object
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
Code such as ::
|
||||
|
||||
raise MyError, 'spam', mytraceback
|
||||
|
||||
will issue a warning when compiled. The statement should be changed to
|
||||
::
|
||||
|
||||
raise MyError('spam'), mytraceback
|
||||
|
||||
and the future statement ::
|
||||
|
||||
from __future__ import raise_with_two_args
|
||||
|
||||
should be added at the top of the module. Note that adding this future
|
||||
statement also turns the other two warnings into errors, so the changes
|
||||
described in the previous examples must also be applied.
|
||||
|
||||
The special case ::
|
||||
|
||||
raise sys.exc_type, sys.exc_info, sys.exc_traceback
|
||||
|
||||
(which is intended to re-raise a previous exception) should be changed
|
||||
simply to ::
|
||||
|
||||
raise
|
||||
|
||||
|
||||
A Failure of the Plan
|
||||
'''''''''''''''''''''
|
||||
|
||||
It may occur that a ``raise`` statement which raises a string or
|
||||
implicitly instantiates is not executed in production or testing during
|
||||
the phase-in period for this PEP. In that case, it will not issue any
|
||||
warnings, but will instead suddenly fail one day in Python 3.0 or a
|
||||
subsequent version. (The failure is that the wrong exception gets
|
||||
raised, namely a ``TypeError`` complaining about the arguments to
|
||||
``raise``, instead of the exception intended.)
|
||||
|
||||
Such cases can be made rarer by prolonging the phase-in period; they
|
||||
cannot be made impossible short of issuing at compile-time a warning
|
||||
for every ``raise`` statement.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] "Standard Exception Classes in Python 1.5", Guido van Rossum.
|
||||
http://www.python.org/doc/essays/stdexceptions.html
|
||||
|
||||
.. [2] "Guidelines for Language Evolution", Paul Prescod.
|
||||
http://www.python.org/peps/pep-0005.html
|
||||
|
||||
.. [3] "Python Language Reference", Guido van Rossum.
|
||||
http://www.python.org/doc/current/ref/raise.html
|
||||
|
||||
.. [4] PEP 236 "Back to the __future__", Tim Peters.
|
||||
http://www.python.org/peps/pep-0236.html
|
||||
|
||||
.. [5] PEP 230 "Warning Framework", Guido van Rossum.
|
||||
http://www.python.org/peps/pep-0230.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:
|
Loading…
Reference in New Issue