PEP 3109 – Raising Exceptions in Python 3000
- PEP
- 3109
- Title
- Raising Exceptions in Python 3000
- Author
- Collin Winter <collinwinter at google.com>
- Status
- Final
- Type
- Standards Track
- 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”. 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:
raise
(with no arguments) is used to re-raise the active exception in anexcept
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 (PEP 352). IfEXCEPTION
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 [1]
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 [4] using the raise
and
throw
fixers ([5], [6]).
The following translations will be performed:
- Zero- and one-expression
raise
statements will be left intact. - Two-expression
raise
statements will be converted fromraise E, V
to
raise E(V)
Two-expression
throw()
calls will be converted fromgenerator.throw(E, V)
to
generator.throw(E(V))
See point #5 for a caveat to this transformation.
- Three-expression
raise
statements will be converted fromraise E, V, T
to
e = E(V) e.__traceback__ = T raise e
Three-expression
throw()
calls will be converted fromgenerator.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 whereE
is a tuple literal can be converted automatically using2to3
’sraise
fixer.raise
statements whereE
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 whereE
is an exception class andV
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-argumentraise
statement. As an example, assuming F is a subclass of Etry: 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.unixcompilertry: 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 [7].
References
- [1] (1, 2)
- 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://svn.python.org/view/sandbox/trunk/2to3/
- [5]
- http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_raise.py
- [6]
- http://svn.python.org/view/sandbox/trunk/2to3/fixes/fix_throw.py
- [7]
- http://svn.python.org/view/python/branches/py3k/Include/?rev=57783&view=rev
Copyright
This document has been placed in the public domain.
Source: https://github.com/python-discord/peps/blob/main/pep-3109.txt
Last modified: 2022-01-21 11:03:51 GMT