From ad5e812ca49f3430e96cadaa8d9d40508fdb67d5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 26 Oct 2024 13:15:38 +0300 Subject: [PATCH 1/8] gh-90370: avoid temporary tuple creation for vararg in AC Current patch partially address issue, working in case when all arguments either positional-only or vararg. This also converts gcd(), lcm() and hypot() functions of the math module to the Argument Clinic. Fix issue python/cpython#101123. Objects/setobject.c and Modules/gcmodule.c adapted. This fixes slight performance regression for set methods, introduced by python/cpython#115112: | Benchmark | ref | patch | |----------------------|:------:|:--------------------:| | set().update(s1, s2) | 354 ns | 264 ns: 1.34x faster | Benchmark code: ```py import pyperf s1, s2 = {1}, {2} runner = pyperf.Runner() runner.bench_func('set().update(s1, s2)', set().update, s1, s2) ``` --- Lib/test/clinic.test.c | 48 ++++----- Lib/test/test_clinic.py | 4 +- ...4-10-27-20-31-43.gh-issue-90370.IP_W3a.rst | 2 + Modules/_testclinic.c | 43 ++++++-- Modules/clinic/_testclinic.c.h | 51 +++------ Modules/clinic/gcmodule.c.h | 36 +++---- Modules/clinic/mathmodule.c.h | 100 ++++++++++++++++- Modules/gcmodule.c | 46 ++++++-- Modules/mathmodule.c | 80 +++++++------- Objects/clinic/setobject.c.h | 102 ++++++------------ Objects/setobject.c | 56 +++++----- Tools/clinic/libclinic/clanguage.py | 14 ++- Tools/clinic/libclinic/parse_args.py | 31 +++--- 13 files changed, 360 insertions(+), 253 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 2a071f8485a2b8..66f30509688f2e 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4148,36 +4148,32 @@ PyDoc_STRVAR(test_vararg_and_posonly__doc__, {"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__}, static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, + PyObject *const *args); static PyObject * test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t nvararg = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; - __clinic_args = PyTuple_New(nargs - 1); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 1; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i])); - } - return_value = test_vararg_and_posonly_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = test_vararg_and_posonly_impl(module, a, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=79b75dc07decc8d6 input=9cfa748bbff09877]*/ +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=f0f68154d891dd6d input=9cfa748bbff09877]*/ /*[clinic input] test_vararg @@ -4931,14 +4927,14 @@ PyDoc_STRVAR(Test___init____doc__, "Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); static int -Test___init___impl(TestObj *self, PyObject *args); +Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args); static int Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; PyTypeObject *base_tp = TestType; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if ((Py_IS_TYPE(self, base_tp) || Py_TYPE(self)->tp_new == base_tp->tp_new) && @@ -4948,17 +4944,16 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_GetSlice(0, -1); - return_value = Test___init___impl((TestObj *)self, __clinic_args); + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = Test___init___impl((TestObj *)self, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static int -Test___init___impl(TestObj *self, PyObject *args) -/*[clinic end generated code: output=0ed1009fe0dcf98d input=2a8bd0033c9ac772]*/ +Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=6a64b417c9080a73 input=2a8bd0033c9ac772]*/ /*[clinic input] @@ -4976,14 +4971,14 @@ PyDoc_STRVAR(Test__doc__, "Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); static PyObject * -Test_impl(PyTypeObject *type, PyObject *args); +Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args); static PyObject * Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; PyTypeObject *base_tp = TestType; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("Test", kwargs)) { @@ -4992,17 +4987,16 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_GetSlice(0, -1); - return_value = Test_impl(type, __clinic_args); + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = Test_impl(type, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -Test_impl(PyTypeObject *type, PyObject *args) -/*[clinic end generated code: output=8b219f6633e2a2e9 input=70ad829df3dd9b84]*/ +Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=bf22f942407383a5 input=70ad829df3dd9b84]*/ /*[clinic input] diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 402106194f169f..d492ea1d76aa3a 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3381,8 +3381,8 @@ def test_keyword_only_parameter(self): def test_varpos(self): # fn(*args) fn = ac_tester.varpos - self.assertEqual(fn(), ()) - self.assertEqual(fn(1, 2), (1, 2)) + self.assertEqual(fn(), ((),)) + self.assertEqual(fn(1, 2), ((1, 2),)) def test_posonly_varpos(self): # fn(a, b, /, *args) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst new file mode 100644 index 00000000000000..b6a19c06a228ca --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst @@ -0,0 +1,2 @@ +Avoid temporary tuple creation for vararg in argument passing with Argument +Clinic generated code (if arguments either vararg or positional-only). diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index ca884af1aa29b8..e3c8ba9b0b5074 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -58,6 +58,20 @@ pack_arguments_newref(int argc, ...) return tuple; } +static PyObject * +pack_varargs_to_tuple(Py_ssize_t varargssize, PyObject *const *args) +{ + assert(!PyErr_Occurred()); + PyObject *tuple = PyTuple_New(varargssize); + if (!tuple) { + return NULL; + } + for (Py_ssize_t i = 0; i < varargssize; i++) { + PyTuple_SET_ITEM(tuple, i, Py_NewRef(args[i])); + } + return tuple; +} + /* Pack arguments to a tuple. * `wrapper` is function which converts primitive type to PyObject. * `arg_type` is type that arguments should be converted to before wrapped. */ @@ -970,10 +984,16 @@ varpos [clinic start generated code]*/ static PyObject * -varpos_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=7b0b9545872bdca4 input=f87cd674145d394c]*/ +varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=b65096f423fb5dcc input=f87cd674145d394c]*/ { - return Py_NewRef(args); + PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -989,10 +1009,16 @@ posonly_varpos static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args) -/*[clinic end generated code: output=5dae5eb2a0d623cd input=c9fd7895cfbaabba]*/ + Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=d10d43d86d117ab3 input=c9fd7895cfbaabba]*/ { - return pack_arguments_newref(3, a, b, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -1157,8 +1183,9 @@ Proof-of-concept of GH-99233 refcount error bug. [clinic start generated code]*/ static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=585855abfbca9a7f input=eecfdc2092d90dc3]*/ +gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=b570007e61e5c670 input=eecfdc2092d90dc3]*/ { Py_RETURN_NONE; } diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 1988c06971087d..b94d8abcb29310 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2530,28 +2530,22 @@ PyDoc_STRVAR(varpos__doc__, {"varpos", _PyCFunction_CAST(varpos), METH_FASTCALL, varpos__doc__}, static PyObject * -varpos_impl(PyObject *module, PyObject *args); +varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); static PyObject * varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("varpos", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = varpos_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = varpos_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2565,32 +2559,26 @@ PyDoc_STRVAR(posonly_varpos__doc__, static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args); + Py_ssize_t nargs, PyObject *const *args); static PyObject * posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t nvararg = Py_MAX(nargs - 2, 0); PyObject *a; PyObject *b; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("posonly_varpos", nargs, 2, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; b = args[1]; - __clinic_args = PyTuple_New(nargs - 2); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 2; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[2 + i])); - } - return_value = posonly_varpos_impl(module, a, b, __clinic_args); + __clinic_args = args + 2; + return_value = posonly_varpos_impl(module, a, b, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -3136,28 +3124,23 @@ PyDoc_STRVAR(gh_99233_refcount__doc__, {"gh_99233_refcount", _PyCFunction_CAST(gh_99233_refcount), METH_FASTCALL, gh_99233_refcount__doc__}, static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args); +gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gh_99233_refcount_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gh_99233_refcount_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -3693,4 +3676,4 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, Py_XDECREF(__clinic_args); return return_value; } -/*[clinic end generated code: output=76ecbb38c632bde8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c3ed3d007eb394f1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 9fff4da616ba00..9afe612bbc54f4 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -312,28 +312,23 @@ PyDoc_STRVAR(gc_get_referrers__doc__, {"get_referrers", _PyCFunction_CAST(gc_get_referrers), METH_FASTCALL, gc_get_referrers__doc__}, static PyObject * -gc_get_referrers_impl(PyObject *module, PyObject *args); +gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referrers", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gc_get_referrers_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gc_get_referrers_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -347,28 +342,23 @@ PyDoc_STRVAR(gc_get_referents__doc__, {"get_referents", _PyCFunction_CAST(gc_get_referents), METH_FASTCALL, gc_get_referents__doc__}, static PyObject * -gc_get_referents_impl(PyObject *module, PyObject *args); +gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referents", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gc_get_referents_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gc_get_referents_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -585,4 +575,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=0a7e91917adcb937 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b827f188bde9435d input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 81eec310ddb21d..54bf75dbf2468e 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -8,6 +8,64 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() +PyDoc_STRVAR(math_gcd__doc__, +"gcd($module, /, *integers)\n" +"--\n" +"\n" +"Greatest Common Divisor."); + +#define MATH_GCD_METHODDEF \ + {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, + +static PyObject * +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("gcd", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_gcd_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + +PyDoc_STRVAR(math_lcm__doc__, +"lcm($module, /, *integers)\n" +"--\n" +"\n" +"Least Common Multiple."); + +#define MATH_LCM_METHODDEF \ + {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, + +static PyObject * +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("lcm", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_lcm_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_ceil__doc__, "ceil($module, x, /)\n" "--\n" @@ -351,6 +409,46 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(math_hypot__doc__, +"hypot($module, /, *coordinates)\n" +"--\n" +"\n" +"Multidimensional Euclidean distance from the origin to a point.\n" +"\n" +"Roughly equivalent to:\n" +" sqrt(sum(x**2 for x in coordinates))\n" +"\n" +"For a two dimensional point (x, y), gives the hypotenuse\n" +"using the Pythagorean theorem: sqrt(x*x + y*y).\n" +"\n" +"For example, the hypotenuse of a 3/4/5 right triangle is:\n" +"\n" +" >>> hypot(3.0, 4.0)\n" +" 5.0"); + +#define MATH_HYPOT_METHODDEF \ + {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot__doc__}, + +static PyObject * +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_hypot_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_sumprod__doc__, "sumprod($module, p, q, /)\n" "--\n" @@ -1011,4 +1109,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=755da3b1dbd9e45f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=69376c19d96a9c50 input=a9049054013a1b77]*/ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 57e4aae9ed557e..8404a14144eed0 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -221,15 +221,29 @@ Return the list of objects that directly refer to any of 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referrers_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=296a09587f6a86b5 input=bae96961b14a0922]*/ +gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=1d44a7695ea25c40 input=bae96961b14a0922]*/ { - if (PySys_Audit("gc.get_referrers", "(O)", args) < 0) { + PyObject *varargs = PyTuple_New(nargs); + + if (!varargs) { + return NULL; + } + for (Py_ssize_t i = 0; i < nargs; i++) { + PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); + } + + if (PySys_Audit("gc.get_referrers", "(O)", varargs) < 0) { + Py_DECREF(varargs); return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); - return _PyGC_GetReferrers(interp, args); + PyObject *result = _PyGC_GetReferrers(interp, varargs); + + Py_DECREF(varargs); + return result; } /* Append obj to list; return true if error (out of memory), false if OK. */ @@ -269,27 +283,41 @@ Return the list of objects that are directly referred to by 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referents_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=d47dc02cefd06fe8 input=b3ceab0c34038cbf]*/ +gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=e459f3e8c0d19311 input=b3ceab0c34038cbf]*/ { - if (PySys_Audit("gc.get_referents", "(O)", args) < 0) { + PyObject *varargs = PyTuple_New(nargs); + + if (!varargs) { + return NULL; + } + for (Py_ssize_t i = 0; i < nargs; i++) { + PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); + } + + if (PySys_Audit("gc.get_referents", "(O)", varargs) < 0) { + Py_DECREF(varargs); return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *result = PyList_New(0); - if (result == NULL) + if (result == NULL) { + Py_DECREF(varargs); return NULL; + } // NOTE: stop the world is a no-op in default build _PyEval_StopTheWorld(interp); - int err = append_referrents(result, args); + int err = append_referrents(result, varargs); _PyEval_StartTheWorld(interp); if (err < 0) { Py_CLEAR(result); } + Py_DECREF(varargs); return result; } diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 058f57770755aa..ad23dadd7b86cc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -719,8 +719,17 @@ m_log10(double x) } +/*[clinic input] +math.gcd + + *integers as args: object + +Greatest Common Divisor. +[clinic start generated code]*/ + static PyObject * -math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=b57687fcf431c1b8 input=94e675b7ceeaf0c9]*/ { // Fast-path for the common case: gcd(int, int) if (nargs == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) @@ -763,12 +772,6 @@ math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) return res; } -PyDoc_STRVAR(math_gcd_doc, -"gcd($module, *integers)\n" -"--\n" -"\n" -"Greatest Common Divisor."); - static PyObject * long_lcm(PyObject *a, PyObject *b) @@ -798,8 +801,17 @@ long_lcm(PyObject *a, PyObject *b) } +/*[clinic input] +math.lcm + + *integers as args: object + +Least Common Multiple. +[clinic start generated code]*/ + static PyObject * -math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=f3eff0c25e4d7030 input=e64c33e85f4c47c6]*/ { PyObject *res, *x; Py_ssize_t i; @@ -839,13 +851,6 @@ math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) } -PyDoc_STRVAR(math_lcm_doc, -"lcm($module, *integers)\n" -"--\n" -"\n" -"Least Common Multiple."); - - /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -2621,9 +2626,28 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) return NULL; } -/* AC: cannot convert yet, waiting for *args support */ +/*[clinic input] +math.hypot + + *coordinates as args: object + +Multidimensional Euclidean distance from the origin to a point. + +Roughly equivalent to: + sqrt(sum(x**2 for x in coordinates)) + +For a two dimensional point (x, y), gives the hypotenuse +using the Pythagorean theorem: sqrt(x*x + y*y). + +For example, the hypotenuse of a 3/4/5 right triangle is: + + >>> hypot(3.0, 4.0) + 5.0 +[clinic start generated code]*/ + static PyObject * -math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=dcb6d4b7a1102ee1 input=5c0061a2d11235ed]*/ { Py_ssize_t i; PyObject *item; @@ -2664,22 +2688,6 @@ math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #undef NUM_STACK_ELEMS -PyDoc_STRVAR(math_hypot_doc, - "hypot(*coordinates) -> value\n\n\ -Multidimensional Euclidean distance from the origin to a point.\n\ -\n\ -Roughly equivalent to:\n\ - sqrt(sum(x**2 for x in coordinates))\n\ -\n\ -For a two dimensional point (x, y), gives the hypotenuse\n\ -using the Pythagorean theorem: sqrt(x*x + y*y).\n\ -\n\ -For example, the hypotenuse of a 3/4/5 right triangle is:\n\ -\n\ - >>> hypot(3.0, 4.0)\n\ - 5.0\n\ -"); - /** sumprod() ***************************************************************/ /* Forward declaration */ @@ -4112,14 +4120,14 @@ static PyMethodDef math_methods[] = { MATH_FREXP_METHODDEF MATH_FSUM_METHODDEF {"gamma", math_gamma, METH_O, math_gamma_doc}, - {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd_doc}, - {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot_doc}, + MATH_GCD_METHODDEF + MATH_HYPOT_METHODDEF MATH_ISCLOSE_METHODDEF MATH_ISFINITE_METHODDEF MATH_ISINF_METHODDEF MATH_ISNAN_METHODDEF MATH_ISQRT_METHODDEF - {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm_doc}, + MATH_LCM_METHODDEF MATH_LDEXP_METHODDEF {"lgamma", math_lgamma, METH_O, math_lgamma_doc}, {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc}, diff --git a/Objects/clinic/setobject.c.h b/Objects/clinic/setobject.c.h index 3853ce3bce685b..1fc1b1166d06b6 100644 --- a/Objects/clinic/setobject.c.h +++ b/Objects/clinic/setobject.c.h @@ -41,28 +41,22 @@ PyDoc_STRVAR(set_update__doc__, {"update", _PyCFunction_CAST(set_update), METH_FASTCALL, set_update__doc__}, static PyObject * -set_update_impl(PySetObject *so, PyObject *args); +set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); static PyObject * set_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_update_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_update_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -148,28 +142,22 @@ PyDoc_STRVAR(set_union__doc__, {"union", _PyCFunction_CAST(set_union), METH_FASTCALL, set_union__doc__}, static PyObject * -set_union_impl(PySetObject *so, PyObject *args); +set_union_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); static PyObject * set_union(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("union", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_union_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_union_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -183,28 +171,23 @@ PyDoc_STRVAR(set_intersection_multi__doc__, {"intersection", _PyCFunction_CAST(set_intersection_multi), METH_FASTCALL, set_intersection_multi__doc__}, static PyObject * -set_intersection_multi_impl(PySetObject *so, PyObject *args); +set_intersection_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_intersection_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_intersection_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_intersection_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -218,28 +201,23 @@ PyDoc_STRVAR(set_intersection_update_multi__doc__, {"intersection_update", _PyCFunction_CAST(set_intersection_update_multi), METH_FASTCALL, set_intersection_update_multi__doc__}, static PyObject * -set_intersection_update_multi_impl(PySetObject *so, PyObject *args); +set_intersection_update_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_intersection_update_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection_update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_intersection_update_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_intersection_update_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -277,28 +255,23 @@ PyDoc_STRVAR(set_difference_update__doc__, {"difference_update", _PyCFunction_CAST(set_difference_update), METH_FASTCALL, set_difference_update__doc__}, static PyObject * -set_difference_update_impl(PySetObject *so, PyObject *args); +set_difference_update_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_difference_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference_update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_difference_update_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_difference_update_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -312,28 +285,23 @@ PyDoc_STRVAR(set_difference_multi__doc__, {"difference", _PyCFunction_CAST(set_difference_multi), METH_FASTCALL, set_difference_multi__doc__}, static PyObject * -set_difference_multi_impl(PySetObject *so, PyObject *args); +set_difference_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_difference_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_difference_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_difference_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -568,4 +536,4 @@ set___sizeof__(PySetObject *so, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=de4ee725bd29f758 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=240fed957387b958 input=a9049054013a1b77]*/ diff --git a/Objects/setobject.c b/Objects/setobject.c index 9f40e085f06fa6..66d7fc730c555c 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1060,13 +1060,13 @@ Update the set, adding elements from all others. [clinic start generated code]*/ static PyObject * -set_update_impl(PySetObject *so, PyObject *args) -/*[clinic end generated code: output=34f6371704974c8a input=df4fe486e38cd337]*/ +set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=050e2a21f8d7d16a input=df4fe486e38cd337]*/ { Py_ssize_t i; - for (i=0 ; i None: nargs = 'PyTuple_GET_SIZE(args)' argname_fmt = 'PyTuple_GET_ITEM(args, %d)' + if self.vararg != NO_VARARG: + if self.max_pos == 0: + self.declarations = "Py_ssize_t nvararg = %s;" % nargs + else: + self.declarations = "Py_ssize_t nvararg = Py_MAX(%s - %d, 0);" % (nargs, self.max_pos) + else: + self.declarations = "" + left_args = f"{nargs} - {self.max_pos}" max_args = NO_VARARG if (self.vararg != NO_VARARG) else self.max_pos if self.limited_capi: @@ -520,28 +528,15 @@ def parse_pos_only(self) -> None: if p.is_vararg(): if self.fastcall: parser_code.append(libclinic.normalize_snippet(""" - %s = PyTuple_New(%s); - if (!%s) {{ - goto exit; - }} - for (Py_ssize_t i = 0; i < %s; ++i) {{ - PyTuple_SET_ITEM(%s, i, Py_NewRef(args[%d + i])); - }} + %s = args + %d; """ % ( p.converter.parser_name, - left_args, - p.converter.parser_name, - left_args, - p.converter.parser_name, - self.max_pos + self.vararg ), indent=4)) else: parser_code.append(libclinic.normalize_snippet(""" - %s = PyTuple_GetSlice(%d, -1); - """ % ( - p.converter.parser_name, - self.max_pos - ), indent=4)) + %s = _PyTuple_CAST(args)->ob_item; + """ % p.converter.parser_name, indent=4)) continue displayname = p.get_displayname(i+1) @@ -588,7 +583,7 @@ def parse_pos_only(self) -> None: goto exit; }} """, indent=4)] - self.parser_body(*parser_code) + self.parser_body(*parser_code, declarations=self.declarations) def parse_general(self, clang: CLanguage) -> None: parsearg: str | None From aa2044c92fdf82b61d90f195ad3ead7ca78f6674 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 10:43:14 +0300 Subject: [PATCH 2/8] + fix mypy and lint errors --- Tools/clinic/libclinic/parse_args.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index 64b1cde8a4d9b4..db26f084e424e3 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -477,7 +477,6 @@ def parse_pos_only(self) -> None: else: self.declarations = "" - left_args = f"{nargs} - {self.max_pos}" max_args = NO_VARARG if (self.vararg != NO_VARARG) else self.max_pos if self.limited_capi: parser_code = [] @@ -528,7 +527,7 @@ def parse_pos_only(self) -> None: if p.is_vararg(): if self.fastcall: parser_code.append(libclinic.normalize_snippet(""" - %s = args + %d; + %s = args + %s; """ % ( p.converter.parser_name, self.vararg From b3a9a78652843a66eacfe375f3c9068e4615b10c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 11:46:05 +0300 Subject: [PATCH 3/8] address review: revert math changes --- Modules/clinic/mathmodule.c.h | 100 +--------------------------------- Modules/mathmodule.c | 80 ++++++++++++--------------- 2 files changed, 37 insertions(+), 143 deletions(-) diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 54bf75dbf2468e..81eec310ddb21d 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -8,64 +8,6 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() -PyDoc_STRVAR(math_gcd__doc__, -"gcd($module, /, *integers)\n" -"--\n" -"\n" -"Greatest Common Divisor."); - -#define MATH_GCD_METHODDEF \ - {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, - -static PyObject * -math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); - -static PyObject * -math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; - PyObject *const *__clinic_args = NULL; - - if (!_PyArg_CheckPositional("gcd", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_gcd_impl(module, nvararg, __clinic_args); - -exit: - return return_value; -} - -PyDoc_STRVAR(math_lcm__doc__, -"lcm($module, /, *integers)\n" -"--\n" -"\n" -"Least Common Multiple."); - -#define MATH_LCM_METHODDEF \ - {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, - -static PyObject * -math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); - -static PyObject * -math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; - PyObject *const *__clinic_args = NULL; - - if (!_PyArg_CheckPositional("lcm", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_lcm_impl(module, nvararg, __clinic_args); - -exit: - return return_value; -} - PyDoc_STRVAR(math_ceil__doc__, "ceil($module, x, /)\n" "--\n" @@ -409,46 +351,6 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -PyDoc_STRVAR(math_hypot__doc__, -"hypot($module, /, *coordinates)\n" -"--\n" -"\n" -"Multidimensional Euclidean distance from the origin to a point.\n" -"\n" -"Roughly equivalent to:\n" -" sqrt(sum(x**2 for x in coordinates))\n" -"\n" -"For a two dimensional point (x, y), gives the hypotenuse\n" -"using the Pythagorean theorem: sqrt(x*x + y*y).\n" -"\n" -"For example, the hypotenuse of a 3/4/5 right triangle is:\n" -"\n" -" >>> hypot(3.0, 4.0)\n" -" 5.0"); - -#define MATH_HYPOT_METHODDEF \ - {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot__doc__}, - -static PyObject * -math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); - -static PyObject * -math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; - PyObject *const *__clinic_args = NULL; - - if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) { - goto exit; - } - __clinic_args = args + 0; - return_value = math_hypot_impl(module, nvararg, __clinic_args); - -exit: - return return_value; -} - PyDoc_STRVAR(math_sumprod__doc__, "sumprod($module, p, q, /)\n" "--\n" @@ -1109,4 +1011,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=69376c19d96a9c50 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=755da3b1dbd9e45f input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index ad23dadd7b86cc..058f57770755aa 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -719,17 +719,8 @@ m_log10(double x) } -/*[clinic input] -math.gcd - - *integers as args: object - -Greatest Common Divisor. -[clinic start generated code]*/ - static PyObject * -math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=b57687fcf431c1b8 input=94e675b7ceeaf0c9]*/ +math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) { // Fast-path for the common case: gcd(int, int) if (nargs == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) @@ -772,6 +763,12 @@ math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) return res; } +PyDoc_STRVAR(math_gcd_doc, +"gcd($module, *integers)\n" +"--\n" +"\n" +"Greatest Common Divisor."); + static PyObject * long_lcm(PyObject *a, PyObject *b) @@ -801,17 +798,8 @@ long_lcm(PyObject *a, PyObject *b) } -/*[clinic input] -math.lcm - - *integers as args: object - -Least Common Multiple. -[clinic start generated code]*/ - static PyObject * -math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=f3eff0c25e4d7030 input=e64c33e85f4c47c6]*/ +math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) { PyObject *res, *x; Py_ssize_t i; @@ -851,6 +839,13 @@ math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) } +PyDoc_STRVAR(math_lcm_doc, +"lcm($module, *integers)\n" +"--\n" +"\n" +"Least Common Multiple."); + + /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -2626,28 +2621,9 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) return NULL; } -/*[clinic input] -math.hypot - - *coordinates as args: object - -Multidimensional Euclidean distance from the origin to a point. - -Roughly equivalent to: - sqrt(sum(x**2 for x in coordinates)) - -For a two dimensional point (x, y), gives the hypotenuse -using the Pythagorean theorem: sqrt(x*x + y*y). - -For example, the hypotenuse of a 3/4/5 right triangle is: - - >>> hypot(3.0, 4.0) - 5.0 -[clinic start generated code]*/ - +/* AC: cannot convert yet, waiting for *args support */ static PyObject * -math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=dcb6d4b7a1102ee1 input=5c0061a2d11235ed]*/ +math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { Py_ssize_t i; PyObject *item; @@ -2688,6 +2664,22 @@ math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) #undef NUM_STACK_ELEMS +PyDoc_STRVAR(math_hypot_doc, + "hypot(*coordinates) -> value\n\n\ +Multidimensional Euclidean distance from the origin to a point.\n\ +\n\ +Roughly equivalent to:\n\ + sqrt(sum(x**2 for x in coordinates))\n\ +\n\ +For a two dimensional point (x, y), gives the hypotenuse\n\ +using the Pythagorean theorem: sqrt(x*x + y*y).\n\ +\n\ +For example, the hypotenuse of a 3/4/5 right triangle is:\n\ +\n\ + >>> hypot(3.0, 4.0)\n\ + 5.0\n\ +"); + /** sumprod() ***************************************************************/ /* Forward declaration */ @@ -4120,14 +4112,14 @@ static PyMethodDef math_methods[] = { MATH_FREXP_METHODDEF MATH_FSUM_METHODDEF {"gamma", math_gamma, METH_O, math_gamma_doc}, - MATH_GCD_METHODDEF - MATH_HYPOT_METHODDEF + {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd_doc}, + {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot_doc}, MATH_ISCLOSE_METHODDEF MATH_ISFINITE_METHODDEF MATH_ISINF_METHODDEF MATH_ISNAN_METHODDEF MATH_ISQRT_METHODDEF - MATH_LCM_METHODDEF + {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm_doc}, MATH_LDEXP_METHODDEF {"lgamma", math_lgamma, METH_O, math_lgamma_doc}, {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc}, From 42c977a6af6e9063dbb1e3681042c19b2c3329a3 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 13:00:33 +0300 Subject: [PATCH 4/8] address review: has_varag_hack -> pass_vararg_directly --- Tools/clinic/libclinic/clanguage.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index 365d0e6a37225b..32aba81ab8a850 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -399,8 +399,9 @@ def render_function( requires_defining_class = (len(selfless) and isinstance(selfless[0].converter, defining_class_converter)) - has_varag_hack = all(p.is_positional_only() or p.is_vararg() - for p in selfless) and not requires_defining_class + pass_vararg_directly = (all(p.is_positional_only() or p.is_vararg() + for p in selfless) + and not requires_defining_class) # offset i by -1 because first_optional needs to ignore self for i, p in enumerate(parameters, -1): @@ -409,7 +410,7 @@ def render_function( if (i != -1) and (p.default is not unspecified): first_optional = min(first_optional, i) - if p.is_vararg() and not has_varag_hack: + if p.is_vararg() and not pass_vararg_directly: data.cleanup.append(f"Py_XDECREF({c.parser_name});") # insert group variable @@ -423,7 +424,7 @@ def render_function( data.impl_parameters.append("int " + group_name) has_option_groups = True - if p.is_vararg() and has_varag_hack: + if p.is_vararg() and pass_vararg_directly: data.impl_arguments.append('nvararg') data.impl_parameters.append('Py_ssize_t nargs') p.converter.type = 'PyObject *const *' From 1dc2756cf9566ce07a00c1a8f19f7f0941c2c563 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 15:14:50 +0300 Subject: [PATCH 5/8] address review: use _PyTuple_FromArray() --- Modules/gcmodule.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 8404a14144eed0..f5fea5aa4dde08 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -8,6 +8,7 @@ #include "pycore_gc.h" #include "pycore_object.h" // _PyObject_IS_GC() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_tuple.h" // _PyTuple_FromArray() typedef struct _gc_runtime_state GCState; @@ -225,15 +226,11 @@ gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) /*[clinic end generated code: output=1d44a7695ea25c40 input=bae96961b14a0922]*/ { - PyObject *varargs = PyTuple_New(nargs); + PyObject *varargs = _PyTuple_FromArray(args, nargs); if (!varargs) { return NULL; } - for (Py_ssize_t i = 0; i < nargs; i++) { - PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); - } - if (PySys_Audit("gc.get_referrers", "(O)", varargs) < 0) { Py_DECREF(varargs); return NULL; @@ -287,15 +284,11 @@ gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) /*[clinic end generated code: output=e459f3e8c0d19311 input=b3ceab0c34038cbf]*/ { - PyObject *varargs = PyTuple_New(nargs); + PyObject *varargs = _PyTuple_FromArray(args, nargs); if (!varargs) { return NULL; } - for (Py_ssize_t i = 0; i < nargs; i++) { - PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); - } - if (PySys_Audit("gc.get_referents", "(O)", varargs) < 0) { Py_DECREF(varargs); return NULL; From 00a356b9b2dcf6eccb285a06ca050e054ee7e69f Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 16:48:47 +0300 Subject: [PATCH 6/8] address review, style: use f-strings, code cleanup --- Tools/clinic/libclinic/parse_args.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index db26f084e424e3..559d4fbdd09c57 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -470,10 +470,7 @@ def parse_pos_only(self) -> None: argname_fmt = 'PyTuple_GET_ITEM(args, %d)' if self.vararg != NO_VARARG: - if self.max_pos == 0: - self.declarations = "Py_ssize_t nvararg = %s;" % nargs - else: - self.declarations = "Py_ssize_t nvararg = Py_MAX(%s - %d, 0);" % (nargs, self.max_pos) + self.declarations = f"Py_ssize_t nvararg = {nargs} - {self.max_pos};" else: self.declarations = "" @@ -525,17 +522,13 @@ def parse_pos_only(self) -> None: use_parser_code = True for i, p in enumerate(self.parameters): if p.is_vararg(): + var = p.converter.parser_name if self.fastcall: - parser_code.append(libclinic.normalize_snippet(""" - %s = args + %s; - """ % ( - p.converter.parser_name, - self.vararg - ), indent=4)) + code = f"{var} = args + {self.vararg};" else: - parser_code.append(libclinic.normalize_snippet(""" - %s = _PyTuple_CAST(args)->ob_item; - """ % p.converter.parser_name, indent=4)) + code = f"{var} = _PyTuple_CAST(args)->ob_item;" + formatted_code = libclinic.normalize_snippet(code, indent=4) + parser_code.append(formatted_code) continue displayname = p.get_displayname(i+1) From db6b0f315f4032902287807adbffa6b06fcdb255 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 17:17:03 +0300 Subject: [PATCH 7/8] +1 --- Modules/clinic/_testclinic.c.h | 8 ++++---- Modules/clinic/gcmodule.c.h | 6 +++--- Objects/clinic/setobject.c.h | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index b94d8abcb29310..7e29998c7db520 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2536,7 +2536,7 @@ static PyObject * varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("varpos", nargs, 0, PY_SSIZE_T_MAX)) { @@ -2565,7 +2565,7 @@ static PyObject * posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = Py_MAX(nargs - 2, 0); + Py_ssize_t nvararg = nargs - 2; PyObject *a; PyObject *b; PyObject *const *__clinic_args = NULL; @@ -3131,7 +3131,7 @@ static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { @@ -3676,4 +3676,4 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, Py_XDECREF(__clinic_args); return return_value; } -/*[clinic end generated code: output=c3ed3d007eb394f1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7662d07e7d29cbeb input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 9afe612bbc54f4..be3bd35b4ffd44 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -319,7 +319,7 @@ static PyObject * gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referrers", nargs, 0, PY_SSIZE_T_MAX)) { @@ -349,7 +349,7 @@ static PyObject * gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referents", nargs, 0, PY_SSIZE_T_MAX)) { @@ -575,4 +575,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=b827f188bde9435d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f488a0d4d6bd3687 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/setobject.c.h b/Objects/clinic/setobject.c.h index 1fc1b1166d06b6..d6e381a9975050 100644 --- a/Objects/clinic/setobject.c.h +++ b/Objects/clinic/setobject.c.h @@ -47,7 +47,7 @@ static PyObject * set_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("update", nargs, 0, PY_SSIZE_T_MAX)) { @@ -148,7 +148,7 @@ static PyObject * set_union(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("union", nargs, 0, PY_SSIZE_T_MAX)) { @@ -178,7 +178,7 @@ static PyObject * set_intersection_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection", nargs, 0, PY_SSIZE_T_MAX)) { @@ -208,7 +208,7 @@ static PyObject * set_intersection_update_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection_update", nargs, 0, PY_SSIZE_T_MAX)) { @@ -262,7 +262,7 @@ static PyObject * set_difference_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference_update", nargs, 0, PY_SSIZE_T_MAX)) { @@ -292,7 +292,7 @@ static PyObject * set_difference_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = nargs; + Py_ssize_t nvararg = nargs - 0; PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference", nargs, 0, PY_SSIZE_T_MAX)) { @@ -536,4 +536,4 @@ set___sizeof__(PySetObject *so, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=240fed957387b958 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9d4b41191b2c602f input=a9049054013a1b77]*/ From f17955759d6caae92c57983491462f345f9f4d45 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 28 Oct 2024 17:26:11 +0300 Subject: [PATCH 8/8] huh, make clinic doesn't do this --- Lib/test/clinic.test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 66f30509688f2e..e22324efc490be 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4155,7 +4155,7 @@ static PyObject * test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t nvararg = Py_MAX(nargs - 1, 0); + Py_ssize_t nvararg = nargs - 1; PyObject *a; PyObject *const *__clinic_args = NULL; @@ -4173,7 +4173,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, PyObject *const *args) -/*[clinic end generated code: output=f0f68154d891dd6d input=9cfa748bbff09877]*/ +/*[clinic end generated code: output=dc2dd9483cc0459e input=9cfa748bbff09877]*/ /*[clinic input] test_vararg