From 51ef54abc42e020d7e80549d49ca32310495b4eb Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 1 Nov 2024 23:15:39 +0300 Subject: [PATCH] gh-125916: Adapt functools.reduce() to Argument Clinic (#125999) --- Lib/test/test_inspect/test_inspect.py | 4 +-- Modules/_functoolsmodule.c | 46 +++++++++++++++------------ Modules/clinic/_functoolsmodule.c.h | 46 ++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 2250b7e76da..a4430a86867 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -5708,8 +5708,8 @@ class TestSignatureDefinitions(unittest.TestCase): self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature) def test_functools_module_has_signatures(self): - no_signature = {'reduce'} - self._test_module_has_signatures(functools, no_signature) + unsupported_signature = {"reduce"} + self._test_module_has_signatures(functools, unsupported_signature=unsupported_signature) def test_gc_module_has_signatures(self): import gc diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index da4e088e546..d2afe1a1bea 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -932,15 +932,31 @@ _functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp) /* reduce (used to be a builtin) ********************************************/ -// Not converted to argument clinic, because of `args` in-place modification. -// AC will affect performance. -static PyObject * -functools_reduce(PyObject *self, PyObject *args) -{ - PyObject *seq, *func, *result = NULL, *it; +/*[clinic input] +_functools.reduce + + function as func: object + iterable as seq: object + initial as result: object = NULL + / + +Apply a function of two arguments cumulatively to the items of an iterable, from left to right. + +This effectively reduces the iterable to a single value. If initial is present, +it is placed before the items of the iterable in the calculation, and serves as +a default when the iterable is empty. + +For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) +calculates ((((1 + 2) + 3) + 4) + 5). +[clinic start generated code]*/ + +static PyObject * +_functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, + PyObject *result) +/*[clinic end generated code: output=30d898fe1267c79d input=d233c2670cba7f66]*/ +{ + PyObject *args, *it; - if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result)) - return NULL; if (result != NULL) Py_INCREF(result); @@ -1006,18 +1022,6 @@ Fail: return NULL; } -PyDoc_STRVAR(functools_reduce_doc, -"reduce(function, iterable[, initial], /) -> value\n\ -\n\ -Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n\ -\n\ -This effectively reduces the iterable to a single value. If initial is present,\n\ -it is placed before the items of the iterable in the calculation, and serves as\n\ -a default when the iterable is empty.\n\ -\n\ -For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n\ -calculates ((((1 + 2) + 3) + 4) + 5)."); - /* lru_cache object **********************************************************/ /* There are four principal algorithmic differences from the pure python version: @@ -1722,7 +1726,7 @@ PyDoc_STRVAR(_functools_doc, "Tools that operate on functions."); static PyMethodDef _functools_methods[] = { - {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc}, + _FUNCTOOLS_REDUCE_METHODDEF _FUNCTOOLS_CMP_TO_KEY_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index e98984dc4d3..0564921034b 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -67,6 +67,50 @@ exit: return return_value; } +PyDoc_STRVAR(_functools_reduce__doc__, +"reduce($module, function, iterable, initial=, /)\n" +"--\n" +"\n" +"Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n" +"\n" +"This effectively reduces the iterable to a single value. If initial is present,\n" +"it is placed before the items of the iterable in the calculation, and serves as\n" +"a default when the iterable is empty.\n" +"\n" +"For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n" +"calculates ((((1 + 2) + 3) + 4) + 5)."); + +#define _FUNCTOOLS_REDUCE_METHODDEF \ + {"reduce", _PyCFunction_CAST(_functools_reduce), METH_FASTCALL, _functools_reduce__doc__}, + +static PyObject * +_functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, + PyObject *result); + +static PyObject * +_functools_reduce(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *func; + PyObject *seq; + PyObject *result = NULL; + + if (!_PyArg_CheckPositional("reduce", nargs, 2, 3)) { + goto exit; + } + func = args[0]; + seq = args[1]; + if (nargs < 3) { + goto skip_optional; + } + result = args[2]; +skip_optional: + return_value = _functools_reduce_impl(module, func, seq, result); + +exit: + return return_value; +} + PyDoc_STRVAR(_functools__lru_cache_wrapper_cache_info__doc__, "cache_info($self, /)\n" "--\n" @@ -114,4 +158,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=755265bb6d5ea751 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=214d6c6307cfcd91 input=a9049054013a1b77]*/