diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index c5c37b7e9e2168..c4eb8b718aee83 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -5520,6 +5520,7 @@ test_deprecate_positional_pos1_len1_optional(PyObject *module, PyObject *const * PyObject *a; PyObject *b = Py_None; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'b' in the clinic input of" \ @@ -5538,6 +5539,7 @@ test_deprecate_positional_pos1_len1_optional(PyObject *module, PyObject *const * "keyword-only." # endif #endif + if (nargs == 2) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing 2 positional arguments to " @@ -5548,6 +5550,7 @@ test_deprecate_positional_pos1_len1_optional(PyObject *module, PyObject *const * goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); if (!args) { goto exit; @@ -5567,7 +5570,7 @@ test_deprecate_positional_pos1_len1_optional(PyObject *module, PyObject *const * static PyObject * test_deprecate_positional_pos1_len1_optional_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=09a6edec1ddcd469 input=89099f3dacd757da]*/ +/*[clinic end generated code: output=43b380a976ba40fb input=89099f3dacd757da]*/ /*[clinic input] @@ -5622,6 +5625,7 @@ test_deprecate_positional_pos1_len1(PyObject *module, PyObject *const *args, Py_ PyObject *a; PyObject *b; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'b' in the clinic input of" \ @@ -5637,15 +5641,17 @@ test_deprecate_positional_pos1_len1(PyObject *module, PyObject *const *args, Py_ " 'test_deprecate_positional_pos1_len1' to be keyword-only." # endif #endif + if (nargs == 2) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing 2 positional arguments to " - "test_deprecate_positional_pos1_len1() is deprecated. Parameter " - "'b' will become a keyword-only parameter in Python 3.14.", 1)) + "test_deprecate_positional_pos1_len1() is deprecated. Parameter" + " 'b' will become a keyword-only parameter in Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); if (!args) { goto exit; @@ -5661,7 +5667,7 @@ test_deprecate_positional_pos1_len1(PyObject *module, PyObject *const *args, Py_ static PyObject * test_deprecate_positional_pos1_len1_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=52a2618293df747d input=1702bbab1e9b3b99]*/ +/*[clinic end generated code: output=e50bde8381ea717f input=1702bbab1e9b3b99]*/ /*[clinic input] @@ -5722,6 +5728,7 @@ test_deprecate_positional_pos1_len2_with_kwd(PyObject *module, PyObject *const * PyObject *c; PyObject *d; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'b' and 'c' in the clinic " \ @@ -5740,6 +5747,7 @@ test_deprecate_positional_pos1_len2_with_kwd(PyObject *module, PyObject *const * "keyword-only." # endif #endif + if (nargs > 1 && nargs <= 3) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing more than 1 positional argument to " @@ -5750,6 +5758,7 @@ test_deprecate_positional_pos1_len2_with_kwd(PyObject *module, PyObject *const * goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 1, argsbuf); if (!args) { goto exit; @@ -5768,7 +5777,7 @@ static PyObject * test_deprecate_positional_pos1_len2_with_kwd_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=550aabea548589b4 input=28cdb885f6c34eab]*/ +/*[clinic end generated code: output=bd0197ebd9af32b8 input=28cdb885f6c34eab]*/ /*[clinic input] @@ -5820,6 +5829,7 @@ test_deprecate_positional_pos0_len1(PyObject *module, PyObject *const *args, Py_ PyObject *argsbuf[1]; PyObject *a; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'a' in the clinic input of" \ @@ -5835,15 +5845,17 @@ test_deprecate_positional_pos0_len1(PyObject *module, PyObject *const *args, Py_ " 'test_deprecate_positional_pos0_len1' to be keyword-only." # endif #endif + if (nargs == 1) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing positional arguments to " - "test_deprecate_positional_pos0_len1() is deprecated. Parameter " - "'a' will become a keyword-only parameter in Python 3.14.", 1)) + "test_deprecate_positional_pos0_len1() is deprecated. Parameter" + " 'a' will become a keyword-only parameter in Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; @@ -5857,7 +5869,7 @@ test_deprecate_positional_pos0_len1(PyObject *module, PyObject *const *args, Py_ static PyObject * test_deprecate_positional_pos0_len1_impl(PyObject *module, PyObject *a) -/*[clinic end generated code: output=66c63ec8d6903bde input=678206db25c0652c]*/ +/*[clinic end generated code: output=da418d2921f7a113 input=678206db25c0652c]*/ /*[clinic input] @@ -5912,6 +5924,7 @@ test_deprecate_positional_pos0_len2(PyObject *module, PyObject *const *args, Py_ PyObject *a; PyObject *b; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'a' and 'b' in the clinic " \ @@ -5930,15 +5943,18 @@ test_deprecate_positional_pos0_len2(PyObject *module, PyObject *const *args, Py_ "keyword-only." # endif #endif + if (nargs > 0 && nargs <= 2) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing positional arguments to " - "test_deprecate_positional_pos0_len2() is deprecated. Parameters " - "'a' and 'b' will become keyword-only parameters in Python 3.14.", 1)) + "test_deprecate_positional_pos0_len2() is deprecated. " + "Parameters 'a' and 'b' will become keyword-only parameters in " + "Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); if (!args) { goto exit; @@ -5954,7 +5970,7 @@ test_deprecate_positional_pos0_len2(PyObject *module, PyObject *const *args, Py_ static PyObject * test_deprecate_positional_pos0_len2_impl(PyObject *module, PyObject *a, PyObject *b) -/*[clinic end generated code: output=6b6df40aaf751b2e input=fae0d0b1d480c939]*/ +/*[clinic end generated code: output=e0a2c3423edd800f input=fae0d0b1d480c939]*/ /*[clinic input] @@ -6018,6 +6034,7 @@ test_deprecate_positional_pos0_len3_with_kwdonly(PyObject *module, PyObject *con PyObject *c; PyObject *e; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'a', 'b' and 'c' in the " \ @@ -6039,16 +6056,18 @@ test_deprecate_positional_pos0_len3_with_kwdonly(PyObject *module, PyObject *con "keyword-only." # endif #endif + if (nargs > 0 && nargs <= 3) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing positional arguments to " "test_deprecate_positional_pos0_len3_with_kwdonly() is " - "deprecated. Parameters 'a', 'b' and 'c' will become keyword-only" - " parameters in Python 3.14.", 1)) + "deprecated. Parameters 'a', 'b' and 'c' will become " + "keyword-only parameters in Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 1, argsbuf); if (!args) { goto exit; @@ -6069,7 +6088,7 @@ test_deprecate_positional_pos0_len3_with_kwdonly_impl(PyObject *module, PyObject *b, PyObject *c, PyObject *e) -/*[clinic end generated code: output=5c936993846d01a3 input=1b0121770c0c52e0]*/ +/*[clinic end generated code: output=db1ce4dccd8c8db9 input=1b0121770c0c52e0]*/ /*[clinic input] @@ -6126,6 +6145,7 @@ test_deprecate_positional_pos2_len1(PyObject *module, PyObject *const *args, Py_ PyObject *b; PyObject *c; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'c' in the clinic input of" \ @@ -6141,15 +6161,17 @@ test_deprecate_positional_pos2_len1(PyObject *module, PyObject *const *args, Py_ " 'test_deprecate_positional_pos2_len1' to be keyword-only." # endif #endif + if (nargs == 3) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing 3 positional arguments to " - "test_deprecate_positional_pos2_len1() is deprecated. Parameter " - "'c' will become a keyword-only parameter in Python 3.14.", 1)) + "test_deprecate_positional_pos2_len1() is deprecated. Parameter" + " 'c' will become a keyword-only parameter in Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); if (!args) { goto exit; @@ -6166,7 +6188,7 @@ test_deprecate_positional_pos2_len1(PyObject *module, PyObject *const *args, Py_ static PyObject * test_deprecate_positional_pos2_len1_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c) -/*[clinic end generated code: output=2641e037296e3b61 input=e1d129689e69ec7c]*/ +/*[clinic end generated code: output=9e3c116878abd5bf input=e1d129689e69ec7c]*/ /*[clinic input] @@ -6226,6 +6248,7 @@ test_deprecate_positional_pos2_len2(PyObject *module, PyObject *const *args, Py_ PyObject *c; PyObject *d; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'c' and 'd' in the clinic " \ @@ -6244,15 +6267,18 @@ test_deprecate_positional_pos2_len2(PyObject *module, PyObject *const *args, Py_ "keyword-only." # endif #endif + if (nargs > 2 && nargs <= 4) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing more than 2 positional arguments to " - "test_deprecate_positional_pos2_len2() is deprecated. Parameters " - "'c' and 'd' will become keyword-only parameters in Python 3.14.", 1)) + "test_deprecate_positional_pos2_len2() is deprecated. " + "Parameters 'c' and 'd' will become keyword-only parameters in " + "Python 3.14.", 1)) { goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 0, argsbuf); if (!args) { goto exit; @@ -6271,7 +6297,7 @@ static PyObject * test_deprecate_positional_pos2_len2_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, PyObject *d) -/*[clinic end generated code: output=4a9068ef8fee61f6 input=0d53533463a12792]*/ +/*[clinic end generated code: output=628616560b97da6c input=0d53533463a12792]*/ /*[clinic input] @@ -6338,6 +6364,7 @@ test_deprecate_positional_pos2_len3_with_kwdonly(PyObject *module, PyObject *con PyObject *d; PyObject *e; + // Emit compiler warnings when we get to Python 3.14. #if PY_VERSION_HEX >= 0x030e00C0 # error \ "In clinic.test.c, update parameter(s) 'c' and 'd' in the clinic " \ @@ -6356,6 +6383,7 @@ test_deprecate_positional_pos2_len3_with_kwdonly(PyObject *module, PyObject *con "be keyword-only." # endif #endif + if (nargs > 2 && nargs <= 4) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing more than 2 positional arguments to " @@ -6366,6 +6394,7 @@ test_deprecate_positional_pos2_len3_with_kwdonly(PyObject *module, PyObject *con goto exit; } } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 1, argsbuf); if (!args) { goto exit; @@ -6388,4 +6417,422 @@ test_deprecate_positional_pos2_len3_with_kwdonly_impl(PyObject *module, PyObject *c, PyObject *d, PyObject *e) -/*[clinic end generated code: output=1154c2e3e798948c input=154fd450448d8935]*/ +/*[clinic end generated code: output=0c179f87e07f6664 input=154fd450448d8935]*/ + + +/*[clinic input] +test_deprecate_positional_multiple_1 + a: object + * [from 3.14] + b: object + c: object + * [from 3.15] + d: object + * [from 3.16] + e: object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_deprecate_positional_multiple_1__doc__, +"test_deprecate_positional_multiple_1($module, /, a, b, c, d, e)\n" +"--\n" +"\n"); + +#define TEST_DEPRECATE_POSITIONAL_MULTIPLE_1_METHODDEF \ + {"test_deprecate_positional_multiple_1", _PyCFunction_CAST(test_deprecate_positional_multiple_1), METH_FASTCALL|METH_KEYWORDS, test_deprecate_positional_multiple_1__doc__}, + +static PyObject * +test_deprecate_positional_multiple_1_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e); + +static PyObject * +test_deprecate_positional_multiple_1(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 5 + 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), &_Py_ID(c), &_Py_ID(d), &_Py_ID(e), }, + }; + #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", "c", "d", "e", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_deprecate_positional_multiple_1", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + + // Emit compiler warnings when we get to Python 3.16. + #if PY_VERSION_HEX >= 0x031000C0 + # error \ + "In clinic.test.c, update parameter(s) 'e' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only." + #elif PY_VERSION_HEX >= 0x031000A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'e' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'e' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only." + # endif + #endif + + // Emit compiler warnings when we get to Python 3.15. + #if PY_VERSION_HEX >= 0x030f00C0 + # error \ + "In clinic.test.c, update parameter(s) 'd' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only." + #elif PY_VERSION_HEX >= 0x030f00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'd' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'd' in the clinic input of" \ + " 'test_deprecate_positional_multiple_1' to be keyword-only." + # endif + #endif + + // Emit compiler warnings when we get to Python 3.14. + #if PY_VERSION_HEX >= 0x030e00C0 + # error \ + "In clinic.test.c, update parameter(s) 'b' and 'c' in the clinic " \ + "input of 'test_deprecate_positional_multiple_1' to be " \ + "keyword-only." + #elif PY_VERSION_HEX >= 0x030e00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'b' and 'c' in the clinic " \ + "input of 'test_deprecate_positional_multiple_1' to be " \ + "keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'b' and 'c' in the clinic " \ + "input of 'test_deprecate_positional_multiple_1' to be " \ + "keyword-only." + # endif + #endif + + if (nargs > 1 && nargs <= 5) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing more than 1 positional argument to " + "test_deprecate_positional_multiple_1() is deprecated. " + "Parameters 'b' and 'c' will become keyword-only parameters in " + "Python 3.14. Parameter 'd' will become a keyword-only " + "parameter in Python 3.15. Parameter 'e' will become a " + "keyword-only parameter in Python 3.16.", 1)) + { + goto exit; + } + } + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 5, 5, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + e = args[4]; + return_value = test_deprecate_positional_multiple_1_impl(module, a, b, c, d, e); + +exit: + return return_value; +} + +static PyObject * +test_deprecate_positional_multiple_1_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e) +/*[clinic end generated code: output=28dd7843f99a8d69 input=440c2f88701ea4c1]*/ + + +/*[clinic input] +test_deprecate_positional_multiple_2 + a: object + b: object + * [from 3.14] + c: object + * [from 3.15] + d: object + e: object + * + f: object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_deprecate_positional_multiple_2__doc__, +"test_deprecate_positional_multiple_2($module, /, a, b, c, d, e, *, f)\n" +"--\n" +"\n"); + +#define TEST_DEPRECATE_POSITIONAL_MULTIPLE_2_METHODDEF \ + {"test_deprecate_positional_multiple_2", _PyCFunction_CAST(test_deprecate_positional_multiple_2), METH_FASTCALL|METH_KEYWORDS, test_deprecate_positional_multiple_2__doc__}, + +static PyObject * +test_deprecate_positional_multiple_2_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e, + PyObject *f); + +static PyObject * +test_deprecate_positional_multiple_2(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 6 + 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), &_Py_ID(c), &_Py_ID(d), &_Py_ID(e), &_Py_ID(f), }, + }; + #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", "c", "d", "e", "f", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_deprecate_positional_multiple_2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + PyObject *f; + + // Emit compiler warnings when we get to Python 3.15. + #if PY_VERSION_HEX >= 0x030f00C0 + # error \ + "In clinic.test.c, update parameter(s) 'd' and 'e' in the clinic " \ + "input of 'test_deprecate_positional_multiple_2' to be " \ + "keyword-only." + #elif PY_VERSION_HEX >= 0x030f00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'd' and 'e' in the clinic " \ + "input of 'test_deprecate_positional_multiple_2' to be " \ + "keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'd' and 'e' in the clinic " \ + "input of 'test_deprecate_positional_multiple_2' to be " \ + "keyword-only." + # endif + #endif + + // Emit compiler warnings when we get to Python 3.14. + #if PY_VERSION_HEX >= 0x030e00C0 + # error \ + "In clinic.test.c, update parameter(s) 'c' in the clinic input of" \ + " 'test_deprecate_positional_multiple_2' to be keyword-only." + #elif PY_VERSION_HEX >= 0x030e00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'c' in the clinic input of" \ + " 'test_deprecate_positional_multiple_2' to be keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'c' in the clinic input of" \ + " 'test_deprecate_positional_multiple_2' to be keyword-only." + # endif + #endif + + if (nargs > 2 && nargs <= 5) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing more than 2 positional arguments to " + "test_deprecate_positional_multiple_2() is deprecated. " + "Parameter 'c' will become a keyword-only parameter in Python " + "3.14. Parameters 'd' and 'e' will become keyword-only " + "parameters in Python 3.15.", 1)) + { + goto exit; + } + } + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 5, 5, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + e = args[4]; + f = args[5]; + return_value = test_deprecate_positional_multiple_2_impl(module, a, b, c, d, e, f); + +exit: + return return_value; +} + +static PyObject * +test_deprecate_positional_multiple_2_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d, PyObject *e, + PyObject *f) +/*[clinic end generated code: output=655eb68fad97479c input=3d4ed8c65d3e1eb8]*/ + + +/*[clinic input] +test_deprecate_positional_multiple_3 + * [from 3.14] + a: object + b: object + * [from 3.15] + c: object + d: object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_deprecate_positional_multiple_3__doc__, +"test_deprecate_positional_multiple_3($module, /, a, b, c, d)\n" +"--\n" +"\n"); + +#define TEST_DEPRECATE_POSITIONAL_MULTIPLE_3_METHODDEF \ + {"test_deprecate_positional_multiple_3", _PyCFunction_CAST(test_deprecate_positional_multiple_3), METH_FASTCALL|METH_KEYWORDS, test_deprecate_positional_multiple_3__doc__}, + +static PyObject * +test_deprecate_positional_multiple_3_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d); + +static PyObject * +test_deprecate_positional_multiple_3(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 4 + 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), &_Py_ID(c), &_Py_ID(d), }, + }; + #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", "c", "d", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_deprecate_positional_multiple_3", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + + // Emit compiler warnings when we get to Python 3.15. + #if PY_VERSION_HEX >= 0x030f00C0 + # error \ + "In clinic.test.c, update parameter(s) 'c' and 'd' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only." + #elif PY_VERSION_HEX >= 0x030f00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'c' and 'd' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'c' and 'd' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only." + # endif + #endif + + // Emit compiler warnings when we get to Python 3.14. + #if PY_VERSION_HEX >= 0x030e00C0 + # error \ + "In clinic.test.c, update parameter(s) 'a' and 'b' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only." + #elif PY_VERSION_HEX >= 0x030e00A0 + # ifdef _MSC_VER + # pragma message ( \ + "In clinic.test.c, update parameter(s) 'a' and 'b' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only.") + # else + # warning \ + "In clinic.test.c, update parameter(s) 'a' and 'b' in the clinic " \ + "input of 'test_deprecate_positional_multiple_3' to be " \ + "keyword-only." + # endif + #endif + + if (nargs > 0 && nargs <= 4) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing positional arguments to " + "test_deprecate_positional_multiple_3() is deprecated. " + "Parameters 'a' and 'b' will become keyword-only parameters in " + "Python 3.14. Parameters 'c' and 'd' will become keyword-only " + "parameters in Python 3.15.", 1)) + { + goto exit; + } + } + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 4, 4, 0, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + return_value = test_deprecate_positional_multiple_3_impl(module, a, b, c, d); + +exit: + return return_value; +} + +static PyObject * +test_deprecate_positional_multiple_3_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *c, + PyObject *d) +/*[clinic end generated code: output=ce5d1b049d7b251c input=9683b00748da8983]*/ diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d13d8623f8093b..21b6ffbcd521ff 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1421,7 +1421,7 @@ def test_parameters_required_after_depr_star(self): "module foo\nfoo.bar\n this: int\n * [from 3.14]", "module foo\nfoo.bar\n this: int\n * [from 3.14]\nDocstring.", ) - err = "Function 'foo.bar' specifies '* [from 3.14]' without any parameters afterwards." + err = "Function 'foo.bar' specifies '* [from ...]' without any parameters afterwards." for block in dataset: with self.subTest(block=block): self.expect_failure(block, err) @@ -1468,46 +1468,81 @@ def test_depr_star_invalid_format_3(self): ) self.expect_failure(block, err, lineno=3) - def test_parameters_required_after_depr_star(self): + def test_depr_star_must_come_before_star(self): block = """ module foo foo.bar this: int + * * [from 3.14] Docstring. """ - err = ( - "Function 'foo.bar' specifies '* [from ...]' without " - "any parameters afterwards" - ) + err = "Function 'foo.bar': '* [from 3.14]' must come before '*'" self.expect_failure(block, err, lineno=4) - def test_depr_star_must_come_before_star(self): + def test_depr_star_duplicate(self): block = """ module foo foo.bar - this: int - * + a: int * [from 3.14] + b: int + * [from 3.14] + c: int Docstring. """ - err = "Function 'foo.bar': '* [from ...]' must come before '*'" - self.expect_failure(block, err, lineno=4) + err = "Function 'foo.bar' uses '[from 3.14]' more than once" + self.expect_failure(block, err, lineno=5) - def test_depr_star_duplicate(self): + def test_depr_star_multiple_order(self): block = """ module foo foo.bar a: int * [from 3.14] b: int - * [from 3.14] + * [from 3.13] c: int Docstring. """ - err = "Function 'foo.bar' uses '[from ...]' more than once" + err = ( + "Function 'foo.bar': '[from 3.13]' must specify " + "a newer version than 3.14" + ) self.expect_failure(block, err, lineno=5) + def test_depr_star_multiple_duplicate_1(self): + block = """ + module foo + foo.bar + a: int + * [from 3.13] + b: int + * [from 3.14] + c: int + * [from 3.14] + d: int + Docstring. + """ + err = "Function 'foo.bar' uses '[from 3.14]' more than once" + self.expect_failure(block, err, lineno=7) + + def test_depr_star_multiple_duplicate_2(self): + block = """ + module foo + foo.bar + a: int + * [from 3.13] + b: int + * [from 3.14] + c: int + * [from 3.13] + d: int + Docstring. + """ + err = "Function 'foo.bar' uses '[from 3.13]' more than once" + self.expect_failure(block, err, lineno=7) + def test_single_slash(self): block = """ module foo diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 3a8431cb67efea..7626aa032eed69 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -227,7 +227,7 @@ def wrapped_c_string_literal( width: int = 72, suffix: str = '', initial_indent: int = 0, - subsequent_indent: int = 4 + subsequent_indent: int = 0 ) -> str: wrapped = textwrap.wrap(text, width=width, replace_whitespace=False, drop_whitespace=False, break_on_hyphens=False) @@ -361,7 +361,7 @@ def suffix_all_lines(s: str, suffix: str) -> str: return ''.join(final) -def pprint_words(items: list[str]) -> str: +def pprint_words(items: Sequence[str]) -> str: if len(items) <= 2: return " and ".join(items) else: @@ -849,7 +849,8 @@ class CLanguage(Language): #define {methoddef_name} #endif /* !defined({methoddef_name}) */ """) - DEPRECATED_POSITIONAL_PROTOTYPE: Final[str] = r""" + DEPRECATED_POSITIONAL_PROTOTYPE_CPP: Final[str] = normalize_snippet(r""" + // Emit compiler warnings when we get to Python {major}.{minor}. #if PY_VERSION_HEX >= 0x{major:02x}{minor:02x}00C0 # error \ {cpp_message} @@ -862,6 +863,8 @@ class CLanguage(Language): {cpp_message} # endif #endif + """, indent=4) + DEPRECATED_POSITIONAL_PROTOTYPE_DEPR: Final[str] = normalize_snippet(r""" if ({condition}) {{{{ if (PyErr_WarnEx(PyExc_DeprecationWarning, {depr_message}, 1)) @@ -869,7 +872,7 @@ class CLanguage(Language): goto exit; }}}} }}}} - """ + """, indent=4) def __init__(self, filename: str) -> None: super().__init__(filename) @@ -892,67 +895,112 @@ def render( function = o return self.render_function(clinic, function) - def deprecate_positional_use( + def deprecate_positional_use_for_version( self, - func: Function, - params: dict[int, Parameter], - ) -> str: - assert len(params) > 0 - names = [repr(p.name) for p in params.values()] - first_pos, first_param = next(iter(params.items())) - last_pos, last_param = next(reversed(params.items())) + fname: str, + positions: Sequence[int], + pnames: Sequence[str], + version: VersionTuple + ) -> tuple[str, str]: + first_pos, last_pos = positions[0], positions[-1] # Pretty-print list of names. - pstr = pprint_words(names) - - # For now, assume there's only one deprecation level. - assert first_param.deprecated_positional == last_param.deprecated_positional - thenceforth = first_param.deprecated_positional - assert thenceforth is not None + pstr = pprint_words(pnames) # Format the preprocessor warning and error messages. assert isinstance(self.cpp.filename, str) source = os.path.basename(self.cpp.filename) - major, minor = thenceforth + major, minor = version cpp_message = ( f"In {source}, update parameter(s) {pstr} in the clinic " - f"input of {func.full_name!r} to be keyword-only." + f"input of {fname!r} to be keyword-only." ) # Format the deprecation message. + if len(pnames) == 1: + depr_message = ( + f"Parameter {pstr} will become a keyword-only parameter " + f"in Python {major}.{minor}." + ) + else: + depr_message = ( + f"Parameters {pstr} will become keyword-only parameters " + f"in Python {major}.{minor}." + ) + + # Format and return the code block. + template = self.DEPRECATED_POSITIONAL_PROTOTYPE_CPP + cpp_message = wrapped_c_string_literal(cpp_message, suffix=" \\", + width=64, subsequent_indent=12) + code = template.format( + major=major, + minor=minor, + cpp_message=cpp_message + ) + return code, depr_message + + def deprecate_positional_use( + self, + func: Function, + params: dict[int, Parameter], + ) -> str: + assert len(params) > 0 + code_blocks: list[str] = [] + _params = params.values() + versions = [p.deprecated_positional for p in _params] + positions = [p for p in params] + names = [repr(p.name) for p in _params] + + first_pos, last_pos = positions[0], positions[-1] + + # Format the deprecation message preamble. + fname = func.full_name if first_pos == 0: - preamble = "Passing positional arguments to " - if len(params) == 1: + preamble = (f"Passing positional arguments to {fname}() " + "is deprecated.") + if len(names) == 1: condition = f"nargs == {first_pos+1}" if first_pos: - preamble = f"Passing {first_pos+1} positional arguments to " - depr_message = preamble + ( - f"{func.full_name}() is deprecated. Parameter {pstr} will " - f"become a keyword-only parameter in Python {major}.{minor}." - ) + preamble = ( + f"Passing {first_pos+1} positional arguments to " + f"{fname}() is deprecated." + ) else: condition = f"nargs > {first_pos} && nargs <= {last_pos+1}" if first_pos: preamble = ( f"Passing more than {first_pos} positional " f"argument{'s' if first_pos != 1 else ''} to " + f"{fname}() is deprecated." ) - depr_message = preamble + ( - f"{func.full_name}() is deprecated. Parameters {pstr} will " - f"become keyword-only parameters in Python {major}.{minor}." - ) - # Format and return the code block. - code = self.DEPRECATED_POSITIONAL_PROTOTYPE.format( + + # Create code blocks and deprecation warning message segments. + depr: list[str] = [preamble] + cpp_blocks: list[str] = [] + grouped = itertools.groupby(zip(params, names, versions), + key=lambda x: x[2]) + for version, items in grouped: + assert version is not None + subpositions, subnames, _ = zip(*items) + code = self.deprecate_positional_use_for_version( + fname, subpositions, subnames, version) + cpp_block, depr_msg = code + depr.append(depr_msg) + cpp_blocks.append(cpp_block) + + # Arrange preprocessor blocks. + cpp_code = "\n\n".join(reversed(cpp_blocks)) + + # Consolidate and format deprecation message. + depr_message = " ".join(depr) + depr_message = wrapped_c_string_literal(depr_message, width=62, + subsequent_indent=16) + template = self.DEPRECATED_POSITIONAL_PROTOTYPE_DEPR + depr_code = template.format( condition=condition, - major=major, - minor=minor, - cpp_message=wrapped_c_string_literal(cpp_message, suffix=" \\", - width=64, - subsequent_indent=16), - depr_message=wrapped_c_string_literal(depr_message, width=64, - subsequent_indent=20), + depr_message=depr_message ) - return normalize_snippet(code, indent=4) + return f"{cpp_code}\n\n{depr_code}\n" def docstring_for_c_string( self, @@ -4553,7 +4601,7 @@ class DSLParser: state: StateKeeper keyword_only: bool positional_only: bool - deprecated_positional: VersionTuple | None + deprecated_positionals: list[VersionTuple] = [] group: int parameter_state: ParamState indent: IndentStack @@ -4589,7 +4637,7 @@ def reset(self) -> None: self.state = self.state_dsl_start self.keyword_only = False self.positional_only = False - self.deprecated_positional = None + self.deprecated_positionals = [] self.group = 0 self.parameter_state: ParamState = ParamState.START self.indent = IndentStack() @@ -5318,9 +5366,14 @@ def bad_node(self, node: ast.AST) -> None: "after 'self'.") + # If there are deprecated positionals, fetch the last one. + if self.deprecated_positionals: + deprecated_positional = self.deprecated_positionals[-1] + else: + deprecated_positional = None p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group, - deprecated_positional=self.deprecated_positional) + deprecated_positional=deprecated_positional) names = [k.name for k in self.function.parameters.values()] if parameter_name in names[1:]: @@ -5357,24 +5410,33 @@ def parse_deprecated_positional(self, thenceforth: str) -> None: assert isinstance(self.function, Function) fname = self.function.full_name + deprecated_so_far = self.deprecated_positionals[:] if self.keyword_only: - fail(f"Function {fname!r}: '* [from ...]' must come before '*'") - if self.deprecated_positional: - fail(f"Function {fname!r} uses '[from ...]' more than once.") + fail(f"Function {fname!r}: '* [from {thenceforth}]' " + "must come before '*'") try: - major, minor = thenceforth.split(".") - self.deprecated_positional = int(major), int(minor) + major, minor = map(int, thenceforth.split(".")) + self.deprecated_positionals.append((major, minor)) except ValueError: fail( f"Function {fname!r}: expected format '* [from major.minor]' " f"where 'major' and 'minor' are integers; got {thenceforth!r}" ) + if deprecated_so_far: + if (major, minor) in deprecated_so_far: + fail(f"Function {fname!r} uses '[from {thenceforth}]' " + "more than once.") + current = deprecated_so_far[-1] + if (major, minor) < current: + curstr = ".".join([str(digit) for digit in current]) + fail(f"Function {fname!r}: '[from {thenceforth}]' " + f"must specify a newer version than {curstr}.") def parse_star(self, function: Function) -> None: """Parse keyword-only parameter marker '*'.""" if self.keyword_only: fail(f"Function {function.name!r} uses '*' more than once.") - self.deprecated_positional = None + self.deprecated_positionals = [] self.keyword_only = True def parse_opening_square_bracket(self, function: Function) -> None: @@ -5768,7 +5830,7 @@ def check_remaining( if self.keyword_only: check_remaining("*", lambda p: p.kind != inspect.Parameter.KEYWORD_ONLY) - if self.deprecated_positional: + if self.deprecated_positionals: check_remaining("* [from ...]", lambda p: not p.deprecated_positional) self.function.docstring = self.format_docstring()