bpo-37194: Add a new public PyObject_CallNoArgs() function (GH-13890)

Add a new public PyObject_CallNoArgs() function to the C API: call a
callable Python object without any arguments.

It is the most efficient way to call a callback without any argument.
On x86-64, for example, PyObject_CallFunctionObjArgs(func, NULL)
allocates 960 bytes on the stack per call, whereas
PyObject_CallNoArgs(func) only allocates 624 bytes per call.

It is excluded from stable ABI 3.8.

Replace private _PyObject_CallNoArg() with public
PyObject_CallNoArgs() in C extensions: _asyncio, _datetime,
_elementtree, _pickle, _tkinter and readline.
This commit is contained in:
Victor Stinner 2019-06-17 14:27:23 +02:00 committed by GitHub
parent 8bf08ee45b
commit 2ff58a24e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 51 additions and 17 deletions

View File

@ -253,6 +253,16 @@ Object Protocol
and ``0`` otherwise. This function always succeeds. and ``0`` otherwise. This function always succeeds.
.. c:function:: PyObject* PyObject_CallNoArgs(PyObject *callable)
Call a callable Python object *callable* without any arguments.
Returns the result of the call on success, or raise an exception and return
*NULL* on failure.
.. versionadded:: 3.9
.. c:function:: PyObject* PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) .. c:function:: PyObject* PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
Call a callable Python object *callable*, with arguments given by the Call a callable Python object *callable*, with arguments given by the

View File

@ -102,6 +102,8 @@ Optimizations
Build and C API Changes Build and C API Changes
======================= =======================
* Add a new public :c:func:`PyObject_CallNoArgs` function to the C API:
call a callable Python object without any arguments.
Deprecated Deprecated

View File

@ -141,6 +141,12 @@ extern "C" {
#endif #endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000
/* Call a callable Python object without any arguments */
PyAPI_FUNC(PyObject *) PyObject_CallNoArgs(PyObject *func);
#endif
/* Call a callable Python object 'callable' with arguments given by the /* Call a callable Python object 'callable' with arguments given by the
tuple 'args' and keywords arguments given by the dictionary 'kwargs'. tuple 'args' and keywords arguments given by the dictionary 'kwargs'.

View File

@ -144,7 +144,9 @@ _PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
return _PyObject_Vectorcall(func, args, (size_t)nargs, NULL); return _PyObject_Vectorcall(func, args, (size_t)nargs, NULL);
} }
/* Call a callable without any arguments */ /* Call a callable without any arguments
Private static inline function variant of public function
PyObject_CallNoArgs(). */
static inline PyObject * static inline PyObject *
_PyObject_CallNoArg(PyObject *func) { _PyObject_CallNoArg(PyObject *func) {
return _PyObject_Vectorcall(func, NULL, 0, NULL); return _PyObject_Vectorcall(func, NULL, 0, NULL);

View File

@ -0,0 +1,6 @@
Add a new public :c:func:`PyObject_CallNoArgs` function to the C API: call a
callable Python object without any arguments. It is the most efficient way to
call a callback without any argument. On x86-64, for example,
``PyObject_CallFunctionObjArgs(func, NULL)`` allocates 960 bytes on the stack
per call, whereas ``PyObject_CallNoArgs(func)`` only allocates 624 bytes per
call.

View File

@ -216,7 +216,7 @@ get_future_loop(PyObject *fut)
return NULL; return NULL;
} }
if (getloop != NULL) { if (getloop != NULL) {
PyObject *res = _PyObject_CallNoArg(getloop); PyObject *res = PyObject_CallNoArgs(getloop);
Py_DECREF(getloop); Py_DECREF(getloop);
return res; return res;
} }
@ -328,7 +328,7 @@ get_event_loop(void)
return loop; return loop;
} }
policy = _PyObject_CallNoArg(asyncio_get_event_loop_policy); policy = PyObject_CallNoArgs(asyncio_get_event_loop_policy);
if (policy == NULL) { if (policy == NULL) {
return NULL; return NULL;
} }
@ -510,7 +510,7 @@ future_init(FutureObj *fut, PyObject *loop)
method, which is called during the interpreter shutdown and the method, which is called during the interpreter shutdown and the
traceback module is already unloaded. traceback module is already unloaded.
*/ */
fut->fut_source_tb = _PyObject_CallNoArg(traceback_extract_stack); fut->fut_source_tb = PyObject_CallNoArgs(traceback_extract_stack);
if (fut->fut_source_tb == NULL) { if (fut->fut_source_tb == NULL) {
return -1; return -1;
} }
@ -553,7 +553,7 @@ future_set_exception(FutureObj *fut, PyObject *exc)
} }
if (PyExceptionClass_Check(exc)) { if (PyExceptionClass_Check(exc)) {
exc_val = _PyObject_CallNoArg(exc); exc_val = PyObject_CallNoArgs(exc);
if (exc_val == NULL) { if (exc_val == NULL) {
return NULL; return NULL;
} }
@ -2593,7 +2593,7 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (!exc) { if (!exc) {
/* exc was not a CancelledError */ /* exc was not a CancelledError */
exc = _PyObject_CallNoArg(asyncio_CancelledError); exc = PyObject_CallNoArgs(asyncio_CancelledError);
if (!exc) { if (!exc) {
goto fail; goto fail;
} }
@ -3308,7 +3308,7 @@ module_init(void)
PyObject *weak_set; PyObject *weak_set;
WITH_MOD("weakref") WITH_MOD("weakref")
GET_MOD_ATTR(weak_set, "WeakSet"); GET_MOD_ATTR(weak_set, "WeakSet");
all_tasks = _PyObject_CallNoArg(weak_set); all_tasks = PyObject_CallNoArgs(weak_set);
Py_CLEAR(weak_set); Py_CLEAR(weak_set);
if (all_tasks == NULL) { if (all_tasks == NULL) {
goto fail; goto fail;

View File

@ -1528,8 +1528,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
ntoappend = 1; ntoappend = 1;
} }
else if ((ch = *pin++) == '\0') { else if ((ch = *pin++) == '\0') {
/* Null byte follows %, copy only '%'. /* Null byte follows %, copy only '%'.
* *
* Back the pin up one char so that we catch the null check * Back the pin up one char so that we catch the null check
* the next time through the loop.*/ * the next time through the loop.*/
pin--; pin--;
@ -1619,7 +1619,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
usednew += ntoappend; usednew += ntoappend;
assert(usednew <= totalnew); assert(usednew <= totalnew);
} /* end while() */ } /* end while() */
if (_PyBytes_Resize(&newfmt, usednew) < 0) if (_PyBytes_Resize(&newfmt, usednew) < 0)
goto Done; goto Done;
{ {
@ -3607,7 +3607,7 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__); getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__);
if (getinitargs != NULL) { if (getinitargs != NULL) {
args = _PyObject_CallNoArg(getinitargs); args = PyObject_CallNoArgs(getinitargs);
Py_DECREF(getinitargs); Py_DECREF(getinitargs);
if (args == NULL) { if (args == NULL) {
return NULL; return NULL;
@ -3624,7 +3624,7 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
getstate = _PyObject_GetAttrId(self, &PyId___getstate__); getstate = _PyObject_GetAttrId(self, &PyId___getstate__);
if (getstate != NULL) { if (getstate != NULL) {
state = _PyObject_CallNoArg(getstate); state = PyObject_CallNoArgs(getstate);
Py_DECREF(getstate); Py_DECREF(getstate);
if (state == NULL) { if (state == NULL) {
Py_DECREF(args); Py_DECREF(args);

View File

@ -3892,7 +3892,7 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self)
} }
else if (self->handle_close) { else if (self->handle_close) {
Py_DECREF(res); Py_DECREF(res);
return _PyObject_CallNoArg(self->handle_close); return PyObject_CallNoArgs(self->handle_close);
} }
else { else {
return res; return res;

View File

@ -1274,7 +1274,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n)
return -1; return -1;
if (n == READ_WHOLE_LINE) { if (n == READ_WHOLE_LINE) {
data = _PyObject_CallNoArg(self->readline); data = PyObject_CallNoArgs(self->readline);
} }
else { else {
PyObject *len; PyObject *len;
@ -4411,7 +4411,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
/* Check for a __reduce__ method. */ /* Check for a __reduce__ method. */
reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__); reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__);
if (reduce_func != NULL) { if (reduce_func != NULL) {
reduce_value = _PyObject_CallNoArg(reduce_func); reduce_value = PyObject_CallNoArgs(reduce_func);
} }
else { else {
PyErr_Format(st->PicklingError, PyErr_Format(st->PicklingError,

View File

@ -2768,7 +2768,7 @@ TimerHandler(ClientData clientData)
ENTER_PYTHON ENTER_PYTHON
res = _PyObject_CallNoArg(func); res = PyObject_CallNoArgs(func);
Py_DECREF(func); Py_DECREF(func);
Py_DECREF(v); /* See Tktt_New() */ Py_DECREF(v); /* See Tktt_New() */

View File

@ -867,7 +867,7 @@ on_hook(PyObject *func)
int result = 0; int result = 0;
if (func != NULL) { if (func != NULL) {
PyObject *r; PyObject *r;
r = _PyObject_CallNoArg(func); r = PyObject_CallNoArgs(func);
if (r == NULL) if (r == NULL)
goto error; goto error;
if (r == Py_None) if (r == Py_None)

View File

@ -70,6 +70,14 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where)
/* --- Core PyObject call functions ------------------------------- */ /* --- Core PyObject call functions ------------------------------- */
/* Call a callable Python object without any arguments */
PyObject *
PyObject_CallNoArgs(PyObject *func)
{
return _PyObject_CallNoArg(func);
}
PyObject * PyObject *
_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwargs) size_t nargsf, PyObject *kwargs)