PEP: | 3109 |
---|---|
Title: | Raising Exceptions in Python 3000 |
Version: | 308ad6119277 |
Last-Modified: | 2010-01-21 01:23:17 +0000 (Thu, 21 Jan 2010) |
Author: | Collin Winter <collinwinter at google.com> |
Status: | Final |
Type: | Standards Track |
Content-Type: | text/x-rst |
Created: | 19-Jan-2006 |
Python-Version: | 3.0 |
Post-History: |
Contents
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" [1]. 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 [2]:
raise E, V, T
where T is a traceback. As specified in PEP 344 [4], 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:
raise (with no arguments) is used to re-raise the active exception in an except suite.
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 [5]. 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 [6].
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 [7] and endorsed the consolidation of the several raise forms.
Grammar Changes
In Python 3, the grammar for raise statements will change from [2]
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 [3]
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 [8] using the raise and throw fixers ([9], [10]).
The following translations will be performed:
Zero- and one-expression raise statements will be left intact.
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.
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.
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.
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:
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)
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 [11].
References
[1] | http://www.python.org/dev/peps/pep-0020/ |
[2] | (1, 2) http://docs.python.org/reference/simple_stmts.html#raise |
[3] | http://www.python.org/dev/peps/pep-0342/ |
[4] | http://www.python.org/dev/peps/pep-0344/ |
[5] | http://www.python.org/dev/peps/pep-0352/ |
[6] | http://mail.python.org/pipermail/python-dev/2005-August/055187.html |
[7] | http://mail.python.org/pipermail/python-dev/2005-August/055190.html |
[8] | http://svn.python.org/view/sandbox/trunk/2to3/ |
[9] | http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_raise.py |
[10] | http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_throw.py |
[11] | http://svn.python.org/view/python/branches/py3k/Include/?rev=57783&view=rev |
Copyright
This document has been placed in the public domain.