Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4a7cc88

Browse files
committed
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.
1 parent d81431f commit 4a7cc88

7 files changed

Lines changed: 125 additions & 96 deletions

File tree

Include/abstract.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
266266
PyAPI_FUNC(PyObject *) PyObject_Call(PyObject *callable_object,
267267
PyObject *args, PyObject *kw);
268268

269+
#ifndef Py_LIMITED_API
270+
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *obj,
271+
const char *func_name);
272+
#endif
273+
269274
/*
270275
Call a callable Python object, callable_object, with
271276
arguments and keywords arguments. The 'args' argument can not be

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Release date: 2015-03-08
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError
14+
if a function returns a result and raises an exception. The SystemError is
15+
chained to the previous exception.
16+
1317
Library
1418
-------
1519

Modules/_io/bufferedio.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,7 @@ static void
680680
_set_BlockingIOError(char *msg, Py_ssize_t written)
681681
{
682682
PyObject *err;
683-
#ifdef Py_DEBUG
684-
/* in debug mode, PyEval_EvalFrameEx() fails with an assertion error
685-
if an exception is set when it is called */
686683
PyErr_Clear();
687-
#endif
688684
err = PyObject_CallFunction(PyExc_BlockingIOError, "isn",
689685
errno, msg, written);
690686
if (err)

Modules/_sqlite/cursor.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,7 @@ PyObject* _pysqlite_fetch_one_row(pysqlite_Cursor* self)
334334
if (self->connection->text_factory == (PyObject*)&PyUnicode_Type) {
335335
converted = PyUnicode_FromStringAndSize(val_str, nbytes);
336336
if (!converted) {
337-
#ifdef Py_DEBUG
338-
/* in debug mode, type_call() fails with an assertion
339-
error if an exception is set when it is called */
340337
PyErr_Clear();
341-
#endif
342338
colname = sqlite3_column_name(self->statement->st, i);
343339
if (!colname) {
344340
colname = "<unknown column name>";

Objects/abstract.c

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,37 +2073,70 @@ PyObject_CallObject(PyObject *o, PyObject *a)
20732073
return PyEval_CallObjectWithKeywords(o, a, NULL);
20742074
}
20752075

2076+
PyObject*
2077+
_Py_CheckFunctionResult(PyObject *result, const char *func_name)
2078+
{
2079+
int err_occurred = (PyErr_Occurred() != NULL);
2080+
2081+
#ifdef NDEBUG
2082+
/* In debug mode: abort() with an assertion error. Use two different
2083+
assertions, so if an assertion fails, it's possible to know
2084+
if result was set or not and if an exception was raised or not. */
2085+
if (result != NULL)
2086+
assert(!err_occurred);
2087+
else
2088+
assert(err_occurred);
2089+
#endif
2090+
2091+
if (result == NULL) {
2092+
if (!err_occurred) {
2093+
PyErr_Format(PyExc_SystemError,
2094+
"NULL result without error in %s", func_name);
2095+
return NULL;
2096+
}
2097+
}
2098+
else {
2099+
if (err_occurred) {
2100+
PyObject *exc, *val, *tb;
2101+
PyErr_Fetch(&exc, &val, &tb);
2102+
2103+
Py_DECREF(result);
2104+
2105+
PyErr_Format(PyExc_SystemError,
2106+
"result with error in %s", func_name);
2107+
_PyErr_ChainExceptions(exc, val, tb);
2108+
return NULL;
2109+
}
2110+
}
2111+
return result;
2112+
}
2113+
20762114
PyObject *
20772115
PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
20782116
{
20792117
ternaryfunc call;
2118+
PyObject *result;
20802119

20812120
/* PyObject_Call() must not be called with an exception set,
20822121
because it may clear it (directly or indirectly) and so the
20832122
caller looses its exception */
20842123
assert(!PyErr_Occurred());
20852124

2086-
if ((call = func->ob_type->tp_call) != NULL) {
2087-
PyObject *result;
2088-
if (Py_EnterRecursiveCall(" while calling a Python object"))
2089-
return NULL;
2090-
result = (*call)(func, arg, kw);
2091-
Py_LeaveRecursiveCall();
2092-
#ifdef NDEBUG
2093-
if (result == NULL && !PyErr_Occurred()) {
2094-
PyErr_SetString(
2095-
PyExc_SystemError,
2096-
"NULL result without error in PyObject_Call");
2097-
}
2098-
#else
2099-
assert((result != NULL && !PyErr_Occurred())
2100-
|| (result == NULL && PyErr_Occurred()));
2101-
#endif
2102-
return result;
2125+
call = func->ob_type->tp_call;
2126+
if (call == NULL) {
2127+
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
2128+
func->ob_type->tp_name);
2129+
return NULL;
21032130
}
2104-
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
2105-
func->ob_type->tp_name);
2106-
return NULL;
2131+
2132+
if (Py_EnterRecursiveCall(" while calling a Python object"))
2133+
return NULL;
2134+
2135+
result = (*call)(func, arg, kw);
2136+
2137+
Py_LeaveRecursiveCall();
2138+
2139+
return _Py_CheckFunctionResult(result, "PyObject_Call");
21072140
}
21082141

21092142
static PyObject*

Objects/methodobject.c

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -78,68 +78,71 @@ PyCFunction_GetFlags(PyObject *op)
7878
}
7979

8080
PyObject *
81-
PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw)
81+
PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds)
8282
{
83-
#define CHECK_RESULT(res) assert(res != NULL || PyErr_Occurred())
84-
8583
PyCFunctionObject* f = (PyCFunctionObject*)func;
8684
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
8785
PyObject *self = PyCFunction_GET_SELF(func);
88-
PyObject *res;
86+
PyObject *arg, *res;
8987
Py_ssize_t size;
88+
int flags;
9089

91-
switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) {
92-
case METH_VARARGS:
93-
if (kw == NULL || PyDict_Size(kw) == 0) {
94-
res = (*meth)(self, arg);
95-
CHECK_RESULT(res);
96-
return res;
97-
}
98-
break;
99-
case METH_VARARGS | METH_KEYWORDS:
100-
res = (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
101-
CHECK_RESULT(res);
102-
return res;
103-
case METH_NOARGS:
104-
if (kw == NULL || PyDict_Size(kw) == 0) {
105-
size = PyTuple_GET_SIZE(arg);
106-
if (size == 0) {
107-
res = (*meth)(self, NULL);
108-
CHECK_RESULT(res);
109-
return res;
110-
}
111-
PyErr_Format(PyExc_TypeError,
112-
"%.200s() takes no arguments (%zd given)",
113-
f->m_ml->ml_name, size);
90+
/* PyCFunction_Call() must not be called with an exception set,
91+
because it may clear it (directly or indirectly) and so the
92+
caller looses its exception */
93+
assert(!PyErr_Occurred());
94+
95+
flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
96+
97+
if (flags == (METH_VARARGS | METH_KEYWORDS)) {
98+
res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds);
99+
}
100+
else {
101+
if (kwds != NULL && PyDict_Size(kwds) != 0) {
102+
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
103+
f->m_ml->ml_name);
114104
return NULL;
115105
}
116-
break;
117-
case METH_O:
118-
if (kw == NULL || PyDict_Size(kw) == 0) {
119-
size = PyTuple_GET_SIZE(arg);
120-
if (size == 1) {
121-
res = (*meth)(self, PyTuple_GET_ITEM(arg, 0));
122-
CHECK_RESULT(res);
123-
return res;
106+
107+
switch (flags) {
108+
case METH_VARARGS:
109+
res = (*meth)(self, args);
110+
break;
111+
112+
case METH_NOARGS:
113+
size = PyTuple_GET_SIZE(args);
114+
if (size != 0) {
115+
PyErr_Format(PyExc_TypeError,
116+
"%.200s() takes no arguments (%zd given)",
117+
f->m_ml->ml_name, size);
118+
return NULL;
124119
}
125-
PyErr_Format(PyExc_TypeError,
126-
"%.200s() takes exactly one argument (%zd given)",
127-
f->m_ml->ml_name, size);
120+
121+
res = (*meth)(self, NULL);
122+
break;
123+
124+
case METH_O:
125+
size = PyTuple_GET_SIZE(args);
126+
if (size != 1) {
127+
PyErr_Format(PyExc_TypeError,
128+
"%.200s() takes exactly one argument (%zd given)",
129+
f->m_ml->ml_name, size);
130+
return NULL;
131+
}
132+
133+
arg = PyTuple_GET_ITEM(args, 0);
134+
res = (*meth)(self, arg);
135+
break;
136+
137+
default:
138+
PyErr_SetString(PyExc_SystemError,
139+
"Bad call flags in PyCFunction_Call. "
140+
"METH_OLDARGS is no longer supported!");
128141
return NULL;
129142
}
130-
break;
131-
default:
132-
PyErr_SetString(PyExc_SystemError, "Bad call flags in "
133-
"PyCFunction_Call. METH_OLDARGS is no "
134-
"longer supported!");
135-
136-
return NULL;
137143
}
138-
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
139-
f->m_ml->ml_name);
140-
return NULL;
141144

142-
#undef CHECK_RESULT
145+
return _Py_CheckFunctionResult(res, "PyCFunction_Call");
143146
}
144147

145148
/* Methods (the standard built-in methods, that is) */

Python/ceval.c

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,8 +3192,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
31923192
if (why != WHY_RETURN)
31933193
retval = NULL;
31943194

3195-
assert((retval != NULL && !PyErr_Occurred())
3196-
|| (retval == NULL && PyErr_Occurred()));
3195+
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
31973196

31983197
fast_yield:
31993198
if (co->co_flags & CO_GENERATOR) {
@@ -3254,7 +3253,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
32543253
f->f_executing = 0;
32553254
tstate->frame = f->f_back;
32563255

3257-
return retval;
3256+
return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx");
32583257
}
32593258

32603259
static void
@@ -4119,13 +4118,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
41194118
{
41204119
PyObject *result;
41214120

4122-
#ifdef Py_DEBUG
4123-
/* PyEval_CallObjectWithKeywords() must not be called with an exception
4124-
set, because it may clear it (directly or indirectly)
4125-
and so the caller looses its exception */
4126-
assert(!PyErr_Occurred());
4127-
#endif
4128-
41294121
if (arg == NULL) {
41304122
arg = PyTuple_New(0);
41314123
if (arg == NULL)
@@ -4149,8 +4141,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
41494141
result = PyObject_Call(func, arg, kw);
41504142
Py_DECREF(arg);
41514143

4152-
assert((result != NULL && !PyErr_Occurred())
4153-
|| (result == NULL && PyErr_Occurred()));
41544144
return result;
41554145
}
41564146

@@ -4253,11 +4243,15 @@ call_function(PyObject ***pp_stack, int oparg
42534243
PyObject *self = PyCFunction_GET_SELF(func);
42544244
if (flags & METH_NOARGS && na == 0) {
42554245
C_TRACE(x, (*meth)(self,NULL));
4246+
4247+
x = _Py_CheckFunctionResult(x, "call_function");
42564248
}
42574249
else if (flags & METH_O && na == 1) {
42584250
PyObject *arg = EXT_POP(*pp_stack);
42594251
C_TRACE(x, (*meth)(self,arg));
42604252
Py_DECREF(arg);
4253+
4254+
x = _Py_CheckFunctionResult(x, "call_function");
42614255
}
42624256
else {
42634257
err_args(func, flags, na);
@@ -4277,7 +4271,8 @@ call_function(PyObject ***pp_stack, int oparg
42774271
x = NULL;
42784272
}
42794273
}
4280-
} else {
4274+
}
4275+
else {
42814276
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
42824277
/* optimize access to bound methods */
42834278
PyObject *self = PyMethod_GET_SELF(func);
@@ -4299,9 +4294,9 @@ call_function(PyObject ***pp_stack, int oparg
42994294
x = do_call(func, pp_stack, na, nk);
43004295
READ_TIMESTAMP(*pintr1);
43014296
Py_DECREF(func);
4297+
4298+
assert((x != NULL) ^ (PyErr_Occurred() != NULL));
43024299
}
4303-
assert((x != NULL && !PyErr_Occurred())
4304-
|| (x == NULL && PyErr_Occurred()));
43054300

43064301
/* Clear the stack of the function object. Also removes
43074302
the arguments in case they weren't consumed already
@@ -4313,8 +4308,7 @@ call_function(PyObject ***pp_stack, int oparg
43134308
PCALL(PCALL_POP);
43144309
}
43154310

4316-
assert((x != NULL && !PyErr_Occurred())
4317-
|| (x == NULL && PyErr_Occurred()));
4311+
assert((x != NULL) ^ (PyErr_Occurred() != NULL));
43184312
return x;
43194313
}
43204314

@@ -4601,8 +4595,6 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
46014595
Py_XDECREF(callargs);
46024596
Py_XDECREF(kwdict);
46034597
Py_XDECREF(stararg);
4604-
assert((result != NULL && !PyErr_Occurred())
4605-
|| (result == NULL && PyErr_Occurred()));
46064598
return result;
46074599
}
46084600

0 commit comments

Comments
 (0)