From 2ff58a24e8a1c7e290d025d69ebaea0bbead3b8c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 17 Jun 2019 14:27:23 +0200 Subject: 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. --- Doc/c-api/object.rst | 10 ++++++++++ Doc/whatsnew/3.9.rst | 2 ++ Include/abstract.h | 6 ++++++ Include/cpython/abstract.h | 4 +++- .../next/C API/2019-06-07-14-03-52.bpo-37194.uck7MD.rst | 6 ++++++ Modules/_asynciomodule.c | 12 ++++++------ Modules/_datetimemodule.c | 10 +++++----- Modules/_elementtree.c | 2 +- Modules/_pickle.c | 4 ++-- Modules/_tkinter.c | 2 +- Modules/readline.c | 2 +- Objects/call.c | 8 ++++++++ 12 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2019-06-07-14-03-52.bpo-37194.uck7MD.rst diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index ce0d059..aecd001 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -253,6 +253,16 @@ Object Protocol 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) Call a callable Python object *callable*, with arguments given by the diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index ed55838..62b013f 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -102,6 +102,8 @@ Optimizations 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 diff --git a/Include/abstract.h b/Include/abstract.h index c226aab..f36fafb 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -141,6 +141,12 @@ extern "C" { #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 tuple 'args' and keywords arguments given by the dictionary 'kwargs'. diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index d036912..415f3e1 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -144,7 +144,9 @@ _PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs) 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 * _PyObject_CallNoArg(PyObject *func) { return _PyObject_Vectorcall(func, NULL, 0, NULL); diff --git a/Misc/NEWS.d/next/C API/2019-06-07-14-03-52.bpo-37194.uck7MD.rst b/Misc/NEWS.d/next/C API/2019-06-07-14-03-52.bpo-37194.uck7MD.rst new file mode 100644 index 0000000..c15d372 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-06-07-14-03-52.bpo-37194.uck7MD.rst @@ -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. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 281161b..4caf3a4 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -216,7 +216,7 @@ get_future_loop(PyObject *fut) return NULL; } if (getloop != NULL) { - PyObject *res = _PyObject_CallNoArg(getloop); + PyObject *res = PyObject_CallNoArgs(getloop); Py_DECREF(getloop); return res; } @@ -328,7 +328,7 @@ get_event_loop(void) return loop; } - policy = _PyObject_CallNoArg(asyncio_get_event_loop_policy); + policy = PyObject_CallNoArgs(asyncio_get_event_loop_policy); if (policy == NULL) { return NULL; } @@ -510,7 +510,7 @@ future_init(FutureObj *fut, PyObject *loop) method, which is called during the interpreter shutdown and the 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) { return -1; } @@ -553,7 +553,7 @@ future_set_exception(FutureObj *fut, PyObject *exc) } if (PyExceptionClass_Check(exc)) { - exc_val = _PyObject_CallNoArg(exc); + exc_val = PyObject_CallNoArgs(exc); if (exc_val == NULL) { return NULL; } @@ -2593,7 +2593,7 @@ task_step_impl(TaskObj *task, PyObject *exc) if (!exc) { /* exc was not a CancelledError */ - exc = _PyObject_CallNoArg(asyncio_CancelledError); + exc = PyObject_CallNoArgs(asyncio_CancelledError); if (!exc) { goto fail; } @@ -3308,7 +3308,7 @@ module_init(void) PyObject *weak_set; WITH_MOD("weakref") GET_MOD_ATTR(weak_set, "WeakSet"); - all_tasks = _PyObject_CallNoArg(weak_set); + all_tasks = PyObject_CallNoArgs(weak_set); Py_CLEAR(weak_set); if (all_tasks == NULL) { goto fail; diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 4d3562c..2e0211c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1528,8 +1528,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, ntoappend = 1; } 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 * the next time through the loop.*/ pin--; @@ -1619,7 +1619,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, usednew += ntoappend; assert(usednew <= totalnew); } /* end while() */ - + if (_PyBytes_Resize(&newfmt, usednew) < 0) goto Done; { @@ -3607,7 +3607,7 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) getinitargs = _PyObject_GetAttrId(self, &PyId___getinitargs__); if (getinitargs != NULL) { - args = _PyObject_CallNoArg(getinitargs); + args = PyObject_CallNoArgs(getinitargs); Py_DECREF(getinitargs); if (args == NULL) { return NULL; @@ -3624,7 +3624,7 @@ tzinfo_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) getstate = _PyObject_GetAttrId(self, &PyId___getstate__); if (getstate != NULL) { - state = _PyObject_CallNoArg(getstate); + state = PyObject_CallNoArgs(getstate); Py_DECREF(getstate); if (state == NULL) { Py_DECREF(args); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 8119c8b..ee1b5e5 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -3892,7 +3892,7 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self) } else if (self->handle_close) { Py_DECREF(res); - return _PyObject_CallNoArg(self->handle_close); + return PyObject_CallNoArgs(self->handle_close); } else { return res; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 34e11bd..3adece0 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1274,7 +1274,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) return -1; if (n == READ_WHOLE_LINE) { - data = _PyObject_CallNoArg(self->readline); + data = PyObject_CallNoArgs(self->readline); } else { PyObject *len; @@ -4411,7 +4411,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save) /* Check for a __reduce__ method. */ reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__); if (reduce_func != NULL) { - reduce_value = _PyObject_CallNoArg(reduce_func); + reduce_value = PyObject_CallNoArgs(reduce_func); } else { PyErr_Format(st->PicklingError, diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 613a95b..a832099 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -2768,7 +2768,7 @@ TimerHandler(ClientData clientData) ENTER_PYTHON - res = _PyObject_CallNoArg(func); + res = PyObject_CallNoArgs(func); Py_DECREF(func); Py_DECREF(v); /* See Tktt_New() */ diff --git a/Modules/readline.c b/Modules/readline.c index 57335fe..930e639 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -867,7 +867,7 @@ on_hook(PyObject *func) int result = 0; if (func != NULL) { PyObject *r; - r = _PyObject_CallNoArg(func); + r = PyObject_CallNoArgs(func); if (r == NULL) goto error; if (r == Py_None) diff --git a/Objects/call.c b/Objects/call.c index 5bef9f5..f9a3207 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -70,6 +70,14 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where) /* --- Core PyObject call functions ------------------------------- */ +/* Call a callable Python object without any arguments */ +PyObject * +PyObject_CallNoArgs(PyObject *func) +{ + return _PyObject_CallNoArg(func); +} + + PyObject * _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwargs) -- cgit v0.12