summaryrefslogtreecommitdiffstats
path: root/Objects/abstract.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-03-06 22:35:27 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-03-06 22:35:27 (GMT)
commit4a7cc8847276df27c8f52987cda619ca279687c2 (patch)
tree9bc7cfee8bf0fc27dc7f14ba4853c935d9f21b4c /Objects/abstract.c
parentd81431f587e9eab67db683908548b0ad46847b38 (diff)
downloadcpython-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.c73
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*