diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-03-06 22:35:27 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-03-06 22:35:27 (GMT) |
commit | 4a7cc8847276df27c8f52987cda619ca279687c2 (patch) | |
tree | 9bc7cfee8bf0fc27dc7f14ba4853c935d9f21b4c /Objects/abstract.c | |
parent | d81431f587e9eab67db683908548b0ad46847b38 (diff) | |
download | cpython-4a7cc8847276df27c8f52987cda619ca279687c2.zip cpython-4a7cc8847276df27c8f52987cda619ca279687c2.tar.gz cpython-4a7cc8847276df27c8f52987cda619ca279687c2.tar.bz2 |
Issue #23571: PyObject_Call(), PyCFunction_Call() and call_function() now
raise a SystemError if a function returns a result and raises an exception.
The SystemError is chained to the previous exception.
Refactor also PyObject_Call() and PyCFunction_Call() to make them more readable.
Remove some checks which became useless (duplicate checks).
Change reviewed by Serhiy Storchaka.
Diffstat (limited to 'Objects/abstract.c')
-rw-r--r-- | Objects/abstract.c | 73 |
1 files changed, 53 insertions, 20 deletions
diff --git a/Objects/abstract.c b/Objects/abstract.c index 06e3382..ab13476 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2073,37 +2073,70 @@ PyObject_CallObject(PyObject *o, PyObject *a) return PyEval_CallObjectWithKeywords(o, a, NULL); } +PyObject* +_Py_CheckFunctionResult(PyObject *result, const char *func_name) +{ + int err_occurred = (PyErr_Occurred() != NULL); + +#ifdef NDEBUG + /* In debug mode: abort() with an assertion error. Use two different + assertions, so if an assertion fails, it's possible to know + if result was set or not and if an exception was raised or not. */ + if (result != NULL) + assert(!err_occurred); + else + assert(err_occurred); +#endif + + if (result == NULL) { + if (!err_occurred) { + PyErr_Format(PyExc_SystemError, + "NULL result without error in %s", func_name); + return NULL; + } + } + else { + if (err_occurred) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + + Py_DECREF(result); + + PyErr_Format(PyExc_SystemError, + "result with error in %s", func_name); + _PyErr_ChainExceptions(exc, val, tb); + return NULL; + } + } + return result; +} + PyObject * PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { ternaryfunc call; + PyObject *result; /* PyObject_Call() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller looses its exception */ assert(!PyErr_Occurred()); - if ((call = func->ob_type->tp_call) != NULL) { - PyObject *result; - if (Py_EnterRecursiveCall(" while calling a Python object")) - return NULL; - result = (*call)(func, arg, kw); - Py_LeaveRecursiveCall(); -#ifdef NDEBUG - if (result == NULL && !PyErr_Occurred()) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } -#else - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); -#endif - return result; + call = func->ob_type->tp_call; + if (call == NULL) { + PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", + func->ob_type->tp_name); + return NULL; } - PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - func->ob_type->tp_name); - return NULL; + + if (Py_EnterRecursiveCall(" while calling a Python object")) + return NULL; + + result = (*call)(func, arg, kw); + + Py_LeaveRecursiveCall(); + + return _Py_CheckFunctionResult(result, "PyObject_Call"); } static PyObject* |