PEP: 3109 Title: Raising Exceptions in Python 3000 Version: $Revision$
Last-Modified: $Date$ Author: Collin Winter <collinwinter@google.com>
Status: Final Type: Standards Track Content-Type: text/x-rst Created:
19-Jan-2006 Python-Version: 3.0 Post-History:

Abstract

This PEP introduces changes to Python's mechanisms for raising
exceptions intended to reduce both line noise and the size of the
language.

Rationale

One of Python's guiding maxims is "there should be one -- and
preferably only one -- obvious way to do it" <20>. Python 2.x's raise
statement violates this principle, permitting multiple ways of
expressing the same thought. For example, these statements are
equivalent: :

    raise E, V

    raise E(V)

There is a third form of the raise statement, allowing arbitrary
tracebacks to be attached to an exception[1]: :

    raise E, V, T

where T is a traceback. As specified in PEP 344, exception objects in
Python 3.x will possess a __traceback__ attribute, admitting this
translation of the three-expression raise statement: :

    raise E, V, T

is translated to :

    e = E(V)
    e.__traceback__ = T
    raise e

Using these translations, we can reduce the raise statement from four
forms to two:

1.  raise (with no arguments) is used to re-raise the active exception
    in an except suite.

2.  raise EXCEPTION is used to raise a new exception. This form has two
    sub-variants: EXCEPTION may be an exception class or an instance of
    an exception class; valid exception classes are BaseException and
    its subclasses (PEP 352). If EXCEPTION is a subclass, it will be
    called with no arguments to obtain an exception instance.

    To raise anything else is an error.

There is a further, more tangible benefit to be obtained through this
consolidation, as noted by A.M. Kuchling[2]. :

    PEP 8 doesn't express any preference between the
    two forms of raise statements:
    raise ValueError, 'blah'
    raise ValueError("blah")

    I like the second form better, because if the exception arguments
    are long or include string formatting, you don't need to use line
    continuation characters because of the containing parens.

The BDFL has concurred[3] and endorsed the consolidation of the several
raise forms.

Grammar Changes

In Python 3, the grammar for raise statements will change from[4] :

    raise_stmt: 'raise' [test [',' test [',' test]]]

to :

    raise_stmt: 'raise' [test]

Changes to Builtin Types

Because of its relation to exception raising, the signature for the
throw() method on generator objects will change, dropping the optional
second and third parameters. The signature thus changes (PEP 342) from :

    generator.throw(E, [V, [T]])

to :

    generator.throw(EXCEPTION)

Where EXCEPTION is either a subclass of BaseException or an instance of
a subclass of BaseException.

Semantic Changes

In Python 2, the following raise statement is legal :

    raise ((E1, (E2, E3)), E4), V

The interpreter will take the tuple's first element as the exception
type (recursively), making the above fully equivalent to :

    raise E1, V

As of Python 3.0, support for raising tuples like this will be dropped.
This change will bring raise statements into line with the throw()
method on generator objects, which already disallows this.

Compatibility Issues

All two- and three-expression raise statements will require
modification, as will all two- and three-expression throw() calls on
generators. Fortunately, the translation from Python 2.x to Python 3.x
in this case is simple and can be handled mechanically by Guido van
Rossum's 2to3 utility[5] using the raise and throw fixers ([6],[7]).

The following translations will be performed:

1.  Zero- and one-expression raise statements will be left intact.

2.  Two-expression raise statements will be converted from :

        raise E, V

    to :

        raise E(V)

    Two-expression throw() calls will be converted from :

        generator.throw(E, V)

    to :

        generator.throw(E(V))

    See point #5 for a caveat to this transformation.

3.  Three-expression raise statements will be converted from :

        raise E, V, T

    to :

        e = E(V)
        e.__traceback__ = T
        raise e

    Three-expression throw() calls will be converted from :

        generator.throw(E, V, T)

    to :

        e = E(V)
        e.__traceback__ = T
        generator.throw(e)

    See point #5 for a caveat to this transformation.

4.  Two- and three-expression raise statements where E is a tuple
    literal can be converted automatically using 2to3's raise fixer.
    raise statements where E is a non-literal tuple, e.g., the result of
    a function call, will need to be converted manually.

5.  Two- and three-expression raise statements where E is an exception
    class and V is an exception instance will need special attention.
    These cases break down into two camps:

    1.  raise E, V as a long-hand version of the zero-argument raise
        statement. As an example, assuming F is a subclass of E :

            try:
                something()
            except F as V:
                raise F(V)
            except E as V:
                handle(V)

        This would be better expressed as :

            try:
                something()
            except F:
                raise
            except E as V:
                handle(V)

    2.  raise E, V as a way of "casting" an exception to another class.
        Taking an example from distutils.compiler.unixcompiler :

            try:
                self.spawn(pp_args)
            except DistutilsExecError as msg:
                raise CompileError(msg)

        This would be better expressed as :

            try:
                self.spawn(pp_args)
            except DistutilsExecError as msg:
                raise CompileError from msg

        Using the raise ... from ... syntax introduced in PEP 344.

Implementation

This PEP was implemented in revision 57783[8].

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 coding: utf-8 End:

[1] http://docs.python.org/reference/simple_stmts.html#raise

[2] https://mail.python.org/pipermail/python-dev/2005-August/055187.html

[3] https://mail.python.org/pipermail/python-dev/2005-August/055190.html

[4] http://docs.python.org/reference/simple_stmts.html#raise

[5] http://svn.python.org/view/sandbox/trunk/2to3/

[6] http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_raise.py

[7] http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_throw.py

[8] http://svn.python.org/view/python/branches/py3k/Include/?rev=57783&view=rev