Commit 2ff58a24 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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.
parent 8bf08ee4
...@@ -253,6 +253,16 @@ Object Protocol ...@@ -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
......
...@@ -102,6 +102,8 @@ Optimizations ...@@ -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
......
...@@ -141,6 +141,12 @@ extern "C" { ...@@ -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'.
......
...@@ -144,7 +144,9 @@ _PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs) ...@@ -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);
......
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.
...@@ -216,7 +216,7 @@ get_future_loop(PyObject *fut) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -1528,8 +1528,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, ...@@ -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, ...@@ -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)) ...@@ -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)) ...@@ -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);
......
...@@ -3892,7 +3892,7 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self) ...@@ -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;
......
...@@ -1274,7 +1274,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) ...@@ -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) ...@@ -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,
......
...@@ -2768,7 +2768,7 @@ TimerHandler(ClientData clientData) ...@@ -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() */
......
...@@ -867,7 +867,7 @@ on_hook(PyObject *func) ...@@ -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)
......
...@@ -70,6 +70,14 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where) ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment