[Python-checkins] peps: PEP 490

victor.stinner python-checkins at python.org
Thu Mar 26 13:14:38 CET 2015


https://hg.python.org/peps/rev/b9d9a19f5964
changeset:   5744:b9d9a19f5964
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Mar 26 13:14:31 2015 +0100
summary:
  PEP 490

files:
  pep-0490.txt |  316 +++++++++++++++++++++-----------------
  1 files changed, 177 insertions(+), 139 deletions(-)


diff --git a/pep-0490.txt b/pep-0490.txt
--- a/pep-0490.txt
+++ b/pep-0490.txt
@@ -19,8 +19,8 @@
 Rationale
 =========
 
-Python 3 introduced a new killer feature: exceptions are chained by default,
-PEP 3134.
+Python 3 introduced a new killer feature: exceptions are chained by
+default, PEP 3134.
 
 Example::
 
@@ -43,9 +43,27 @@
         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.
+Exceptions are chained by default in Python code, but not in
+extensions written in C.
+
+A new private ``_PyErr_ChainExceptions()`` function was introduced in
+Python 3.4.3 and 3.5 to chain exceptions. Currently, it must be called
+explicitly to chain exceptions and its usage is not trivial.
+
+Example of ``_PyErr_ChainExceptions()`` usage from the ``zipimport``
+module to chain the previous ``OSError`` to a new ``ZipImportError``
+exception::
+
+    PyObject *exc, *val, *tb;
+    PyErr_Fetch(&exc, &val, &tb);
+    PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
+    _PyErr_ChainExceptions(exc, val, tb);
+
+This PEP proposes to also chain exceptions automatically at C level to
+stay consistent and give more information on failures to help
+debugging. The previous example becomes simply::
+
+    PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
 
 
 Proposal
@@ -54,141 +72,110 @@
 Modify PyErr_*() functions to chain exceptions
 ----------------------------------------------
 
-Modify C functions raising exceptions of the Python C API to automatically
-chain exceptions.
+Modify C functions raising exceptions of the Python C API to
+automatically chain exceptions: modify ``PyErr_SetString()``,
+``PyErr_Format()``, ``PyErr_SetNone()``, etc.
 
 
 Modify functions to not chain exceptions
 ----------------------------------------
 
-To remove the previous exception, PyErr_Clear() can be called before
-PyErr_SetString() (or other functions raising exceptions).
+Keeping the previous exception is not always interesting when the new
+exception contains information of the previous exception or even more
+information, especially when the two exceptions have the same type.
 
-Keeping the original exception is not always interesting.
+Example of an useless exception chain with ``int(str)``::
 
-Example of Python code (``os._Environ.__getitem__``) suppressing the context::
-
-    try:
-        value = self._data[self.encodekey(key)]
-    except KeyError:
-        # raise KeyError with the original key value
-        raise KeyError(key) from None
-
-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
+    TypeError: a bytes-like object is required, not 'type'
 
     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
+      File "<stdin>", line 1, in <module>
+    TypeError: int() argument must be a string, a bytes-like object or a number, not 'type'
 
-The ``OverflowError`` exception is not very useful, it can be hidden.
+The new ``TypeError`` exception contains more information than the
+previous exception. The previous exception should be hidden.
+
+The ``PyErr_Clear()`` function can be called to clear the current
+exception before raising a new exception, to not chain the current
+exception with a new exception.
 
 
 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()
+Some functions save and then restore the current exception. If a new
+exception is raised, the exception is currently displayed into
+sys.stderr or ignored depending on the function.  Some of these
+functions should be modified to chain exceptions instead.
 
 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
+* ``ptrace_enter_call()``: ignore exception
+* ``subprocess_fork_exec()``: ignore exception raised by enable_gc()
+* ``t_bootstrap()`` of the ``_thread`` module: ignore exception raised
+  by trying to display the bootstrap function to ``sys.stderr``
+* ``PyDict_GetItem()``, ``_PyDict_GetItem_KnownHash()``: ignore
+  exception raised by looking for a key in the dictionary
+* ``_PyErr_TrySetFromCause()``: ignore exception
+* ``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 exception?
+* ``call_exc_trace()``, ``call_trace_protected()``: ignore exception
+* ``remove_importlib_frames()``: ignore exception
+* ``do_mktuple()``, helper 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
 
+Examples of function displaying the new exception to ``sys.stderr``:
 
-Python C API
-------------
-
-Include/pyerror.h declares functions related to exceptions.
-
-Functions raising exceptions:
-
-* PyErr_SetNone(exc_type)
-* PyErr_SetObject(exc_type, exc_value)
-* PyErr_SetString(exc_type, message)
-* PyErr_Format(exc, format, ...)
-
-Helpers to raise specific exceptions:
-
-* PyErr_BadArgument()
-* PyErr_BadInternalCall()
-* PyErr_NoMemory()
-* PyErr_SetFromErrno(exc)
-* PyErr_SetFromWindowsErr(err)
-* PyErr_SetImportError(message, name, path)
-* _PyErr_SetKeyError(key)
-* _PyErr_TrySetFromCause(prefix_format, ...)
-
-Manage the current exception:
-
-* PyErr_Clear(): clear the current exception, like "except: pass"
-* PyErr_Fetch(exc_type, exc_value, exc_tb)
-* PyErr_Restore(exc_type, exc_value, exc_tb)
-* PyErr_GetExcInfo(exc_type, exc_value, exc_tb)
-* PyErr_SetExcInfo(exc_type, exc_value, exc_tb)
-
-Others function to handle exceptions:
-
-* PyErr_ExceptionMatches(exc): check to implement "except exc: ..."
-* PyErr_GivenExceptionMatches(exc1, exc2)
-* PyErr_NormalizeException(exc_type, exc_value, exc_tb)
-* _PyErr_ChainExceptions(exc_type, exc_value, exc_tb)
+* ``atexit_callfuncs()``: display exceptions with
+  ``PyErr_Display()`` and return the latest exception, the function
+  calls multiple callbacks and only returns the latest exception
+* ``sock_dealloc()``: log the ``ResourceWarning`` 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 the
+  ``__del__()`` method with ``PyErr_WriteUnraisable()``
+* ``PyErr_GivenExceptionMatches()``: display exception raised by
+  ``PyType_IsSubtype()`` with ``PyErr_WriteUnraisable()``
 
 
 Backward compatibility
 ======================
 
-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.
+A side effect of chaining exceptions is that exceptions store
+traceback objects which 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: an
+exception is stored in a local variable and the frame indirectly
+stored in the exception. 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 reference cycle can now be fixed with the new
+``traceback.TracebackException`` object introduced in Python 3.5. It
+stores 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.
+The ``asyncio`` is impacted by the reference cycle issue. This module
+is also maintained outside Python standard library to release a
+version for Python 3.3.  ``traceback.TracebackException`` will maybe
+be backported in a private ``asyncio`` module to fix reference cycle
+issues.
 
 
 Alternatives
@@ -197,29 +184,90 @@
 No change
 ---------
 
-Python 3.5 introduces a new private ``_PyErr_ChainExceptions()`` function which
-is enough to chain manually exceptions.
+A new private ``_PyErr_ChainExceptions()`` function is enough to chain
+manually exceptions.
+
+Exceptions will only be chained explicitly where it makes sense.
 
 
 New helpers to chain exceptions
 -------------------------------
 
-Functions like ``PyErr_SetString()`` don't chain automatically exceptions. To
-make usage of ``_PyErr_ChainExceptions()`` easier, new functions are added:
+Functions like ``PyErr_SetString()`` don't chain automatically
+exceptions. To make the usage of ``_PyErr_ChainExceptions()`` easier,
+new private functions are added:
 
-* PyErr_SetStringChain(exc_type, message)
-* PyErr_FormatChaine(exc_type, format, ...)
-* PyErr_SetNoneChain(exc_type)
-* PyErr_SetObjectChain(exc_type, exc_value)
+* ``_PyErr_SetStringChain(exc_type, message)``
+* ``_PyErr_FormatChain(exc_type, format, ...)``
+* ``_PyErr_SetNoneChain(exc_type)``
+* ``_PyErr_SetObjectChain(exc_type, exc_value)``
 
-Helper functions like _PyErr_SetKeyError(key) or PyErr_SetImportError(message,
-name, path) don't chain exceptions. The generic
-_PyErr_ChainExceptions(exc_type, exc_value, exc_tb) should be used.
+Helper functions to raise specific exceptions like
+``_PyErr_SetKeyError(key)`` or ``PyErr_SetImportError(message, name,
+path)`` don't chain exceptions.  The generic
+``_PyErr_ChainExceptions(exc_type, exc_value, exc_tb)`` should be used
+to chain exceptions with these helper functions.
 
 
 Appendix
 ========
 
+PEPs
+----
+
+* `PEP 3134 -- Exception Chaining and Embedded Tracebacks
+  <https://www.python.org/dev/peps/pep-3134/>`_ (Python 3.0):
+  new ``__context__`` and ``__cause__`` attributes for exceptions
+* `PEP 415 - Implement context suppression with exception attributes
+  <https://www.python.org/dev/peps/pep-0415/>`_ (Python 3.3):
+  ``raise exc from None``
+* `PEP 409 - Suppressing exception context
+  <https://www.python.org/dev/peps/pep-0409/>`_
+  (superseded by the PEP 415)
+
+
+Python C API
+------------
+
+The header file ``Include/pyerror.h`` declares functions related to
+exceptions.
+
+Functions raising exceptions:
+
+* ``PyErr_SetNone(exc_type)``
+* ``PyErr_SetObject(exc_type, exc_value)``
+* ``PyErr_SetString(exc_type, message)``
+* ``PyErr_Format(exc, format, ...)``
+
+Helpers to raise specific exceptions:
+
+* ``PyErr_BadArgument()``
+* ``PyErr_BadInternalCall()``
+* ``PyErr_NoMemory()``
+* ``PyErr_SetFromErrno(exc)``
+* ``PyErr_SetFromWindowsErr(err)``
+* ``PyErr_SetImportError(message, name, path)``
+* ``_PyErr_SetKeyError(key)``
+* ``_PyErr_TrySetFromCause(prefix_format, ...)``
+
+Manage the current exception:
+
+* ``PyErr_Clear()``: clear the current exception,
+  like ``except: pass``
+* ``PyErr_Fetch(exc_type, exc_value, exc_tb)``
+* ``PyErr_Restore(exc_type, exc_value, exc_tb)``
+* ``PyErr_GetExcInfo(exc_type, exc_value, exc_tb)``
+* ``PyErr_SetExcInfo(exc_type, exc_value, exc_tb)``
+
+Others function to handle exceptions:
+
+* ``PyErr_ExceptionMatches(exc)``: check to implement
+  ``except exc:  ...``
+* ``PyErr_GivenExceptionMatches(exc1, exc2)``
+* ``PyErr_NormalizeException(exc_type, exc_value, exc_tb)``
+* ``_PyErr_ChainExceptions(exc_type, exc_value, exc_tb)``
+
+
 Python Issues
 -------------
 
@@ -230,14 +278,17 @@
 * `Issue #23696: zipimport: chain ImportError to OSError
   <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/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>`_
+* `Issue #23782: Leak in _PyTraceback_Add
+  <http://bugs.python.org/issue23782>`_
 
-Changes preventing loosing exceptions:
+Changes preventing to loose exceptions:
 
 * `Issue #23571: Raise SystemError if a function returns a result with an
   exception set <http://bugs.python.org/issue23571>`_
@@ -245,19 +296,6 @@
   <http://bugs.python.org/issue18408>`_
 
 
-PEPs
-----
-
-* `PEP 3134 -- Exception Chaining and Embedded Tracebacks
-  <https://www.python.org/dev/peps/pep-3134/>`_ (Python 3.0):
-  new ``__context__`` and ``__cause__`` attributes for exceptions
-* `PEP 415 - Implement context suppression with exception attributes
-  <https://www.python.org/dev/peps/pep-0415/>`_ (Python 3.3):
-  ``raise exc from None``
-* `PEP 409 - Suppressing exception context
-  <https://www.python.org/dev/peps/pep-0409/>`_ (superseded by the PEP 415)
-
-
 Copyright
 =========
 

-- 
Repository URL: https://hg.python.org/peps


More information about the Python-checkins mailing list