Add "raise ... from" statement.
Add reference to Perl 6 exception RFC.
This commit is contained in:
parent
22bae2bbd0
commit
3d514b0e42
115
pep-0344.txt
115
pep-0344.txt
|
@ -15,7 +15,8 @@ Abstract
|
||||||
This PEP proposes three standard attributes on exception instances:
|
This PEP proposes three standard attributes on exception instances:
|
||||||
the '__context__' attribute for implicitly chained exceptions, the
|
the '__context__' attribute for implicitly chained exceptions, the
|
||||||
'__cause__' attribute for explicitly chained exceptions, and the
|
'__cause__' attribute for explicitly chained exceptions, and the
|
||||||
'__traceback__' attribute for the traceback.
|
'__traceback__' attribute for the traceback. A new "raise ... from"
|
||||||
|
statement sets the '__cause__' attribute.
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
|
@ -68,10 +69,11 @@ History
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
|
|
||||||
This PEP distinguishes implicit chaining from explicit chaining of
|
The Python-Dev discussions revealed interest in exception chaining
|
||||||
exceptions because the unexpected raising of a secondary exception
|
for two quite different purposes. To handle the unexpected raising
|
||||||
and the intentional translation of an exception are two different
|
of a secondary exception, the exception must be retained implicitly.
|
||||||
situations deserving quite different interpretations.
|
To support intentional translation of an exception, there must be a
|
||||||
|
way to chain exceptions explicitly. This PEP addresses both.
|
||||||
|
|
||||||
Several attribute names for chained exceptions have been suggested
|
Several attribute names for chained exceptions have been suggested
|
||||||
on Python-Dev [2], including 'cause', 'antecedent', 'reason',
|
on Python-Dev [2], including 'cause', 'antecedent', 'reason',
|
||||||
|
@ -84,9 +86,8 @@ Rationale
|
||||||
occurs in the context of handling another exception.
|
occurs in the context of handling another exception.
|
||||||
|
|
||||||
This PEP suggests names with leading and trailing double-underscores
|
This PEP suggests names with leading and trailing double-underscores
|
||||||
for '__context__' and '__traceback__' because the attributes are set
|
for these three attributes because they are set by the Python VM.
|
||||||
by the Python VM. The name '__cause__' is not set automatically by
|
Only in very special cases should they be set by normal assignment.
|
||||||
the VM, but it seems confusing and collision-prone to use 'cause'.
|
|
||||||
|
|
||||||
This PEP handles exceptions that occur during 'except' blocks and
|
This PEP handles exceptions that occur during 'except' blocks and
|
||||||
'finally' blocks in the same way. Reading the traceback makes it
|
'finally' blocks in the same way. Reading the traceback makes it
|
||||||
|
@ -98,7 +99,7 @@ Rationale
|
||||||
raised exception for compatibility with current behaviour.
|
raised exception for compatibility with current behaviour.
|
||||||
|
|
||||||
This PEP proposes that tracebacks display the outermost exception
|
This PEP proposes that tracebacks display the outermost exception
|
||||||
last, because it would be consistent with the chronological order
|
last, because this would be consistent with the chronological order
|
||||||
of tracebacks (from oldest to most recent frame) and because the
|
of tracebacks (from oldest to most recent frame) and because the
|
||||||
actual thrown exception is easier to find on the last line.
|
actual thrown exception is easier to find on the last line.
|
||||||
|
|
||||||
|
@ -109,22 +110,21 @@ Rationale
|
||||||
As for other languages, Java and Ruby both discard the original
|
As for other languages, Java and Ruby both discard the original
|
||||||
exception when another exception occurs in a 'catch'/'rescue' or
|
exception when another exception occurs in a 'catch'/'rescue' or
|
||||||
'finally'/'ensure' clause. Perl 5 lacks built-in structured
|
'finally'/'ensure' clause. Perl 5 lacks built-in structured
|
||||||
exception handling. For Perl 6, RFC 88 proposes an exception
|
exception handling. For Perl 6, RFC 88 [9] proposes an exception
|
||||||
mechanism that retains chained exceptions in an array named @@.
|
mechanism that implicitly retains chained exceptions in an array
|
||||||
In that RFC, the most recently raised exception is exposed for
|
named @@. In that RFC, the most recently raised exception is
|
||||||
matching, as in this PEP; also, arbitrary expressions (possibly
|
exposed for matching, as in this PEP; also, arbitrary expressions
|
||||||
involving @@) can be evaluated for exception matching.
|
(possibly involving @@) can be evaluated for exception matching.
|
||||||
|
|
||||||
Exceptions in C# contain a read-only 'InnerException' property that
|
Exceptions in C# contain a read-only 'InnerException' property that
|
||||||
may point to another exception [9]. According to its documentation,
|
may point to another exception. Its documentation [10] says that
|
||||||
"When an exception X is thrown as a direct result of a previous
|
"When an exception X is thrown as a direct result of a previous
|
||||||
exception Y, the InnerException property of X should contain a
|
exception Y, the InnerException property of X should contain a
|
||||||
reference to Y." This property is not set by the VM automatically;
|
reference to Y." This property is not set by the VM automatically;
|
||||||
rather, all exception constructors take an optional 'innerException'
|
rather, all exception constructors take an optional 'innerException'
|
||||||
argument to set it explicitly. The '__cause__' attribute fulfills
|
argument to set it explicitly. The '__cause__' attribute fulfills
|
||||||
the same purpose as InnerException, but this PEP proposes adding a
|
the same purpose as InnerException, but this PEP proposes a new form
|
||||||
single method to the base Exception class rather than extending the
|
of 'raise' rather than extending the constructors of all exceptions.
|
||||||
constructors of all exceptions.
|
|
||||||
|
|
||||||
The reason all three of these attributes are presented together in
|
The reason all three of these attributes are presented together in
|
||||||
one proposal is that the '__traceback__' attribute provides
|
one proposal is that the '__traceback__' attribute provides
|
||||||
|
@ -207,13 +207,15 @@ Implicit Exception Chaining
|
||||||
Explicit Exception Chaining
|
Explicit Exception Chaining
|
||||||
|
|
||||||
The '__cause__' attribute on exception objects is always initialized
|
The '__cause__' attribute on exception objects is always initialized
|
||||||
to None. It is set by calling the 'setcause' method, a new method
|
to None. It is set by a new form of the 'raise' statement:
|
||||||
defined on the base Exception class. For convenience, this method
|
|
||||||
returns the exception itself:
|
|
||||||
|
|
||||||
def setcause(self, cause):
|
raise EXCEPTION from CAUSE
|
||||||
self.__cause__ = cause
|
|
||||||
return self
|
which is equivalent to:
|
||||||
|
|
||||||
|
exc = EXCEPTION
|
||||||
|
exc.__cause__ = CAUSE
|
||||||
|
raise exc
|
||||||
|
|
||||||
In the following example, a database provides implementations for a
|
In the following example, a database provides implementations for a
|
||||||
few different kinds of storage, with file storage as one kind. The
|
few different kinds of storage, with file storage as one kind. The
|
||||||
|
@ -229,7 +231,11 @@ Explicit Exception Chaining
|
||||||
try:
|
try:
|
||||||
self.file = open(filename)
|
self.file = open(filename)
|
||||||
except IOError, exc:
|
except IOError, exc:
|
||||||
raise DatabaseError('failed to open').setcause(exc)
|
raise DatabaseError('failed to open') from exc
|
||||||
|
|
||||||
|
If the call to open() raises an exception, the problem will be
|
||||||
|
reported as a DatabaseError, with a __cause__ attribute that reveals
|
||||||
|
the IOError as the original cause.
|
||||||
|
|
||||||
|
|
||||||
Traceback Attribute
|
Traceback Attribute
|
||||||
|
@ -297,7 +303,10 @@ Enhanced Reporting
|
||||||
exc, link = exc.__context__, 'During handling...'
|
exc, link = exc.__context__, 'During handling...'
|
||||||
for exc, link in reversed(chain):
|
for exc, link in reversed(chain):
|
||||||
print_exc(exc)
|
print_exc(exc)
|
||||||
print '\n' + link + '\n'
|
if link:
|
||||||
|
print
|
||||||
|
print link
|
||||||
|
print
|
||||||
|
|
||||||
In the 'traceback' module, the format_exception, print_exception,
|
In the 'traceback' module, the format_exception, print_exception,
|
||||||
print_exc, and print_last functions will be updated to accept an
|
print_exc, and print_last functions will be updated to accept an
|
||||||
|
@ -320,7 +329,8 @@ C API
|
||||||
A new API function, PyErr_SetContext(context), will help C
|
A new API function, PyErr_SetContext(context), will help C
|
||||||
programmers provide chained exception information. This function
|
programmers provide chained exception information. This function
|
||||||
will first normalize the current exception so it is an instance,
|
will first normalize the current exception so it is an instance,
|
||||||
then set its '__context__' attribute.
|
then set its '__context__' attribute. A similar API function,
|
||||||
|
PyErr_SetCause(cause), will set the '__cause__' attribute.
|
||||||
|
|
||||||
|
|
||||||
Compatibility
|
Compatibility
|
||||||
|
@ -328,28 +338,50 @@ Compatibility
|
||||||
Chained exceptions expose the type of the most recent exception, so
|
Chained exceptions expose the type of the most recent exception, so
|
||||||
they will still match the same 'except' clauses as they do now.
|
they will still match the same 'except' clauses as they do now.
|
||||||
|
|
||||||
The proposed changes should not break any code unless the code sets
|
The proposed changes should not break any code unless it sets or
|
||||||
or uses attributes named '__context__', '__cause__', 'setcause', or
|
uses attributes named '__context__', '__cause__', or '__traceback__'
|
||||||
'__traceback__' on exception instances. As of 2005-05-12, the
|
on exception instances. As of 2005-05-12, the Python standard
|
||||||
Python standard library contains no mention of such attributes.
|
library contains no mention of such attributes.
|
||||||
|
|
||||||
|
|
||||||
Open Issues
|
Open Issues
|
||||||
|
|
||||||
Walter Dörwald [10] expressed a desire to attach extra information
|
Walter Dörwald [11] expressed a desire to attach extra information
|
||||||
to an exception during its upward propagation without changing its
|
to an exception during its upward propagation without changing its
|
||||||
type. This could be a useful feature, but it is not addressed by
|
type. This could be a useful feature, but it is not addressed by
|
||||||
this PEP. It could conceivably be addressed by a separate PEP
|
this PEP. It could conceivably be addressed by a separate PEP
|
||||||
establishing conventions for other informational attributes on
|
establishing conventions for other informational attributes on
|
||||||
exceptions.
|
exceptions.
|
||||||
|
|
||||||
It is not clear whether the '__context__' and '__cause__' features
|
|
||||||
proposed here would be sufficient to cover all the use cases that
|
|
||||||
Raymond Hettinger [1] originally had in mind.
|
|
||||||
|
|
||||||
As written, this PEP makes it impossible to suppress '__context__',
|
As written, this PEP makes it impossible to suppress '__context__',
|
||||||
since setting exc.__context__ to None will only result in it being
|
since setting exc.__context__ to None in an 'except' or 'finally'
|
||||||
set again the moment that exc is raised.
|
clause will only result in it being set again when exc is raised.
|
||||||
|
|
||||||
|
To improve encapsulation, library implementors may want to wrap all
|
||||||
|
implementation-level exceptions with an application-level exception.
|
||||||
|
One could try to wrap exceptions by writing this:
|
||||||
|
|
||||||
|
try:
|
||||||
|
... implementation may raise an exception ...
|
||||||
|
except:
|
||||||
|
import sys
|
||||||
|
raise ApplicationError from sys.exc_value
|
||||||
|
|
||||||
|
or this:
|
||||||
|
|
||||||
|
try:
|
||||||
|
... implementation may raise an exception ...
|
||||||
|
except Exception, exc:
|
||||||
|
raise ApplicationError from exc
|
||||||
|
|
||||||
|
but both are somewhat flawed. It would be nice to be able to name
|
||||||
|
the current exception in a catch-all 'except' clause, but that isn't
|
||||||
|
addressed here. Such a feature would allow something like this:
|
||||||
|
|
||||||
|
try:
|
||||||
|
... implementation may raise an exception ...
|
||||||
|
except *, exc:
|
||||||
|
raise ApplicationError from exc
|
||||||
|
|
||||||
The exception context is lost when a 'yield' statement is executed;
|
The exception context is lost when a 'yield' statement is executed;
|
||||||
resuming the frame after the 'yield' does not restore the context.
|
resuming the frame after the 'yield' does not restore the context.
|
||||||
|
@ -442,10 +474,13 @@ References
|
||||||
[8] Guido van Rossum discusses automatic chaining in PyErr_Set*
|
[8] Guido van Rossum discusses automatic chaining in PyErr_Set*
|
||||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||||
|
|
||||||
[9] MSDN .NET Framework Library, "Exception.InnerException Property"
|
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||||
|
http://dev.perl.org/perl6/rfc/88.html
|
||||||
|
|
||||||
|
[10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||||
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemexceptionclassinnerexceptiontopic.asp
|
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemexceptionclassinnerexceptiontopic.asp
|
||||||
|
|
||||||
[10] Walter Dörwald suggests wrapping exceptions to add details
|
[11] Walter Dörwald suggests wrapping exceptions to add details
|
||||||
http://mail.python.org/pipermail/python-dev/2003-June/036148.html
|
http://mail.python.org/pipermail/python-dev/2003-June/036148.html
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue