287 lines
7.4 KiB
Plaintext
287 lines
7.4 KiB
Plaintext
PEP: 3109
|
||
Title: Raising Exceptions in Python 3000
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Collin Winter <collinw@gmail.com>
|
||
Status: Draft
|
||
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" [#zen]_. 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 [#grammar]_: ::
|
||
|
||
raise E, V, T
|
||
|
||
where T is a traceback. As specified in PEP 344 [#pep344]_,
|
||
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 [#pep352]_. 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 [#amk-line-noise]_. ::
|
||
|
||
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 [#guido-declaration]_ and endorsed the
|
||
consolidation of the several ``raise`` forms.
|
||
|
||
|
||
Grammar Changes
|
||
===============
|
||
|
||
In Python 3, the grammar for ``raise`` statements will change
|
||
from [#grammar]_ ::
|
||
|
||
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
|
||
from [#throw-sig]_ ::
|
||
|
||
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 [#2to3]_ using the ``raise`` and
|
||
``throw`` fixers ([#raise-fixer]_, [#throw-fixer]_).
|
||
|
||
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.
|
||
|
||
|
||
References
|
||
==========
|
||
|
||
.. [#zen]
|
||
http://www.python.org/dev/peps/pep-0020/
|
||
|
||
.. [#grammar]
|
||
http://www.python.org/doc/current/ref/raise.html
|
||
|
||
.. [#throw-sig]
|
||
http://www.python.org/dev/peps/pep-0342/
|
||
|
||
.. [#pep344]
|
||
http://www.python.org/dev/peps/pep-0344/
|
||
|
||
.. [#pep352]
|
||
http://www.python.org/dev/peps/pep-0352/
|
||
|
||
.. [#amk-line-noise]
|
||
http://mail.python.org/pipermail/python-dev/2005-August/055187.html
|
||
|
||
.. [#guido-declaration]
|
||
http://mail.python.org/pipermail/python-dev/2005-August/055190.html
|
||
|
||
.. [#2to3]
|
||
http://svn.python.org/view/sandbox/trunk/2to3/
|
||
|
||
.. [#raise-fixer]
|
||
http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_raise.py
|
||
|
||
.. [#throw-fixer]
|
||
http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_throw.py
|
||
|
||
|
||
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:
|