diff options
-rw-r--r-- | Include/internal/pycore_global_objects_fini_generated.h | 3 | ||||
-rw-r--r-- | Include/internal/pycore_global_strings.h | 3 | ||||
-rw-r--r-- | Include/internal/pycore_runtime_init_generated.h | 3 | ||||
-rw-r--r-- | Include/internal/pycore_unicodeobject_generated.h | 9 | ||||
-rw-r--r-- | Lib/test/test_clinic.py | 96 | ||||
-rw-r--r-- | Modules/_testclinic.c | 82 | ||||
-rw-r--r-- | Modules/clinic/_testclinic_depr.c.h | 363 | ||||
-rwxr-xr-x | Tools/clinic/clinic.py | 110 |
8 files changed, 565 insertions, 104 deletions
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index ee90105..2f930ba 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -923,6 +923,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exp)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extend)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extra_tokens)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(f)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(facility)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(factory)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(false)); @@ -954,6 +955,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(func)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(future)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(g)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(generation)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(genexpr)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get)); @@ -967,6 +969,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(globals)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(groupindex)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(groups)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(h)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(handle)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hash_name)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(header)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index b081c0e..5a0cd1a 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -412,6 +412,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(exp) STRUCT_FOR_ID(extend) STRUCT_FOR_ID(extra_tokens) + STRUCT_FOR_ID(f) STRUCT_FOR_ID(facility) STRUCT_FOR_ID(factory) STRUCT_FOR_ID(false) @@ -443,6 +444,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(fset) STRUCT_FOR_ID(func) STRUCT_FOR_ID(future) + STRUCT_FOR_ID(g) STRUCT_FOR_ID(generation) STRUCT_FOR_ID(genexpr) STRUCT_FOR_ID(get) @@ -456,6 +458,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(globals) STRUCT_FOR_ID(groupindex) STRUCT_FOR_ID(groups) + STRUCT_FOR_ID(h) STRUCT_FOR_ID(handle) STRUCT_FOR_ID(hash_name) STRUCT_FOR_ID(header) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 8c9c7f7..8c0fcdb 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -918,6 +918,7 @@ extern "C" { INIT_ID(exp), \ INIT_ID(extend), \ INIT_ID(extra_tokens), \ + INIT_ID(f), \ INIT_ID(facility), \ INIT_ID(factory), \ INIT_ID(false), \ @@ -949,6 +950,7 @@ extern "C" { INIT_ID(fset), \ INIT_ID(func), \ INIT_ID(future), \ + INIT_ID(g), \ INIT_ID(generation), \ INIT_ID(genexpr), \ INIT_ID(get), \ @@ -962,6 +964,7 @@ extern "C" { INIT_ID(globals), \ INIT_ID(groupindex), \ INIT_ID(groups), \ + INIT_ID(h), \ INIT_ID(handle), \ INIT_ID(hash_name), \ INIT_ID(header), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 59f4007..841eb78 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1077,6 +1077,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(extra_tokens); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(f); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(facility); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1170,6 +1173,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(future); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(g); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(generation); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1209,6 +1215,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(groups); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(h); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(handle); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 934c1e3..3106150 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1751,7 +1751,7 @@ class ClinicParserTest(TestCase): * [from 3.14] Docstring. """ - err = "Function 'bar': '* [from ...]' must come before '*'" + err = "Function 'bar': '* [from ...]' must precede '*'" self.expect_failure(block, err, lineno=4) def test_depr_star_duplicate(self): @@ -1765,7 +1765,7 @@ class ClinicParserTest(TestCase): c: int Docstring. """ - err = "Function 'bar' uses '* [from ...]' more than once." + err = "Function 'bar' uses '* [from 3.14]' more than once." self.expect_failure(block, err, lineno=5) def test_depr_star_duplicate2(self): @@ -1779,7 +1779,7 @@ class ClinicParserTest(TestCase): c: int Docstring. """ - err = "Function 'bar' uses '* [from ...]' more than once." + err = "Function 'bar': '* [from 3.15]' must precede '* [from 3.14]'" self.expect_failure(block, err, lineno=5) def test_depr_slash_duplicate(self): @@ -1793,7 +1793,7 @@ class ClinicParserTest(TestCase): c: int Docstring. """ - err = "Function 'bar' uses '/ [from ...]' more than once." + err = "Function 'bar' uses '/ [from 3.14]' more than once." self.expect_failure(block, err, lineno=5) def test_depr_slash_duplicate2(self): @@ -1801,13 +1801,13 @@ class ClinicParserTest(TestCase): module foo foo.bar a: int - / [from 3.14] - b: int / [from 3.15] + b: int + / [from 3.14] c: int Docstring. """ - err = "Function 'bar' uses '/ [from ...]' more than once." + err = "Function 'bar': '/ [from 3.14]' must precede '/ [from 3.15]'" self.expect_failure(block, err, lineno=5) def test_single_slash(self): @@ -2724,7 +2724,15 @@ class ClinicFunctionalTest(unittest.TestCase): locals().update((name, getattr(ac_tester, name)) for name in dir(ac_tester) if name.startswith('test_')) - def check_depr_star(self, pnames, fn, *args, name=None, **kwds): + def check_depr(self, regex, fn, /, *args, **kwds): + with self.assertWarnsRegex(DeprecationWarning, regex) as cm: + # Record the line number, so we're sure we've got the correct stack + # level on the deprecation warning. + _, lineno = fn(*args, **kwds), sys._getframe().f_lineno + self.assertEqual(cm.filename, __file__) + self.assertEqual(cm.lineno, lineno) + + def check_depr_star(self, pnames, fn, /, *args, name=None, **kwds): if name is None: name = fn.__qualname__ if isinstance(fn, type): @@ -2734,12 +2742,7 @@ class ClinicFunctionalTest(unittest.TestCase): fr"{re.escape(name)}\(\) is deprecated. Parameters? {pnames} will " fr"become( a)? keyword-only parameters? in Python 3\.14" ) - with self.assertWarnsRegex(DeprecationWarning, regex) as cm: - # Record the line number, so we're sure we've got the correct stack - # level on the deprecation warning. - _, lineno = fn(*args, **kwds), sys._getframe().f_lineno - self.assertEqual(cm.filename, __file__) - self.assertEqual(cm.lineno, lineno) + self.check_depr(regex, fn, *args, **kwds) def check_depr_kwd(self, pnames, fn, *args, name=None, **kwds): if name is None: @@ -2749,15 +2752,10 @@ class ClinicFunctionalTest(unittest.TestCase): pl = 's' if ' ' in pnames else '' regex = ( fr"Passing keyword argument{pl} {pnames} to " - fr"{re.escape(name)}\(\) is deprecated. Corresponding parameter{pl} " + fr"{re.escape(name)}\(\) is deprecated. Parameter{pl} {pnames} " fr"will become positional-only in Python 3\.14." ) - with self.assertWarnsRegex(DeprecationWarning, regex) as cm: - # Record the line number, so we're sure we've got the correct stack - # level on the deprecation warning. - _, lineno = fn(*args, **kwds), sys._getframe().f_lineno - self.assertEqual(cm.filename, __file__) - self.assertEqual(cm.lineno, lineno) + self.check_depr(regex, fn, *args, **kwds) def test_objects_converter(self): with self.assertRaises(TypeError): @@ -3368,6 +3366,24 @@ class ClinicFunctionalTest(unittest.TestCase): check("a", "b", c="c") self.assertRaises(TypeError, fn, "a", "b", "c", "d") + def test_depr_star_multi(self): + fn = ac_tester.depr_star_multi + self.assertRaises(TypeError, fn, "a") + fn("a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + errmsg = ( + "Passing more than 1 positional argument to depr_star_multi() is deprecated. " + "Parameter 'b' will become a keyword-only parameter in Python 3.16. " + "Parameters 'c' and 'd' will become keyword-only parameters in Python 3.15. " + "Parameters 'e', 'f' and 'g' will become keyword-only parameters in Python 3.14.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", c="c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", "c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", "c", "d", e="e", f="f", g="g", h="h") + check("a", "b", "c", "d", "e", f="f", g="g", h="h") + check("a", "b", "c", "d", "e", "f", g="g", h="h") + check("a", "b", "c", "d", "e", "f", "g", h="h") + self.assertRaises(TypeError, fn, "a", "b", "c", "d", "e", "f", "g", "h") + def test_depr_kwd_required_1(self): fn = ac_tester.depr_kwd_required_1 fn("a", "b") @@ -3452,6 +3468,44 @@ class ClinicFunctionalTest(unittest.TestCase): self.assertRaises(TypeError, fn, "a", c="c") self.assertRaises(TypeError, fn, a="a", b="b", c="c") + def test_depr_kwd_multi(self): + fn = ac_tester.depr_kwd_multi + fn("a", "b", "c", "d", "e", "f", "g", h="h") + errmsg = ( + "Passing keyword arguments 'b', 'c', 'd', 'e', 'f' and 'g' to depr_kwd_multi() is deprecated. " + "Parameter 'b' will become positional-only in Python 3.14. " + "Parameters 'c' and 'd' will become positional-only in Python 3.15. " + "Parameters 'e', 'f' and 'g' will become positional-only in Python 3.16.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", "c", "d", "e", "f", g="g", h="h") + check("a", "b", "c", "d", "e", f="f", g="g", h="h") + check("a", "b", "c", "d", e="e", f="f", g="g", h="h") + check("a", "b", "c", d="d", e="e", f="f", g="g", h="h") + check("a", "b", c="c", d="d", e="e", f="f", g="g", h="h") + check("a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g", h="h") + + def test_depr_multi(self): + fn = ac_tester.depr_multi + self.assertRaises(TypeError, fn, "a", "b", "c", "d", "e", "f", "g") + errmsg = ( + "Passing more than 4 positional arguments to depr_multi() is deprecated. " + "Parameter 'e' will become a keyword-only parameter in Python 3.15. " + "Parameter 'f' will become a keyword-only parameter in Python 3.14.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", "c", "d", "e", "f", g="g") + check("a", "b", "c", "d", "e", f="f", g="g") + fn("a", "b", "c", "d", e="e", f="f", g="g") + fn("a", "b", "c", d="d", e="e", f="f", g="g") + errmsg = ( + "Passing keyword arguments 'b' and 'c' to depr_multi() is deprecated. " + "Parameter 'b' will become positional-only in Python 3.14. " + "Parameter 'c' will become positional-only in Python 3.15.") + check = partial(self.check_depr, re.escape(errmsg), fn) + check("a", "b", c="c", d="d", e="e", f="f", g="g") + check("a", b="b", c="c", d="d", e="e", f="f", g="g") + self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g") + class PermutationTests(unittest.TestCase): """Test permutation support functions.""" diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index efec04d..2e0535d 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -1581,6 +1581,32 @@ depr_star_noinline_impl(PyObject *module, PyObject *a, PyObject *b, /*[clinic input] +depr_star_multi + a: object + * [from 3.16] + b: object + * [from 3.15] + c: object + d: object + * [from 3.14] + e: object + f: object + g: object + * + h: object +[clinic start generated code]*/ + +static PyObject * +depr_star_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g, + PyObject *h) +/*[clinic end generated code: output=77681653f4202068 input=3ebd05d888a957ea]*/ +{ + Py_RETURN_NONE; +} + + +/*[clinic input] depr_kwd_required_1 a: object / @@ -1702,6 +1728,59 @@ depr_kwd_noinline_impl(PyObject *module, PyObject *a, PyObject *b, Py_RETURN_NONE; } + +/*[clinic input] +depr_kwd_multi + a: object + / + b: object + / [from 3.14] + c: object + d: object + / [from 3.15] + e: object + f: object + g: object + / [from 3.16] + h: object +[clinic start generated code]*/ + +static PyObject * +depr_kwd_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g, + PyObject *h) +/*[clinic end generated code: output=ddfbde80fe1942e1 input=7a074e621c79efd7]*/ +{ + Py_RETURN_NONE; +} + + +/*[clinic input] +depr_multi + a: object + / + b: object + / [from 3.14] + c: object + / [from 3.15] + d: object + * [from 3.15] + e: object + * [from 3.14] + f: object + * + g: object +[clinic start generated code]*/ + +static PyObject * +depr_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g) +/*[clinic end generated code: output=f81c92852ca2d4ee input=5b847c5e44bedd02]*/ +{ + Py_RETURN_NONE; +} + + // Reset PY_VERSION_HEX #undef PY_VERSION_HEX #define PY_VERSION_HEX _SAVED_PY_VERSION @@ -1779,6 +1858,7 @@ static PyMethodDef tester_methods[] = { DEPR_STAR_POS2_LEN2_METHODDEF DEPR_STAR_POS2_LEN2_WITH_KWD_METHODDEF DEPR_STAR_NOINLINE_METHODDEF + DEPR_STAR_MULTI_METHODDEF DEPR_KWD_REQUIRED_1_METHODDEF DEPR_KWD_REQUIRED_2_METHODDEF DEPR_KWD_OPTIONAL_1_METHODDEF @@ -1786,6 +1866,8 @@ static PyMethodDef tester_methods[] = { DEPR_KWD_OPTIONAL_3_METHODDEF DEPR_KWD_REQUIRED_OPTIONAL_METHODDEF DEPR_KWD_NOINLINE_METHODDEF + DEPR_KWD_MULTI_METHODDEF + DEPR_MULTI_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_testclinic_depr.c.h b/Modules/clinic/_testclinic_depr.c.h index 661fdaf..b8365bd 100644 --- a/Modules/clinic/_testclinic_depr.c.h +++ b/Modules/clinic/_testclinic_depr.c.h @@ -419,8 +419,7 @@ PyDoc_STRVAR(depr_kwd_new__doc__, "The deprecation message should use the class name instead of __new__.\n" "\n" "Note: Passing keyword argument \'a\' to _testclinic.DeprKwdNew() is\n" -"deprecated. Corresponding parameter will become positional-only in\n" -"Python 3.14.\n" +"deprecated. Parameter \'a\' will become positional-only in Python 3.14.\n" ""); static PyObject * @@ -479,8 +478,8 @@ depr_kwd_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (kwargs && PyDict_GET_SIZE(kwargs) && nargs < 1 && fastargs[0]) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword argument 'a' to _testclinic.DeprKwdNew() is " - "deprecated. Corresponding parameter will become positional-only " - "in Python 3.14.", 1)) + "deprecated. Parameter 'a' will become positional-only in Python " + "3.14.", 1)) { goto exit; } @@ -503,8 +502,7 @@ PyDoc_STRVAR(depr_kwd_init__doc__, "The deprecation message should use the class name instead of __init__.\n" "\n" "Note: Passing keyword argument \'a\' to _testclinic.DeprKwdInit() is\n" -"deprecated. Corresponding parameter will become positional-only in\n" -"Python 3.14.\n" +"deprecated. Parameter \'a\' will become positional-only in Python 3.14.\n" ""); static int @@ -563,8 +561,8 @@ depr_kwd_init(PyObject *self, PyObject *args, PyObject *kwargs) if (kwargs && PyDict_GET_SIZE(kwargs) && nargs < 1 && fastargs[0]) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword argument 'a' to _testclinic.DeprKwdInit() is " - "deprecated. Corresponding parameter will become positional-only " - "in Python 3.14.", 1)) + "deprecated. Parameter 'a' will become positional-only in Python " + "3.14.", 1)) { goto exit; } @@ -641,8 +639,8 @@ depr_kwd_init_noinline(PyObject *self, PyObject *args, PyObject *kwargs) } if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'b' and 'c' to " - "_testclinic.DeprKwdInitNoInline() is deprecated. Corresponding " - "parameters will become positional-only in Python 3.14.", 1)) + "_testclinic.DeprKwdInitNoInline() is deprecated. Parameters 'b' " + "and 'c' will become positional-only in Python 3.14.", 1)) { goto exit; } @@ -1482,13 +1480,110 @@ exit: return return_value; } +PyDoc_STRVAR(depr_star_multi__doc__, +"depr_star_multi($module, /, a, b, c, d, e, f, g, *, h)\n" +"--\n" +"\n" +"Note: Passing more than 1 positional argument to depr_star_multi() is\n" +"deprecated. Parameter \'b\' will become a keyword-only parameter in\n" +"Python 3.16. Parameters \'c\' and \'d\' will become keyword-only\n" +"parameters in Python 3.15. Parameters \'e\', \'f\' and \'g\' will become\n" +"keyword-only parameters in Python 3.14.\n" +""); + +#define DEPR_STAR_MULTI_METHODDEF \ + {"depr_star_multi", _PyCFunction_CAST(depr_star_multi), METH_FASTCALL|METH_KEYWORDS, depr_star_multi__doc__}, + +static PyObject * +depr_star_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g, + PyObject *h); + +// Emit compiler warnings when we get to Python 3.14. +#if PY_VERSION_HEX >= 0x030e00C0 +# error "Update the clinic input of 'depr_star_multi'." +#elif PY_VERSION_HEX >= 0x030e00A0 +# ifdef _MSC_VER +# pragma message ("Update the clinic input of 'depr_star_multi'.") +# else +# warning "Update the clinic input of 'depr_star_multi'." +# endif +#endif + +static PyObject * +depr_star_multi(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 8 + 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), &_Py_ID(g), &_Py_ID(h), }, + }; + #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", "g", "h", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "depr_star_multi", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[8]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + PyObject *f; + PyObject *g; + PyObject *h; + + if (nargs > 1 && nargs <= 7) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing more than 1 positional argument to depr_star_multi() is " + "deprecated. Parameter 'b' will become a keyword-only parameter " + "in Python 3.16. Parameters 'c' and 'd' will become keyword-only " + "parameters in Python 3.15. Parameters 'e', 'f' and 'g' will " + "become keyword-only parameters in Python 3.14.", 1)) + { + goto exit; + } + } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 7, 7, 1, argsbuf); + if (!args) { + goto exit; + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + e = args[4]; + f = args[5]; + g = args[6]; + h = args[7]; + return_value = depr_star_multi_impl(module, a, b, c, d, e, f, g, h); + +exit: + return return_value; +} + PyDoc_STRVAR(depr_kwd_required_1__doc__, "depr_kwd_required_1($module, a, /, b)\n" "--\n" "\n" "Note: Passing keyword argument \'b\' to depr_kwd_required_1() is\n" -"deprecated. Corresponding parameter will become positional-only in\n" -"Python 3.14.\n" +"deprecated. Parameter \'b\' will become positional-only in Python 3.14.\n" ""); #define DEPR_KWD_REQUIRED_1_METHODDEF \ @@ -1548,8 +1643,8 @@ depr_kwd_required_1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (nargs < 2) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword argument 'b' to depr_kwd_required_1() is " - "deprecated. Corresponding parameter will become positional-only " - "in Python 3.14.", 1)) + "deprecated. Parameter 'b' will become positional-only in Python " + "3.14.", 1)) { goto exit; } @@ -1567,7 +1662,7 @@ PyDoc_STRVAR(depr_kwd_required_2__doc__, "--\n" "\n" "Note: Passing keyword arguments \'b\' and \'c\' to depr_kwd_required_2()\n" -"is deprecated. Corresponding parameters will become positional-only in\n" +"is deprecated. Parameters \'b\' and \'c\' will become positional-only in\n" "Python 3.14.\n" ""); @@ -1630,7 +1725,7 @@ depr_kwd_required_2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (nargs < 3) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'b' and 'c' to depr_kwd_required_2() " - "is deprecated. Corresponding parameters will become " + "is deprecated. Parameters 'b' and 'c' will become " "positional-only in Python 3.14.", 1)) { goto exit; @@ -1650,8 +1745,7 @@ PyDoc_STRVAR(depr_kwd_optional_1__doc__, "--\n" "\n" "Note: Passing keyword argument \'b\' to depr_kwd_optional_1() is\n" -"deprecated. Corresponding parameter will become positional-only in\n" -"Python 3.14.\n" +"deprecated. Parameter \'b\' will become positional-only in Python 3.14.\n" ""); #define DEPR_KWD_OPTIONAL_1_METHODDEF \ @@ -1712,8 +1806,8 @@ depr_kwd_optional_1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (kwnames && PyTuple_GET_SIZE(kwnames) && nargs < 2 && args[1]) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword argument 'b' to depr_kwd_optional_1() is " - "deprecated. Corresponding parameter will become positional-only " - "in Python 3.14.", 1)) + "deprecated. Parameter 'b' will become positional-only in Python " + "3.14.", 1)) { goto exit; } @@ -1735,7 +1829,7 @@ PyDoc_STRVAR(depr_kwd_optional_2__doc__, "--\n" "\n" "Note: Passing keyword arguments \'b\' and \'c\' to depr_kwd_optional_2()\n" -"is deprecated. Corresponding parameters will become positional-only in\n" +"is deprecated. Parameters \'b\' and \'c\' will become positional-only in\n" "Python 3.14.\n" ""); @@ -1799,7 +1893,7 @@ depr_kwd_optional_2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (kwnames && PyTuple_GET_SIZE(kwnames) && ((nargs < 2 && args[1]) || (nargs < 3 && args[2]))) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'b' and 'c' to depr_kwd_optional_2() " - "is deprecated. Corresponding parameters will become " + "is deprecated. Parameters 'b' and 'c' will become " "positional-only in Python 3.14.", 1)) { goto exit; @@ -1828,7 +1922,7 @@ PyDoc_STRVAR(depr_kwd_optional_3__doc__, "--\n" "\n" "Note: Passing keyword arguments \'a\', \'b\' and \'c\' to\n" -"depr_kwd_optional_3() is deprecated. Corresponding parameters will\n" +"depr_kwd_optional_3() is deprecated. Parameters \'a\', \'b\' and \'c\' will\n" "become positional-only in Python 3.14.\n" ""); @@ -1892,8 +1986,8 @@ depr_kwd_optional_3(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (kwnames && PyTuple_GET_SIZE(kwnames) && ((nargs < 1 && args[0]) || (nargs < 2 && args[1]) || (nargs < 3 && args[2]))) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'a', 'b' and 'c' to " - "depr_kwd_optional_3() is deprecated. Corresponding parameters " - "will become positional-only in Python 3.14.", 1)) + "depr_kwd_optional_3() is deprecated. Parameters 'a', 'b' and 'c'" + " will become positional-only in Python 3.14.", 1)) { goto exit; } @@ -1926,7 +2020,7 @@ PyDoc_STRVAR(depr_kwd_required_optional__doc__, "--\n" "\n" "Note: Passing keyword arguments \'b\' and \'c\' to\n" -"depr_kwd_required_optional() is deprecated. Corresponding parameters\n" +"depr_kwd_required_optional() is deprecated. Parameters \'b\' and \'c\'\n" "will become positional-only in Python 3.14.\n" ""); @@ -1990,8 +2084,8 @@ depr_kwd_required_optional(PyObject *module, PyObject *const *args, Py_ssize_t n if (kwnames && PyTuple_GET_SIZE(kwnames) && ((nargs < 2) || (nargs < 3 && args[2]))) { if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'b' and 'c' to " - "depr_kwd_required_optional() is deprecated. Corresponding " - "parameters will become positional-only in Python 3.14.", 1)) + "depr_kwd_required_optional() is deprecated. Parameters 'b' and " + "'c' will become positional-only in Python 3.14.", 1)) { goto exit; } @@ -2014,7 +2108,7 @@ PyDoc_STRVAR(depr_kwd_noinline__doc__, "--\n" "\n" "Note: Passing keyword arguments \'b\' and \'c\' to depr_kwd_noinline() is\n" -"deprecated. Corresponding parameters will become positional-only in\n" +"deprecated. Parameters \'b\' and \'c\' will become positional-only in\n" "Python 3.14.\n" ""); @@ -2081,8 +2175,8 @@ depr_kwd_noinline(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO } if (PyErr_WarnEx(PyExc_DeprecationWarning, "Passing keyword arguments 'b' and 'c' to depr_kwd_noinline() is " - "deprecated. Corresponding parameters will become positional-only" - " in Python 3.14.", 1)) + "deprecated. Parameters 'b' and 'c' will become positional-only " + "in Python 3.14.", 1)) { goto exit; } @@ -2092,4 +2186,209 @@ depr_kwd_noinline(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO exit: return return_value; } -/*[clinic end generated code: output=fc558c1efdcab076 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(depr_kwd_multi__doc__, +"depr_kwd_multi($module, a, /, b, c, d, e, f, g, h)\n" +"--\n" +"\n" +"Note: Passing keyword arguments \'b\', \'c\', \'d\', \'e\', \'f\' and \'g\' to\n" +"depr_kwd_multi() is deprecated. Parameter \'b\' will become positional-\n" +"only in Python 3.14. Parameters \'c\' and \'d\' will become positional-\n" +"only in Python 3.15. Parameters \'e\', \'f\' and \'g\' will become\n" +"positional-only in Python 3.16.\n" +""); + +#define DEPR_KWD_MULTI_METHODDEF \ + {"depr_kwd_multi", _PyCFunction_CAST(depr_kwd_multi), METH_FASTCALL|METH_KEYWORDS, depr_kwd_multi__doc__}, + +static PyObject * +depr_kwd_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g, + PyObject *h); + +// Emit compiler warnings when we get to Python 3.14. +#if PY_VERSION_HEX >= 0x030e00C0 +# error "Update the clinic input of 'depr_kwd_multi'." +#elif PY_VERSION_HEX >= 0x030e00A0 +# ifdef _MSC_VER +# pragma message ("Update the clinic input of 'depr_kwd_multi'.") +# else +# warning "Update the clinic input of 'depr_kwd_multi'." +# endif +#endif + +static PyObject * +depr_kwd_multi(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 7 + 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(b), &_Py_ID(c), &_Py_ID(d), &_Py_ID(e), &_Py_ID(f), &_Py_ID(g), &_Py_ID(h), }, + }; + #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[] = {"", "b", "c", "d", "e", "f", "g", "h", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "depr_kwd_multi", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[8]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + PyObject *f; + PyObject *g; + PyObject *h; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 8, 8, 0, argsbuf); + if (!args) { + goto exit; + } + if (nargs < 7) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing keyword arguments 'b', 'c', 'd', 'e', 'f' and 'g' to " + "depr_kwd_multi() is deprecated. Parameter 'b' will become " + "positional-only in Python 3.14. Parameters 'c' and 'd' will " + "become positional-only in Python 3.15. Parameters 'e', 'f' and " + "'g' will become positional-only in Python 3.16.", 1)) + { + goto exit; + } + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + e = args[4]; + f = args[5]; + g = args[6]; + h = args[7]; + return_value = depr_kwd_multi_impl(module, a, b, c, d, e, f, g, h); + +exit: + return return_value; +} + +PyDoc_STRVAR(depr_multi__doc__, +"depr_multi($module, a, /, b, c, d, e, f, *, g)\n" +"--\n" +"\n" +"Note: Passing keyword arguments \'b\' and \'c\' to depr_multi() is\n" +"deprecated. Parameter \'b\' will become positional-only in Python 3.14.\n" +"Parameter \'c\' will become positional-only in Python 3.15.\n" +"\n" +"\n" +"Note: Passing more than 4 positional arguments to depr_multi() is\n" +"deprecated. Parameter \'e\' will become a keyword-only parameter in\n" +"Python 3.15. Parameter \'f\' will become a keyword-only parameter in\n" +"Python 3.14.\n" +""); + +#define DEPR_MULTI_METHODDEF \ + {"depr_multi", _PyCFunction_CAST(depr_multi), METH_FASTCALL|METH_KEYWORDS, depr_multi__doc__}, + +static PyObject * +depr_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, + PyObject *d, PyObject *e, PyObject *f, PyObject *g); + +// Emit compiler warnings when we get to Python 3.14. +#if PY_VERSION_HEX >= 0x030e00C0 +# error "Update the clinic input of 'depr_multi'." +#elif PY_VERSION_HEX >= 0x030e00A0 +# ifdef _MSC_VER +# pragma message ("Update the clinic input of 'depr_multi'.") +# else +# warning "Update the clinic input of 'depr_multi'." +# endif +#endif + +static PyObject * +depr_multi(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(b), &_Py_ID(c), &_Py_ID(d), &_Py_ID(e), &_Py_ID(f), &_Py_ID(g), }, + }; + #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[] = {"", "b", "c", "d", "e", "f", "g", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "depr_multi", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[7]; + PyObject *a; + PyObject *b; + PyObject *c; + PyObject *d; + PyObject *e; + PyObject *f; + PyObject *g; + + if (nargs > 4 && nargs <= 6) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing more than 4 positional arguments to depr_multi() is " + "deprecated. Parameter 'e' will become a keyword-only parameter " + "in Python 3.15. Parameter 'f' will become a keyword-only " + "parameter in Python 3.14.", 1)) + { + goto exit; + } + } + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 6, 6, 1, argsbuf); + if (!args) { + goto exit; + } + if (nargs < 3) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Passing keyword arguments 'b' and 'c' to depr_multi() is " + "deprecated. Parameter 'b' will become positional-only in Python " + "3.14. Parameter 'c' will become positional-only in Python 3.15.", 1)) + { + goto exit; + } + } + a = args[0]; + b = args[1]; + c = args[2]; + d = args[3]; + e = args[4]; + f = args[5]; + g = args[6]; + return_value = depr_multi_impl(module, a, b, c, d, e, f, g); + +exit: + return return_value; +} +/*[clinic end generated code: output=ee8b1933e4bf4dc4 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index fe84e81..2c6fc40 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -35,6 +35,7 @@ from collections.abc import ( Iterator, Sequence, ) +from operator import attrgetter from types import FunctionType, NoneType from typing import ( TYPE_CHECKING, @@ -922,41 +923,38 @@ class CLanguage(Language): 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())) - - # 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 - major, minor = thenceforth + first_pos = next(iter(params)) + last_pos = next(reversed(params)) # Format the deprecation message. - if first_pos == 0: - preamble = "Passing positional arguments to " if len(params) == 1: condition = f"nargs == {first_pos+1}" - if first_pos: - preamble = f"Passing {first_pos+1} positional arguments to " - message = preamble + ( - f"{func.fulldisplayname}() is deprecated. Parameter {pstr} will " - f"become a keyword-only parameter in Python {major}.{minor}." - ) + amount = f"{first_pos+1} " if first_pos else "" + pl = "s" 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 " + amount = f"more than {first_pos} " if first_pos else "" + pl = "s" if first_pos != 1 else "" + message = ( + f"Passing {amount}positional argument{pl} to " + f"{func.fulldisplayname}() is deprecated." + ) + + for (major, minor), group in itertools.groupby( + params.values(), key=attrgetter("deprecated_positional") + ): + names = [repr(p.name) for p in group] + pstr = pprint_words(names) + if len(names) == 1: + message += ( + f" Parameter {pstr} will become a keyword-only parameter " + f"in Python {major}.{minor}." + ) + else: + message += ( + f" Parameters {pstr} will become keyword-only parameters " + f"in Python {major}.{minor}." ) - message = preamble + ( - f"{func.fulldisplayname}() is deprecated. Parameters {pstr} will " - f"become keyword-only parameters in Python {major}.{minor}." - ) # Append deprecation warning to docstring. docstring = textwrap.fill(f"Note: {message}") @@ -977,19 +975,8 @@ class CLanguage(Language): argname_fmt: str | None, ) -> str: assert len(params) > 0 - names = [repr(p.name) for p in params.values()] - first_param = next(iter(params.values())) last_param = next(reversed(params.values())) - # Pretty-print list of names. - pstr = pprint_words(names) - - # For now, assume there's only one deprecation level. - assert first_param.deprecated_keyword == last_param.deprecated_keyword - thenceforth = first_param.deprecated_keyword - assert thenceforth is not None - major, minor = thenceforth - # Format the deprecation message. containscheck = "" conditions = [] @@ -1013,16 +1000,25 @@ class CLanguage(Language): condition = f"kwargs && PyDict_GET_SIZE(kwargs) && {condition}" else: condition = f"kwnames && PyTuple_GET_SIZE(kwnames) && {condition}" - if len(params) == 1: - what1 = "argument" - what2 = "parameter" - else: - what1 = "arguments" - what2 = "parameters" + names = [repr(p.name) for p in params.values()] + pstr = pprint_words(names) + pl = 's' if len(params) != 1 else '' message = ( - f"Passing keyword {what1} {pstr} to {func.fulldisplayname}() is deprecated. " - f"Corresponding {what2} will become positional-only in Python {major}.{minor}." + f"Passing keyword argument{pl} {pstr} to " + f"{func.fulldisplayname}() is deprecated." ) + + for (major, minor), group in itertools.groupby( + params.values(), key=attrgetter("deprecated_keyword") + ): + names = [repr(p.name) for p in group] + pstr = pprint_words(names) + pl = 's' if len(names) != 1 else '' + message += ( + f" Parameter{pl} {pstr} will become positional-only " + f"in Python {major}.{minor}." + ) + if containscheck: errcheck = f""" if (PyErr_Occurred()) {{{{ // {containscheck}() above can fail @@ -5528,9 +5524,15 @@ class DSLParser: self.keyword_only = True else: if self.keyword_only: - fail(f"Function {function.name!r}: '* [from ...]' must come before '*'") + fail(f"Function {function.name!r}: '* [from ...]' must precede '*'") if self.deprecated_positional: - fail(f"Function {function.name!r} uses '* [from ...]' more than once.") + if self.deprecated_positional == version: + fail(f"Function {function.name!r} uses '* [from " + f"{version[0]}.{version[1]}]' more than once.") + if self.deprecated_positional < version: + fail(f"Function {function.name!r}: '* [from " + f"{version[0]}.{version[1]}]' must precede '* [from " + f"{self.deprecated_positional[0]}.{self.deprecated_positional[1]}]'") self.deprecated_positional = version def parse_opening_square_bracket(self, function: Function) -> None: @@ -5582,7 +5584,13 @@ class DSLParser: fail(f"Function {function.name!r} uses '/' more than once.") else: if self.deprecated_keyword: - fail(f"Function {function.name!r} uses '/ [from ...]' more than once.") + if self.deprecated_keyword == version: + fail(f"Function {function.name!r} uses '/ [from " + f"{version[0]}.{version[1]}]' more than once.") + if self.deprecated_keyword > version: + fail(f"Function {function.name!r}: '/ [from " + f"{version[0]}.{version[1]}]' must precede '/ [from " + f"{self.deprecated_keyword[0]}.{self.deprecated_keyword[1]}]'") if self.deprecated_positional: fail(f"Function {function.name!r}: '/ [from ...]' must precede '* [from ...]'") if self.keyword_only: @@ -5613,7 +5621,7 @@ class DSLParser: if p.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD: if version is None: p.kind = inspect.Parameter.POSITIONAL_ONLY - else: + elif p.deprecated_keyword is None: p.deprecated_keyword = version def state_parameter_docstring_start(self, line: str) -> None: |