diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 88f34fe7513bf2..3afec5a3f62c1f 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -99,6 +99,13 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( int minpos, int maxpos, int minkw, int vararg, PyObject **buf); +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVarargKwonly( + PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + struct _PyArg_Parser *parser, + int minpos, int maxpos, int minkw, + int vararg, PyObject **buf); + #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ (minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? (args) : \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 53e5df5ba872ed..c219a909d352a1 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3767,36 +3767,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 varargssize, 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 varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; 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, varargssize, __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=08dc2bf7afbf1613]*/ +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=0c242df582a012d1 input=08dc2bf7afbf1613]*/ /*[clinic input] test_vararg @@ -3816,7 +3812,8 @@ PyDoc_STRVAR(test_vararg__doc__, {"test_vararg", _PyCFunction_CAST(test_vararg), METH_FASTCALL|METH_KEYWORDS, test_vararg__doc__}, static PyObject * -test_vararg_impl(PyObject *module, PyObject *a, PyObject *args); +test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args); static PyObject * test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -3847,26 +3844,28 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[0]; + PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; - return_value = test_vararg_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = test_vararg_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=880365c61ae205d7 input=81d33815ad1bae6e]*/ +test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=2f4921abd6707648 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3887,7 +3886,8 @@ PyDoc_STRVAR(test_vararg_with_default__doc__, {"test_vararg_with_default", _PyCFunction_CAST(test_vararg_with_default), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default__doc__}, static PyObject * -test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +test_vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b); static PyObject * @@ -3919,37 +3919,39 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } skip_optional_kwonly: - return_value = test_vararg_with_default_impl(module, a, __clinic_args, b); + return_value = test_vararg_with_default_impl(module, a, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +test_vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=291e9a5a09831128 input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=60a4333c34883cc3 input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3970,7 +3972,8 @@ PyDoc_STRVAR(test_vararg_with_only_defaults__doc__, {"test_vararg_with_only_defaults", _PyCFunction_CAST(test_vararg_with_only_defaults), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_only_defaults__doc__}, static PyObject * -test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, +test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, int b, PyObject *c); static PyObject * @@ -4002,22 +4005,24 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *__clinic_args; int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - b = PyObject_IsTrue(args[1]); + if (fastargs[0]) { + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } @@ -4025,19 +4030,19 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize goto skip_optional_kwonly; } } - c = args[2]; + c = fastargs[1]; skip_optional_kwonly: - return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c); + return_value = test_vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b, c); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, +test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=dd21b28f0db26a4b input=fa56a709a035666e]*/ +/*[clinic end generated code: output=fd25c0b3171663cc input=fa56a709a035666e]*/ /*[clinic input] test_paramname_module @@ -4102,3 +4107,259 @@ exit: static PyObject * test_paramname_module_impl(PyObject *module, PyObject *mod) /*[clinic end generated code: output=4a2a849ecbcc8b53 input=afefe259667f13ba]*/ + +/*[clinic input] +test_vararg_with_other_name + + *strange_name_vararg: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_other_name__doc__, +"test_vararg_with_other_name($module, /, *strange_name_vararg)\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_OTHER_NAME_METHODDEF \ + {"test_vararg_with_other_name", _PyCFunction_CAST(test_vararg_with_other_name), METH_FASTCALL, test_vararg_with_other_name__doc__}, + +static PyObject * +test_vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg); + +static PyObject * +test_vararg_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *strange_name_vararg; + + if (!_PyArg_CheckPositional("test_vararg_with_other_name", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + strange_name_vararg = args + 0; + return_value = test_vararg_with_other_name_impl(module, varargssize, strange_name_vararg); + +exit: + return return_value; +} + +static PyObject * +test_vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg) +/*[clinic end generated code: output=7340df6f8a2b95a8 input=44747a55460ea494]*/ + +/*[clinic input] +test_vararg_with_default_with_other_name + + a as other_a: object + *args as other_args: object + b as other_b: bool = False + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_default_with_other_name__doc__, +"test_vararg_with_default_with_other_name($module, /, a, *args, b=False)\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF \ + {"test_vararg_with_default_with_other_name", _PyCFunction_CAST(test_vararg_with_default_with_other_name), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default_with_other_name__doc__}, + +static PyObject * +test_vararg_with_default_with_other_name_impl(PyObject *module, + PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b); + +static PyObject * +test_vararg_with_default_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_vararg_with_default_with_other_name", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *const *fastargs; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *other_a; + PyObject *const *other_args; + int other_b = 0; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + other_a = args[0]; + other_args = args + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + other_b = PyObject_IsTrue(fastargs[0]); + if (other_b < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = test_vararg_with_default_with_other_name_impl(module, other_a, varargssize, other_args, other_b); + +exit: + return return_value; +} + +static PyObject * +test_vararg_with_default_with_other_name_impl(PyObject *module, + PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b) +/*[clinic end generated code: output=d2149d17eba67671 input=779a17a90096c922]*/ + + +/*[clinic input] +module TestModule +class TestModule.TestClass1 "TestClassObject *" "&TestModule_Type" +class TestModule.TestClass2 "TestClassObject *" "&TestModule_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cd38925654911fd4]*/ + + +/*[clinic input] +TestModule.TestClass1.__init__ + + *args: object + +[clinic start generated code]*/ + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, + Py_ssize_t varargssize, + PyObject *const *args); + +static int +TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + Py_ssize_t varargssize = PyTuple_GET_SIZE(args); + PyTypeObject *base_tp = &TestModule_Type; + PyObject *const *__clinic_args; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("TestClass1", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass1", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, varargssize, __clinic_args); + +exit: + return return_value; +} + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=3f3f406f43e296d2 input=3e093b8ea0b74b0c]*/ + + +/*[clinic input] +TestModule.TestClass2.__init__ + + pos: object + *args: object + kw: object = None + +[clinic start generated code]*/ + +static int +TestModule_TestClass2___init___impl(TestClassObject *self, PyObject *pos, + Py_ssize_t varargssize, + PyObject *const *args, PyObject *kw); + +static int +TestModule_TestClass2___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(pos), &_Py_ID(kw), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pos", "kw", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClass2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *pos; + PyObject *const *__clinic_args; + PyObject *kw = Py_None; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + pos = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + kw = fastargs[0]; +skip_optional_kwonly: + return_value = TestModule_TestClass2___init___impl((TestClassObject *)self, pos, varargssize, __clinic_args, kw); + +exit: + return return_value; +} + +static int +TestModule_TestClass2___init___impl(TestClassObject *self, PyObject *pos, + Py_ssize_t varargssize, + PyObject *const *args, PyObject *kw) +/*[clinic end generated code: output=335f080bcb0946ab input=833936c1d51c4329]*/ diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 4abf739cf52ca3..076e6dffbf5e57 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1234,6 +1234,8 @@ def test_keyword_only_parameter(self): def test_posonly_vararg(self): with self.assertRaises(TypeError): ac_tester.posonly_vararg() + with self.assertRaises(TypeError): + ac_tester.posonly_vararg(1, invalid_kw=2) self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ())) self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ())) self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4))) @@ -1242,30 +1244,72 @@ def test_vararg_and_posonly(self): with self.assertRaises(TypeError): ac_tester.vararg_and_posonly() with self.assertRaises(TypeError): - ac_tester.vararg_and_posonly(1, b=2) + ac_tester.vararg_and_posonly(1, invalid_kw=2) self.assertEqual(ac_tester.vararg_and_posonly(1, 2, 3, 4), (1, (2, 3, 4))) def test_vararg(self): with self.assertRaises(TypeError): ac_tester.vararg() with self.assertRaises(TypeError): - ac_tester.vararg(1, b=2) + ac_tester.vararg(1, invalid_kw=2) self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4))) def test_vararg_with_default(self): with self.assertRaises(TypeError): ac_tester.vararg_with_default() + with self.assertRaises(TypeError): + ac_tester.vararg_with_default(1, invalid_kw=1) self.assertEqual(ac_tester.vararg_with_default(1, b=False), (1, (), False)) self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), False)) self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4, b=True), (1, (2, 3, 4), True)) def test_vararg_with_only_defaults(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_only_defaults(invalid_kw=1) self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None)) self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5)) + def test_vararg_with_other_name(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_other_name(invalid_kw=2) + self.assertEqual(ac_tester.vararg_with_other_name(), ((), )) + self.assertEqual(ac_tester.vararg_with_other_name(1, 2, 3, 4), ((1, 2, 3, 4), )) + + def test_vararg_with_default_with_other_name(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_default_with_other_name() + with self.assertRaises(TypeError): + ac_tester.vararg_with_default_with_other_name(1, invalid_kw=2) + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, b=False), (1, (), False)) + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4), (1, (2, 3, 4), False)) + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4, b=True), (1, (2, 3, 4), True)) + + def test_class_new_vararg_only(self): + # TODO: Here a TypeError should be raised + # with self.assertRaises(TypeError): + # ac_tester.class_new_vararg_only(invalid_kw=1) + self.assertEqual(ac_tester.class_new_vararg_only(), ((), )) + self.assertEqual(ac_tester.class_new_vararg_only(1, 2, 3, 4), ((1, 2, 3, 4), )) + + def test_class_new_vararg(self): + with self.assertRaises(TypeError): + ac_tester.class_new_vararg() + with self.assertRaises(TypeError): + ac_tester.class_new_vararg(1, invalid_kw=2) + self.assertEqual(ac_tester.class_new_vararg(1, 2, 3, 4), (1, (2, 3, 4))) + + def test_class_new_vararg_with_default(self): + with self.assertRaises(TypeError): + ac_tester.class_new_vararg_with_default() + with self.assertRaises(TypeError): + ac_tester.class_new_vararg_with_default(1, invalid_kw=2) + self.assertEqual(ac_tester.class_new_vararg_with_default(1, b=2), (1, (), 2)) + self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), None)) + self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4, b=5), (1, (2, 3, 4), 5)) + def test_gh_32092_oob(self): ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst new file mode 100644 index 00000000000000..104040ab3bfa15 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst @@ -0,0 +1,5 @@ +Avoid creating temporary varargs tuple in argument passing with Argument Clinic generated code. + +Introduce the :c:func:`_PyArg_UnpackKeywordsWithVarargKwonly` function, +which checks and parses positional arguments, varargs, and keyword arguments, +but returns only parsed kwonly arguments. diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 91fdee24d328d9..6f118d027d98db 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -9,9 +9,29 @@ #include "Python.h" +typedef struct TestClassObject { + PyObject_HEAD +} TestClassObject; + +static PyTypeObject TestClass_Type; + #include "clinic/_testclinic.c.h" +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, implicitly increase all the arguments' refcount. * NULL arguments will be replaced to Py_None. */ static PyObject * @@ -962,10 +982,16 @@ posonly_vararg static PyObject * posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args) -/*[clinic end generated code: output=ee6713acda6b954e input=783427fe7ec2b67a]*/ + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=744bd14cfaa7b46c input=783427fe7ec2b67a]*/ { - return pack_arguments_newref(3, a, b, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -979,10 +1005,17 @@ vararg_and_posonly [clinic start generated code]*/ static PyObject * -vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=42792f799465a14d input=defe017b19ba52e8]*/ +vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=75f9bcbfc99efb90 input=defe017b19ba52e8]*/ { - return pack_arguments_newref(2, a, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -995,10 +1028,17 @@ vararg [clinic start generated code]*/ static PyObject * -vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=91ab7a0efc52dd5e input=02c0f772d05f591e]*/ +vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=36e183ea6ecb9f8f input=02c0f772d05f591e]*/ { - return pack_arguments_newref(2, a, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -1012,12 +1052,19 @@ vararg_with_default [clinic start generated code]*/ static PyObject * -vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=182c01035958ce92 input=68cafa6a79f89e36]*/ +/*[clinic end generated code: output=f4e18bc71825f3f9 input=68cafa6a79f89e36]*/ { + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } PyObject *obj_b = b ? Py_True : Py_False; - return pack_arguments_newref(3, a, args, obj_b); + PyObject *result = pack_arguments_newref(3, a, vararg_tuple, obj_b); + Py_DECREF(vararg_tuple); + return result; } @@ -1030,10 +1077,158 @@ vararg_with_only_defaults [clinic start generated code]*/ static PyObject * -vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b) -/*[clinic end generated code: output=c06b1826d91f2f7b input=678c069bc67550e1]*/ +vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *b) +/*[clinic end generated code: output=049328b782106d03 input=678c069bc67550e1]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, vararg_tuple, b); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +vararg_with_other_name + + *strange_name_vararg: object + +[clinic start generated code]*/ + +static PyObject * +vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg) +/*[clinic end generated code: output=73a50a717838e561 input=db56857a45dfa50e]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, strange_name_vararg); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +vararg_with_default_with_other_name + + a as other_a: object + *args as other_args: object + b as other_b: bool = False + +[clinic start generated code]*/ + +static PyObject * +vararg_with_default_with_other_name_impl(PyObject *module, PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b) +/*[clinic end generated code: output=a7ac0fa0c132a04f input=b895540d71cd74f9]*/ { - return pack_arguments_newref(2, args, b); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, other_args); + if (!vararg_tuple) { + return NULL; + } + PyObject *obj_b = other_b ? Py_True : Py_False; + PyObject *result = pack_arguments_newref(3, other_a, vararg_tuple, obj_b); + Py_DECREF(vararg_tuple); + return result; +} + + + +static PyTypeObject TestClass_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "TestClassType", + .tp_basicsize = sizeof(TestClassObject), + .tp_flags = Py_TPFLAGS_DEFAULT, +}; + +/*[clinic input] +class _testclinic.TestClassVararg "TestClassObject *" "&TestClass_Type" +class _testclinic.TestClassVarargOnly "TestClassObject *" "&TestClass_Type" +class _testclinic.TestClassVarargWithDefault "TestClassObject *" "&TestClass_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=97c670b477b79c5a]*/ + + +/*[clinic input] +@classmethod +_testclinic.TestClassVarargOnly.__new__ + + *args: object + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVarargOnly_impl(PyTypeObject *type, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=05947a2aa5f8adff input=584464d2a3960b1b]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +@classmethod +_testclinic.TestClassVararg.__new__ + + a: object + *args: object + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVararg_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=631d42b9183732a2 input=88e1a6be73982e01]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +@classmethod +_testclinic.TestClassVarargWithDefault.__new__ + + a: object + *args: object + b: object = None + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVarargWithDefault_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args, + PyObject *b) +/*[clinic end generated code: output=f91a821bb9f592a9 input=19c364f334bc623e]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, vararg_tuple, b); + Py_DECREF(vararg_tuple); + return result; } @@ -1053,8 +1248,9 @@ Proof-of-concept of GH-32092 OOB bug. static PyObject * gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, - PyObject *varargs, PyObject *kw1, PyObject *kw2) -/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/ + Py_ssize_t varargssize, PyObject *const *varargs, + PyObject *kw1, PyObject *kw2) +/*[clinic end generated code: output=ecb8573caf9791ac input=46d15c881608f8ff]*/ { Py_RETURN_NONE; } @@ -1072,9 +1268,10 @@ Proof-of-concept of GH-32092 keyword args passing bug. [clinic start generated code]*/ static PyObject * -gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, +gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, + Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/ +/*[clinic end generated code: output=5fb8caec2618b065 input=5c0bd5b9079a0cce]*/ { Py_RETURN_NONE; } @@ -1091,8 +1288,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=85f5fb47ac91a626]*/ +gh_99233_refcount_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=2712f6e3397e4eca input=85f5fb47ac91a626]*/ { Py_RETURN_NONE; } @@ -1164,6 +1362,20 @@ static PyMethodDef tester_methods[] = { VARARG_METHODDEF VARARG_WITH_DEFAULT_METHODDEF VARARG_WITH_ONLY_DEFAULTS_METHODDEF + VARARG_WITH_OTHER_NAME_METHODDEF + VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF + {"class_new_vararg_only", + _PyCFunction_CAST(_testclinic_TestClassVarargOnly), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test vararg parsing in class method `__new__`.")}, + {"class_new_vararg", + _PyCFunction_CAST(_testclinic_TestClassVararg), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test posargs and vararg parsing in class method `__new__`.")}, + {"class_new_vararg_with_default", + _PyCFunction_CAST(_testclinic_TestClassVarargWithDefault), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test vararg and kwargs parsing in class method `__new__`.")}, GH_32092_OOB_METHODDEF GH_32092_KW_PASS_METHODDEF GH_99233_REFCOUNT_METHODDEF diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 831f58ca650aab..4dd2d86dfdb6c0 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2336,7 +2336,7 @@ PyDoc_STRVAR(posonly_vararg__doc__, static PyObject * posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args); + Py_ssize_t varargssize, PyObject *const *args); static PyObject * posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2367,22 +2367,23 @@ posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[0]; + PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *a; PyObject *b; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; b = args[1]; - __clinic_args = args[2]; - return_value = posonly_vararg_impl(module, a, b, __clinic_args); + __clinic_args = args + 2; + return_value = posonly_vararg_impl(module, a, b, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2395,30 +2396,25 @@ PyDoc_STRVAR(vararg_and_posonly__doc__, {"vararg_and_posonly", _PyCFunction_CAST(vararg_and_posonly), METH_FASTCALL, vararg_and_posonly__doc__}, static PyObject * -vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); +vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args); static PyObject * vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; if (!_PyArg_CheckPositional("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 = vararg_and_posonly_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = vararg_and_posonly_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2431,7 +2427,8 @@ PyDoc_STRVAR(vararg__doc__, {"vararg", _PyCFunction_CAST(vararg), METH_FASTCALL|METH_KEYWORDS, vararg__doc__}, static PyObject * -vararg_impl(PyObject *module, PyObject *a, PyObject *args); +vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args); static PyObject * vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2462,20 +2459,21 @@ vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwna .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[0]; + PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; - return_value = vararg_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = vararg_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2488,7 +2486,8 @@ PyDoc_STRVAR(vararg_with_default__doc__, {"vararg_with_default", _PyCFunction_CAST(vararg_with_default), METH_FASTCALL|METH_KEYWORDS, vararg_with_default__doc__}, static PyObject * -vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b); static PyObject * @@ -2520,30 +2519,31 @@ vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } skip_optional_kwonly: - return_value = vararg_with_default_impl(module, a, __clinic_args, b); + return_value = vararg_with_default_impl(module, a, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2556,7 +2556,8 @@ PyDoc_STRVAR(vararg_with_only_defaults__doc__, {"vararg_with_only_defaults", _PyCFunction_CAST(vararg_with_only_defaults), METH_FASTCALL|METH_KEYWORDS, vararg_with_only_defaults__doc__}, static PyObject * -vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b); +vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *b); static PyObject * vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2587,25 +2588,267 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[1]; + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *__clinic_args; PyObject *b = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - b = args[1]; + b = fastargs[0]; +skip_optional_kwonly: + return_value = vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b); + +exit: + return return_value; +} + +PyDoc_STRVAR(vararg_with_other_name__doc__, +"vararg_with_other_name($module, /, *strange_name_vararg)\n" +"--\n" +"\n"); + +#define VARARG_WITH_OTHER_NAME_METHODDEF \ + {"vararg_with_other_name", _PyCFunction_CAST(vararg_with_other_name), METH_FASTCALL, vararg_with_other_name__doc__}, + +static PyObject * +vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg); + +static PyObject * +vararg_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *strange_name_vararg; + + if (!_PyArg_CheckPositional("vararg_with_other_name", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + strange_name_vararg = args + 0; + return_value = vararg_with_other_name_impl(module, varargssize, strange_name_vararg); + +exit: + return return_value; +} + +PyDoc_STRVAR(vararg_with_default_with_other_name__doc__, +"vararg_with_default_with_other_name($module, /, a, *args, b=False)\n" +"--\n" +"\n"); + +#define VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF \ + {"vararg_with_default_with_other_name", _PyCFunction_CAST(vararg_with_default_with_other_name), METH_FASTCALL|METH_KEYWORDS, vararg_with_default_with_other_name__doc__}, + +static PyObject * +vararg_with_default_with_other_name_impl(PyObject *module, PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b); + +static PyObject * +vararg_with_default_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "vararg_with_default_with_other_name", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *const *fastargs; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *other_a; + PyObject *const *other_args; + int other_b = 0; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + other_a = args[0]; + other_args = args + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + other_b = PyObject_IsTrue(fastargs[0]); + if (other_b < 0) { + goto exit; + } skip_optional_kwonly: - return_value = vararg_with_only_defaults_impl(module, __clinic_args, b); + return_value = vararg_with_default_with_other_name_impl(module, other_a, varargssize, other_args, other_b); + +exit: + return return_value; +} + +static PyObject * +_testclinic_TestClassVarargOnly_impl(PyTypeObject *type, + Py_ssize_t varargssize, + PyObject *const *args); + +static PyObject * +_testclinic_TestClassVarargOnly(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = PyTuple_GET_SIZE(args); + PyTypeObject *base_tp = &TestClass_Type; + PyObject *const *__clinic_args; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClassVarargOnly", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClassVarargOnly", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = _testclinic_TestClassVarargOnly_impl(type, varargssize, __clinic_args); + +exit: + return return_value; +} + +static PyObject * +_testclinic_TestClassVararg_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args); + +static PyObject * +_testclinic_TestClassVararg(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClassVararg", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[0]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *a; + PyObject *const *__clinic_args; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; + return_value = _testclinic_TestClassVararg_impl(type, a, varargssize, __clinic_args); + +exit: + return return_value; +} + +static PyObject * +_testclinic_TestClassVarargWithDefault_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args, + PyObject *b); + +static PyObject * +_testclinic_TestClassVarargWithDefault(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClassVarargWithDefault", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *a; + PyObject *const *__clinic_args; + PyObject *b = Py_None; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + b = fastargs[0]; +skip_optional_kwonly: + return_value = _testclinic_TestClassVarargWithDefault_impl(type, a, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2620,7 +2863,8 @@ PyDoc_STRVAR(gh_32092_oob__doc__, static PyObject * gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, - PyObject *varargs, PyObject *kw1, PyObject *kw2); + Py_ssize_t varargssize, PyObject *const *varargs, + PyObject *kw1, PyObject *kw2); static PyObject * gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2651,36 +2895,37 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[2]; + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 2) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *pos1; PyObject *pos2; - PyObject *varargs = NULL; + PyObject *const *varargs; PyObject *kw1 = Py_None; PyObject *kw2 = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); + if (!fastargs) { goto exit; } pos1 = args[0]; pos2 = args[1]; - varargs = args[2]; + varargs = args + 2; if (!noptargs) { goto skip_optional_kwonly; } - if (args[3]) { - kw1 = args[3]; + if (fastargs[0]) { + kw1 = fastargs[0]; if (!--noptargs) { goto skip_optional_kwonly; } } - kw2 = args[4]; + kw2 = fastargs[1]; skip_optional_kwonly: - return_value = gh_32092_oob_impl(module, pos1, pos2, varargs, kw1, kw2); + return_value = gh_32092_oob_impl(module, pos1, pos2, varargssize, varargs, kw1, kw2); exit: - Py_XDECREF(varargs); return return_value; } @@ -2694,7 +2939,8 @@ PyDoc_STRVAR(gh_32092_kw_pass__doc__, {"gh_32092_kw_pass", _PyCFunction_CAST(gh_32092_kw_pass), METH_FASTCALL|METH_KEYWORDS, gh_32092_kw_pass__doc__}, static PyObject * -gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, +gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, + Py_ssize_t varargssize, PyObject *const *args, PyObject *kw); static PyObject * @@ -2726,27 +2972,28 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *pos; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; PyObject *kw = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } pos = args[0]; - __clinic_args = args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - kw = args[2]; + kw = fastargs[0]; skip_optional_kwonly: - return_value = gh_32092_kw_pass_impl(module, pos, __clinic_args, kw); + return_value = gh_32092_kw_pass_impl(module, pos, varargssize, __clinic_args, kw); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2760,28 +3007,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 varargssize, + 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 varargssize = nargs; + PyObject *const *__clinic_args; 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, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2817,4 +3059,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=e8211606b03d733a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bba2892730e7f50a input=a9049054013a1b77]*/ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 8840bbabe4b584..704e301ae58987 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1984,9 +1984,10 @@ Prints the values to a stream, or to sys.stdout by default. [clinic start generated code]*/ static PyObject * -builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, - PyObject *end, PyObject *file, int flush) -/*[clinic end generated code: output=3cfc0940f5bc237b input=c143c575d24fe665]*/ +builtin_print_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *sep, PyObject *end, + PyObject *file, int flush) +/*[clinic end generated code: output=50b729b4a8731c69 input=c143c575d24fe665]*/ { int i, err; @@ -2023,7 +2024,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } - for (i = 0; i < PyTuple_GET_SIZE(args); i++) { + for (i = 0; i < varargssize; i++) { if (i > 0) { if (sep == NULL) { err = PyFile_WriteString(" ", file); @@ -2035,7 +2036,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } } - err = PyFile_WriteObject(PyTuple_GET_ITEM(args, i), file, Py_PRINT_RAW); + err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW); if (err) { return NULL; } diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index b77b4a1e4b410e..13f4f7b56f8eca 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1047,8 +1047,9 @@ PyDoc_STRVAR(builtin_print__doc__, {"print", _PyCFunction_CAST(builtin_print), METH_FASTCALL|METH_KEYWORDS, builtin_print__doc__}, static PyObject * -builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, - PyObject *end, PyObject *file, int flush); +builtin_print_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *sep, PyObject *end, + PyObject *file, int flush); static PyObject * builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1079,49 +1080,50 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[4]; + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *__clinic_args; PyObject *sep = Py_None; PyObject *end = Py_None; PyObject *file = Py_None; int flush = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - sep = args[1]; + if (fastargs[0]) { + sep = fastargs[0]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[2]) { - end = args[2]; + if (fastargs[1]) { + end = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[3]) { - file = args[3]; + if (fastargs[2]) { + file = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - flush = PyObject_IsTrue(args[4]); + flush = PyObject_IsTrue(fastargs[3]); if (flush < 0) { goto exit; } skip_optional_kwonly: - return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush); + return_value = builtin_print_impl(module, varargssize, __clinic_args, sep, end, file, flush); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -1409,4 +1411,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=84a04e7446debf58 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=637176d7f6fb764f input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 66dd90877fe6ff..5c3d589a9ff5f2 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2645,6 +2645,127 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, return NULL; } +/* Specialized version of _PyArg_UnpackKeywordsWithVararg. + * This function only returns parsed kwonly arguments array. */ +PyObject * const * +_PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + struct _PyArg_Parser *parser, + int minpos, int maxpos, int minkw, + int vararg, PyObject **buf) +{ + PyObject *kwtuple; + PyObject *keyword; + Py_ssize_t varargssize = Py_MAX(nargs - maxpos, 0); + int i, posonly, minposonly, maxargs; + int reqlimit = minkw ? maxpos + minkw : minpos; + Py_ssize_t nkwargs; + PyObject *current_arg; + PyObject * const *kwstack = NULL; + + assert(kwargs == NULL || PyDict_Check(kwargs)); + assert(kwargs == NULL || kwnames == NULL); + + if (parser == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + return NULL; + } + + if (args == NULL && nargs == 0) { + args = buf; + } + + if (!parser_init(parser)) { + return NULL; + } + + kwtuple = parser->kwtuple; + posonly = parser->pos; + minposonly = Py_MIN(posonly, minpos); + maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple); + if (kwargs != NULL) { + nkwargs = PyDict_GET_SIZE(kwargs); + } + else if (kwnames != NULL) { + nkwargs = PyTuple_GET_SIZE(kwnames); + kwstack = args + nargs; + } + else { + nkwargs = 0; + } + if (nargs < minposonly) { + PyErr_Format(PyExc_TypeError, + "%.200s%s takes %s %d positional argument%s" + " (%zd given)", + (parser->fname == NULL) ? "function" : parser->fname, + (parser->fname == NULL) ? "" : "()", + minposonly < maxpos ? "at least" : "exactly", + minposonly, + minposonly == 1 ? "" : "s", + nargs); + return NULL; + } + + /* copy keyword args using kwtuple to drive process */ + for (i = Py_MAX((int)nargs, posonly) - + Py_SAFE_DOWNCAST(varargssize, Py_ssize_t, int); i < maxargs; i++) { + if (nkwargs) { + keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); + if (kwargs != NULL) { + current_arg = PyDict_GetItemWithError(kwargs, keyword); + if (!current_arg && PyErr_Occurred()) { + return NULL; + } + } + else { + current_arg = find_keyword(kwnames, kwstack, keyword); + } + } + else { + current_arg = NULL; + } + + /* This function only cares about keyword-only argument. + * If an arguments is passed in as a keyword argument, + * we do not process it. + * + * For example: + * def f(posonly, /, kw, *varargs, kwonly1, kwonly2): + * pass + * f(1, kw=2, kwonly1=3, kwonly2=4) + * + * This `buf` array should be: [3, 4]. */ + if (i >= vararg) { + buf[i - vararg] = current_arg; + } + + if (current_arg) { + --nkwargs; + } + else if (i < minpos || (maxpos <= i && i < reqlimit)) { + /* Less arguments than required */ + keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); + PyErr_Format(PyExc_TypeError, "%.200s%s missing required " + "argument '%U' (pos %d)", + (parser->fname == NULL) ? "function" : parser->fname, + (parser->fname == NULL) ? "" : "()", + keyword, i+1); + return NULL; + } + } + + if (nkwargs > 0) { + error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname); + return NULL; + } + + return buf; +} static const char * skipitem(const char **p_format, va_list *p_va, int flags) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b8b2b75c749152..7f7b6a1ebb6725 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -722,6 +722,8 @@ def output_templates(self, f): if not p.is_optional(): min_kw_only = i - max_pos elif p.is_vararg(): + if not isinstance(p.converter, object_converter): + fail("Only object type varargs are accepted") if vararg != NO_VARARG: fail("Too many var args") pseudo_args += 1 @@ -942,6 +944,13 @@ def parser_body(prototype, *fields, declarations=''): nargs = 'PyTuple_GET_SIZE(args)' argname_fmt = 'PyTuple_GET_ITEM(args, %d)' + if vararg != NO_VARARG: + if max_pos == 0: + declarations = "Py_ssize_t varargssize = %s;" % nargs + else: + declarations = "Py_ssize_t varargssize = Py_MAX(%s - %d, 0);" % (nargs, max_pos) + else: + declarations = "" left_args = "{} - {}".format(nargs, max_pos) max_args = NO_VARARG if (vararg != NO_VARARG) else max_pos @@ -959,28 +968,16 @@ def parser_body(prototype, *fields, declarations=''): if p.is_vararg(): if not new_or_init: parser_code.append(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, - max_pos - ), indent=4)) + p.converter.parser_name, + vararg + ), indent=4)) else: + # return a borrowed reference of items in args tuple parser_code.append(normalize_snippet(""" - %s = PyTuple_GetSlice(%d, -1); - """ % ( - p.converter.parser_name, - max_pos - ), indent=4)) + %s = _PyTuple_CAST(args)->ob_item; + """ % p.converter.parser_name, indent=4)) continue parsearg = p.converter.parse_arg(argname, displayname) @@ -1015,7 +1012,7 @@ def parser_body(prototype, *fields, declarations=''): goto exit; }} """, indent=4)] - parser_definition = parser_body(parser_prototype, *parser_code) + parser_definition = parser_body(parser_prototype, *parser_code, declarations=declarations) else: has_optional_kw = (max(pos_only, min_pos) + min_kw_only < len(converters) - int(vararg != NO_VARARG)) @@ -1026,35 +1023,44 @@ def parser_body(prototype, *fields, declarations=''): min_kw_only ) nargs = "nargs" + argsbuf_size = len(converters) + varargssize = "" else: - args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( + args_declaration = "_PyArg_UnpackKeywordsWithVarargKwonly", "%s, %s, %s, %s" % ( min_pos, max_pos, min_kw_only, vararg ) nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0" + argsbuf_size = len(converters) - vararg - 1 + if max_pos == 0: + varargssize = "\nPy_ssize_t varargssize = nargs;" + else: + varargssize = "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % max_pos + declarations = declare_parser(f) + declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size if not new_or_init: flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = parser_prototype_fastcall_keywords - argname_fmt = 'args[%d]' - declarations = declare_parser(f) - declarations += "\nPyObject *argsbuf[%s];" % len(converters) + if vararg != NO_VARARG: + declarations += "\nPyObject *const *fastargs;" + parsed_argname = "fastargs" + else: + parsed_argname = "args" if has_optional_kw: declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (nargs, min_pos + min_kw_only) - parser_code = [normalize_snippet(""" - args = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf); - if (!args) {{ + parser_code = [normalize_snippet(f""" + {parsed_argname} = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf); + if (!{parsed_argname}) {{{{ goto exit; - }} + }}}} """ % args_declaration, indent=4)] + argname_fmt = f"{parsed_argname}[%d]" else: # positional-or-keyword arguments flags = "METH_VARARGS|METH_KEYWORDS" parser_prototype = parser_prototype_keyword - argname_fmt = 'fastargs[%d]' - declarations = declare_parser(f) - declarations += "\nPyObject *argsbuf[%s];" % len(converters) declarations += "\nPyObject * const *fastargs;" declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" if has_optional_kw: @@ -1065,6 +1071,8 @@ def parser_body(prototype, *fields, declarations=''): goto exit; }} """ % args_declaration, indent=4)] + argname_fmt = 'fastargs[%d]' + declarations += varargssize if requires_defining_class: flags = 'METH_METHOD|' + flags @@ -1076,7 +1084,24 @@ def parser_body(prototype, *fields, declarations=''): raise ValueError("defining_class should be the first " "parameter (after self)") displayname = p.get_displayname(i+1) - parsearg = p.converter.parse_arg(argname_fmt % i, displayname) + if vararg != NO_VARARG: + if i < int(vararg): + # positional args + if new_or_init: + parsearg = p.converter.parse_arg('PyTuple_GET_ITEM(args, %d)' % i, displayname) + else: + parsearg = p.converter.parse_arg('args[%d]' % i, displayname) + elif i > int(vararg): + # keyword args + parsearg = p.converter.parse_arg('fastargs[%d]' % (i - vararg - 1), displayname) + else: + # vararg + if new_or_init: + parsearg = p.converter.parse_arg('_PyTuple_CAST(args)->ob_item + %d' % vararg, p.converter.parser_name) + else: + parsearg = p.converter.parse_arg('args + %d' % vararg, p.converter.parser_name) + else: + parsearg = p.converter.parse_arg(argname_fmt % i, displayname) if parsearg is None: #print('Cannot convert %s %r for %s' % (p.converter.__class__.__name__, p.converter.format_unit, p.converter.name), file=sys.stderr) parser_code = None @@ -1120,7 +1145,7 @@ def parser_body(prototype, *fields, declarations=''): add_label = label parser_code.append(normalize_snippet(""" if (%s) {{ - """ % (argname_fmt % i), indent=4)) + """ % (argname_fmt % (i if vararg == NO_VARARG else i - vararg - 1)), indent=4)) parser_code.append(normalize_snippet(parsearg, indent=8)) parser_code.append(normalize_snippet(""" if (!--noptargs) {{ @@ -1174,7 +1199,11 @@ def parser_body(prototype, *fields, declarations=''): raise ValueError("Slot methods cannot access their defining class.") if not parses_keywords: - declarations = '{base_type_ptr}' + if not parser_body_declarations: + parser_body_declarations = '{base_type_ptr}' + else: + parser_body_declarations += '\n{base_type_ptr}' + fields.insert(0, normalize_snippet(""" if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{ goto exit; @@ -1188,7 +1217,7 @@ def parser_body(prototype, *fields, declarations=''): """, indent=4)) parser_definition = parser_body(parser_prototype, *fields, - declarations=declarations) + declarations=parser_body_declarations) if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): @@ -1389,9 +1418,6 @@ def render_function(self, clinic, f): if (i != -1) and (p.default is not unspecified): first_optional = min(first_optional, i) - if p.is_vararg(): - data.cleanup.append("Py_XDECREF({});".format(c.parser_name)) - # insert group variable group = p.group if last_group != group: @@ -1403,6 +1429,11 @@ def render_function(self, clinic, f): data.impl_parameters.append("int " + group_name) has_option_groups = True + if p.is_vararg(): + p.converter.type = 'PyObject *const *' + data.impl_arguments.append('varargssize') + data.impl_parameters.append('Py_ssize_t varargssize') + c.render(p, data) if has_option_groups and (not positional): @@ -4626,11 +4657,7 @@ def state_parameter(self, line): if not default: if self.parameter_state == self.ps_optional: fail("Can't have a parameter without a default (" + repr(parameter_name) + ")\nafter a parameter with a default!") - if is_vararg: - value = NULL - kwargs.setdefault('c_default', "NULL") - else: - value = unspecified + value = unspecified if 'py_default' in kwargs: fail("You can't specify py_default without specifying a default value!") else: