PEP 490
This commit is contained in:
parent
0a0eae4b07
commit
d1d9f7a914
123
pep-0490.txt
123
pep-0490.txt
|
@ -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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue