This commit is contained in:
Victor Stinner 2015-03-26 11:14:48 +01:00
parent 0a0eae4b07
commit d1d9f7a914
1 changed files with 112 additions and 11 deletions

View File

@ -13,22 +13,46 @@ Python-Version: 3.5
Abstract
========
Python 3 chained exceptions: PEP 3134. This PEP makes the same change at C
level.
Chain exceptions at C level, as already done at Python level.
Rationale
=========
Python 3 chained exceptions: PEP 3134. This PEP makes the same change at C
level.
Python 3 introduced a new killer feature: exceptions are chained by default,
PEP 3134.
Example::
try:
raise TypeError("err1")
except TypeError:
raise ValueError("err2")
Output::
Traceback (most recent call last):
File "test.py", line 2, in <module>
raise TypeError("err1")
TypeError: err1
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 4, in <module>
raise ValueError("err2")
ValueError: err2
Exceptions are chained by default in Python code, but not in extensions written
in C. This PEP proposes to also chain exceptions automatically at C level to
stay consistent.
Proposal
========
Modify PyErr_*() funnctions to chain exceptions
-----------------------------------------------
Modify PyErr_*() functions to chain exceptions
----------------------------------------------
Modify C functions raising exceptions of the Python C API to automatically
chain exceptions.
@ -37,8 +61,12 @@ chain exceptions.
Modify functions to not chain exceptions
----------------------------------------
Keep the original exception is not always interesting. Example in Python
(``os._Environ.__getitem__``)::
To remove the previous exception, PyErr_Clear() can be called before
PyErr_SetString() (or other functions raising exceptions).
Keeping the original exception is not always interesting.
Example of Python code (``os._Environ.__getitem__``) suppressing the context::
try:
value = self._data[self.encodekey(key)]
@ -46,8 +74,63 @@ Keep the original exception is not always interesting. Example in Python
# raise KeyError with the original key value
raise KeyError(key) from None
To remove the previous exception, PyErr_Clear() can be called before
PyErr_SetString() (or other functions raising exceptions).
Example of an exception chain raised by the struct module with exceptions
chained by default::
$ python3.5 -c 'import struct; struct.pack("b", 2**100)'
OverflowError: Python int too large to convert to C long
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 1, in <module>
struct.error: argument out of range
The ``OverflowError`` exception is not very useful, it can be hidden.
Modify functions to chain exceptions
------------------------------------
Some functions save and then restore the current exception. If a new exception
is raised, the exception is displayed or ignored depending on the function.
Maybe some of these functions should be modified to chain exceptions instead?
Examples of function displaying the new exception:
* atexit_callfuncs(): display exceptions with PyErr_Display() and return the
latest exception
* sock_dealloc(): log warning exception with PyErr_WriteUnraisable()
* slot_tp_del(): display exception with PyErr_WriteUnraisable()
* _PyGen_Finalize(): display gen_close() exception with PyErr_WriteUnraisable()
* slot_tp_finalize(): display exception raised by __del__() with
PyErr_WriteUnraisable()
* PyErr_GivenExceptionMatches(): display exception raised by PyType_IsSubtype()
with PyErr_WriteUnraisable()
Examples of function ignoring the new exception(s):
* ptrace_enter_call(): ignore exception
* subprocess_fork_exec(): ignore exception raised by enable_gc()
* t_bootstrap() of the _thread module: ignore raised exception when trying to
display the bootstrap function to sys.stderr
* PyDict_GetItem(), _PyDict_GetItem_KnownHash(): ignore exception raiesd when
looking for a key
* _PyErr_TrySetFromCause(): ignore ecxeption
* PyFrame_LocalsToFast(): ignore exception raised by dict_to_map()
* _PyObject_Dump(): ignore exception. _PyObject_Dump() is used to debug, to
inspect a running process, it should not modify the Python state.
* Py_ReprLeave(): ignore exception "because there is no way to report them"
* type_dealloc(): ignore exception raised by remove_all_subclasses()
* PyObject_ClearWeakRefs(): ignore exceptions?
* call_exc_trace(), call_trace_protected(): ignore exception
* remove_importlib_frames(): ignore exception
* do_mktuple(), helped used by Py_BuildValue() for example: ignore exception?
* flush_io(): ignore exception
* sys_write(), sys_format(): ignore exception
* _PyTraceback_Add(): ignore exception
* PyTraceBack_Print(): ignore exception
Python C API
@ -92,7 +175,20 @@ Others function to handle exceptions:
Backward compatibility
======================
XXX
A side effect of chaining exceptions is that exceptions store traceback
objects. Traceback objects store frame objects which store local variables.
Local variables are kept alive by exceptions. A common issue is a reference
cycle between local variables and exceptions. The cycle only impacts
applications storing exceptions.
The reference cycle can now be fixed with the new traceback.TracebackException
object introduced in Python 3.5 which store informations required to format a
full textual traceback without storing local variables.
The asyncio is impacted by the reference cycle issue, but this module is also
maintained outside Python standard library to release a version for Python 3.3.
traceback.TracebackException will probably be backward in a private asyncio
module to fix the issue.
Alternatives
@ -135,6 +231,11 @@ Chain exceptions:
<http://bugs.python.org/issue23696>`_
* `Issue #21715: Chaining exceptions at C level
<http://bugs.python.org/issue21715>`_: added _PyErr_ChainExceptions()
* `Issue #18488: sqlite: finalize() method of user function may be called with
an exception set if a call to step() method failed
<http://bugs.python.org/issue18488>`_
* `Issue #23781: Add private _PyErr_ReplaceException() in 2.7
<http://bugs.python.org/issue23781>`_
Changes preventing loosing exceptions: