Skip to content

Commit 467ab19

Browse files
Issue python#28410: Added _PyErr_FormatFromCause() -- the helper for raising
new exception with setting current exception as __cause__. _PyErr_FormatFromCause(exception, format, args...) is equivalent to Python raise exception(format % args) from sys.exc_info()[1]
1 parent b0426cd commit 467ab19

7 files changed

Lines changed: 70 additions & 53 deletions

File tree

Include/pyerrors.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ PyAPI_FUNC(PyObject *) PyErr_FormatV(
255255
va_list vargs);
256256
#endif
257257

258+
#ifndef Py_LIMITED_API
259+
/* Like PyErr_Format(), but saves current exception as __context__ and
260+
__cause__.
261+
*/
262+
PyAPI_FUNC(PyObject *) _PyErr_FormatFromCause(
263+
PyObject *exception,
264+
const char *format, /* ASCII-encoded string */
265+
...
266+
);
267+
#endif
268+
258269
#ifdef MS_WINDOWS
259270
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(
260271
int ierr,

Lib/test/test_capi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ def test_return_result_with_error(self):
222222
br'result with an error set\n'
223223
br'ValueError\n'
224224
br'\n'
225-
br'During handling of the above exception, '
226-
br'another exception occurred:\n'
225+
br'The above exception was the direct cause '
226+
br'of the following exception:\n'
227227
br'\n'
228228
br'SystemError: <built-in '
229229
br'function return_result_with_error> '

Modules/zipimport.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -907,10 +907,8 @@ read_directory(PyObject *archive)
907907
fp = _Py_fopen_obj(archive, "rb");
908908
if (fp == NULL) {
909909
if (PyErr_ExceptionMatches(PyExc_OSError)) {
910-
PyObject *exc, *val, *tb;
911-
PyErr_Fetch(&exc, &val, &tb);
912-
PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
913-
_PyErr_ChainExceptions(exc, val, tb);
910+
_PyErr_FormatFromCause(ZipImportError,
911+
"can't open Zip file: %R", archive);
914912
}
915913
return NULL;
916914
}

Objects/abstract.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,20 +2198,18 @@ _Py_CheckFunctionResult(PyObject *func, PyObject *result, const char *where)
21982198
}
21992199
else {
22002200
if (err_occurred) {
2201-
PyObject *exc, *val, *tb;
2202-
PyErr_Fetch(&exc, &val, &tb);
2203-
22042201
Py_DECREF(result);
22052202

2206-
if (func)
2207-
PyErr_Format(PyExc_SystemError,
2208-
"%R returned a result with an error set",
2209-
func);
2210-
else
2211-
PyErr_Format(PyExc_SystemError,
2212-
"%s returned a result with an error set",
2213-
where);
2214-
_PyErr_ChainExceptions(exc, val, tb);
2203+
if (func) {
2204+
_PyErr_FormatFromCause(PyExc_SystemError,
2205+
"%R returned a result with an error set",
2206+
func);
2207+
}
2208+
else {
2209+
_PyErr_FormatFromCause(PyExc_SystemError,
2210+
"%s returned a result with an error set",
2211+
where);
2212+
}
22152213
#ifdef Py_DEBUG
22162214
/* Ensure that the bug is caught in debug mode */
22172215
Py_FatalError("a function returned a result with an error set");

Objects/genobject.c

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -118,33 +118,6 @@ gen_dealloc(PyGenObject *gen)
118118
PyObject_GC_Del(gen);
119119
}
120120

121-
static void
122-
gen_chain_runtime_error(const char *msg)
123-
{
124-
PyObject *exc, *val, *val2, *tb;
125-
126-
/* TODO: This about rewriting using _PyErr_ChainExceptions. */
127-
128-
PyErr_Fetch(&exc, &val, &tb);
129-
PyErr_NormalizeException(&exc, &val, &tb);
130-
if (tb != NULL) {
131-
PyException_SetTraceback(val, tb);
132-
}
133-
134-
Py_DECREF(exc);
135-
Py_XDECREF(tb);
136-
137-
PyErr_SetString(PyExc_RuntimeError, msg);
138-
PyErr_Fetch(&exc, &val2, &tb);
139-
PyErr_NormalizeException(&exc, &val2, &tb);
140-
141-
Py_INCREF(val);
142-
PyException_SetCause(val2, val);
143-
PyException_SetContext(val2, val);
144-
145-
PyErr_Restore(exc, val2, tb);
146-
}
147-
148121
static PyObject *
149122
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
150123
{
@@ -276,8 +249,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
276249
else if PyAsyncGen_CheckExact(gen) {
277250
msg = "async generator raised StopIteration";
278251
}
279-
/* Raise a RuntimeError */
280-
gen_chain_runtime_error(msg);
252+
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
281253
}
282254
else {
283255
/* `gen` is an ordinary generator without
@@ -309,7 +281,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
309281
raise a RuntimeError.
310282
*/
311283
const char *msg = "async generator raised StopAsyncIteration";
312-
gen_chain_runtime_error(msg);
284+
_PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
313285
}
314286

315287
if (!result || f->f_stacktop == NULL) {

Objects/unicodeobject.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,13 +3835,10 @@ PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size)
38353835
Py_FileSystemDefaultEncodeErrors);
38363836
#ifdef MS_WINDOWS
38373837
if (!res && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
3838-
PyObject *exc, *val, *tb;
3839-
PyErr_Fetch(&exc, &val, &tb);
3840-
PyErr_Format(PyExc_RuntimeError,
3841-
"filesystem path bytes were not correctly encoded with '%s'. " \
3838+
_PyErr_FormatFromCause(PyExc_RuntimeError,
3839+
"filesystem path bytes were not correctly encoded with '%s'. "
38423840
"Please report this at http://bugs.python.org/issue27781",
38433841
Py_FileSystemDefaultEncoding);
3844-
_PyErr_ChainExceptions(exc, val, tb);
38453842
}
38463843
#endif
38473844
return res;

Python/errors.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,47 @@ _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
401401
}
402402
}
403403

404+
static PyObject *
405+
_PyErr_FormatVFromCause(PyObject *exception, const char *format, va_list vargs)
406+
{
407+
PyObject *exc, *val, *val2, *tb;
408+
409+
assert(PyErr_Occurred());
410+
PyErr_Fetch(&exc, &val, &tb);
411+
PyErr_NormalizeException(&exc, &val, &tb);
412+
if (tb != NULL) {
413+
PyException_SetTraceback(val, tb);
414+
Py_DECREF(tb);
415+
}
416+
Py_DECREF(exc);
417+
assert(!PyErr_Occurred());
418+
419+
PyErr_FormatV(exception, format, vargs);
420+
421+
PyErr_Fetch(&exc, &val2, &tb);
422+
PyErr_NormalizeException(&exc, &val2, &tb);
423+
Py_INCREF(val);
424+
PyException_SetCause(val2, val);
425+
PyException_SetContext(val2, val);
426+
PyErr_Restore(exc, val2, tb);
427+
428+
return NULL;
429+
}
430+
431+
PyObject *
432+
_PyErr_FormatFromCause(PyObject *exception, const char *format, ...)
433+
{
434+
va_list vargs;
435+
#ifdef HAVE_STDARG_PROTOTYPES
436+
va_start(vargs, format);
437+
#else
438+
va_start(vargs);
439+
#endif
440+
_PyErr_FormatVFromCause(exception, format, vargs);
441+
va_end(vargs);
442+
return NULL;
443+
}
444+
404445
/* Convenience functions to set a type error exception and return 0 */
405446

406447
int

0 commit comments

Comments
 (0)