Skip to content

Commit 33a859b

Browse files
committed
bpo-44938: Expose PyErr_ChainExceptions in the stable API
1 parent c2c857b commit 33a859b

20 files changed

Lines changed: 64 additions & 21 deletions

‎Doc/c-api/exceptions.rst‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,41 @@ For convenience, some of these functions will always return a
125125
126126
.. versionadded:: 3.5
127127
128+
.. c:function:: void PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
129+
130+
Set the provided exception as the cause for the current exception. If there is no exception
131+
currently set (:c:func:`PyErr_Occurred` returns ``0``) this function behaves as like :c:func:`PyErr_Restore`, but if an exception is already set, set the context
132+
associated with it.
133+
134+
The exception type should be a class. Do not pass an invalid exception type or value. This call
135+
takes away a reference to each object: you must own a reference to each object before the call
136+
and after the call you no longer own these references.
137+
138+
.. warning::
139+
140+
The caller is responsible for ensuring that this call won't create
141+
any cycles in the exception context chain.
142+
143+
See :ref:`tut-exception-chaining` for more information regarding the semantics of exception
144+
chaining.
145+
146+
Example usage:
147+
148+
.. code-block:: c
149+
150+
PyObject *type, *value, *traceback;
151+
152+
/* Fetch the original exception */
153+
PyErr_Fetch(&type, &value, &traceback);
154+
155+
/* ... code that might produce other errors ... */
156+
157+
/* Set the original exception as the cause for the new exception */
158+
PyErr_ChainExceptions(type, value, traceback);
159+
160+
161+
.. versionadded:: 3.11
162+
128163
129164
.. c:function:: void PyErr_SetNone(PyObject *type)
130165

‎Doc/whatsnew/3.11.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ Changes in the Python API
295295

296296
C API Changes
297297
=============
298+
299+
* Add a new :c:func:`PyErr_ChainExceptions` function that allows to chain exceptions
300+
using the C-API. See :ref:`tut-exception-chaining` for more information
301+
regarding the semantics of exception chaining. (Contributed by Pablo Galindo in :issue:`44938`.)
302+
298303
* Add a new :c:func:`PyType_GetName` function to get type's short name.
299304
(Contributed by Hai Shi in :issue:`42035`.)
300305

‎Include/cpython/pyerrors.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, Py
8989

9090
/* Context manipulation (PEP 3134) */
9191

92-
PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *);
93-
9492
/* Like PyErr_Format(), but saves current exception as __context__ and
9593
__cause__.
9694
*/

‎Include/pyerrors.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *);
2222
PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **);
2323
PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *);
2424
#endif
25+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030B0000
26+
PyAPI_FUNC(void) PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *);
27+
#endif
2528

2629
/* Defined in Python/pylifecycle.c
2730
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Expose :c:func:`PyErr_ChainExceptions` in the stable API. Patch by Pablo
2+
Galindo.

‎Modules/_asynciomodule.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2955,7 +2955,7 @@ task_step(TaskObj *task, PyObject *exc)
29552955
PyObject *et, *ev, *tb;
29562956
PyErr_Fetch(&et, &ev, &tb);
29572957
leave_task(task->task_loop, (PyObject*)task);
2958-
_PyErr_ChainExceptions(et, ev, tb);
2958+
PyErr_ChainExceptions(et, ev, tb);
29592959
return NULL;
29602960
}
29612961
else {

‎Modules/_io/_iomodule.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
497497
PyObject *exc, *val, *tb, *close_result;
498498
PyErr_Fetch(&exc, &val, &tb);
499499
close_result = _PyObject_CallMethodIdNoArgs(result, &PyId_close);
500-
_PyErr_ChainExceptions(exc, val, tb);
500+
PyErr_ChainExceptions(exc, val, tb);
501501
Py_XDECREF(close_result);
502502
Py_DECREF(result);
503503
}

‎Modules/_io/bufferedio.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ buffered_close(buffered *self, PyObject *args)
526526
}
527527

528528
if (exc != NULL) {
529-
_PyErr_ChainExceptions(exc, val, tb);
529+
PyErr_ChainExceptions(exc, val, tb);
530530
Py_CLEAR(res);
531531
}
532532

@@ -2239,7 +2239,7 @@ bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored))
22392239
Py_DECREF(ret);
22402240
ret = _forward_call(self->reader, &PyId_close, NULL);
22412241
if (exc != NULL) {
2242-
_PyErr_ChainExceptions(exc, val, tb);
2242+
PyErr_ChainExceptions(exc, val, tb);
22432243
Py_CLEAR(ret);
22442244
}
22452245
return ret;

‎Modules/_io/fileio.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ _io_FileIO_close_impl(fileio *self)
163163
}
164164
rc = internal_close(self);
165165
if (res == NULL)
166-
_PyErr_ChainExceptions(exc, val, tb);
166+
PyErr_ChainExceptions(exc, val, tb);
167167
if (rc < 0)
168168
Py_CLEAR(res);
169169
return res;

‎Modules/_io/iobase.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ _io__IOBase_close_impl(PyObject *self)
240240

241241
PyErr_Fetch(&exc, &val, &tb);
242242
rc = _PyObject_SetAttrId(self, &PyId___IOBase_closed, Py_True);
243-
_PyErr_ChainExceptions(exc, val, tb);
243+
PyErr_ChainExceptions(exc, val, tb);
244244
if (rc < 0) {
245245
Py_CLEAR(res);
246246
}

0 commit comments

Comments
 (0)