[Python-checkins] python/nondist/peps pep-0317.txt,NONE,1.1

goodger@users.sourceforge.net goodger@users.sourceforge.net
Sun, 08 Jun 2003 21:04:33 -0700


Update of /cvsroot/python/python/nondist/peps
In directory sc8-pr-cvs1:/tmp/cvs-serv26849

Added Files:
	pep-0317.txt 
Log Message:
Eliminate Implicit Exception Instantiation, by Steven Taschuk

--- NEW FILE: pep-0317.txt ---
PEP: 317
Title: Eliminate Implicit Exception Instantiation
Version: $Revision: 1.1 $
Last-Modified: $Date: 2003/06/09 04:04:31 $
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: