mirror of https://github.com/python/cpython
gh-107880: Teach Argument Clinic to clone __init__ and __new__ methods (#107885)
This commit is contained in:
parent
7ddc1eaff1
commit
9b75ada6e4
|
@ -2973,6 +2973,17 @@ class ClinicFunctionalTest(unittest.TestCase):
|
|||
ac_tester.DeprStarNew(None)
|
||||
self.assertEqual(cm.filename, __file__)
|
||||
|
||||
def test_depr_star_new_cloned(self):
|
||||
regex = re.escape(
|
||||
"Passing positional arguments to _testclinic.DeprStarNew.cloned() "
|
||||
"is deprecated. Parameter 'a' will become a keyword-only parameter "
|
||||
"in Python 3.14."
|
||||
)
|
||||
obj = ac_tester.DeprStarNew(a=None)
|
||||
with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
|
||||
obj.cloned(None)
|
||||
self.assertEqual(cm.filename, __file__)
|
||||
|
||||
def test_depr_star_init(self):
|
||||
regex = re.escape(
|
||||
"Passing positional arguments to _testclinic.DeprStarInit() is "
|
||||
|
@ -2983,6 +2994,17 @@ class ClinicFunctionalTest(unittest.TestCase):
|
|||
ac_tester.DeprStarInit(None)
|
||||
self.assertEqual(cm.filename, __file__)
|
||||
|
||||
def test_depr_star_init_cloned(self):
|
||||
regex = re.escape(
|
||||
"Passing positional arguments to _testclinic.DeprStarInit.cloned() "
|
||||
"is deprecated. Parameter 'a' will become a keyword-only parameter "
|
||||
"in Python 3.14."
|
||||
)
|
||||
obj = ac_tester.DeprStarInit(a=None)
|
||||
with self.assertWarnsRegex(DeprecationWarning, regex) as cm:
|
||||
obj.cloned(None)
|
||||
self.assertEqual(cm.filename, __file__)
|
||||
|
||||
def test_depr_star_pos0_len1(self):
|
||||
fn = ac_tester.depr_star_pos0_len1
|
||||
fn(a=None)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Argument Clinic can now clone :meth:`!__init__` and :meth:`!__new__`
|
||||
methods.
|
|
@ -1230,12 +1230,29 @@ depr_star_new_impl(PyTypeObject *type, PyObject *a)
|
|||
return type->tp_alloc(type, 0);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_testclinic.DeprStarNew.cloned as depr_star_new_clone = _testclinic.DeprStarNew.__new__
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
depr_star_new_clone_impl(PyObject *type, PyObject *a)
|
||||
/*[clinic end generated code: output=3b17bf885fa736bc input=ea659285d5dbec6c]*/
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static struct PyMethodDef depr_star_new_methods[] = {
|
||||
DEPR_STAR_NEW_CLONE_METHODDEF
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject DeprStarNew = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "_testclinic.DeprStarNew",
|
||||
.tp_basicsize = sizeof(PyObject),
|
||||
.tp_new = depr_star_new,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_methods = depr_star_new_methods,
|
||||
};
|
||||
|
||||
|
||||
|
@ -1254,6 +1271,22 @@ depr_star_init_impl(PyObject *self, PyObject *a)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
_testclinic.DeprStarInit.cloned as depr_star_init_clone = _testclinic.DeprStarInit.__init__
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
depr_star_init_clone_impl(PyObject *self, PyObject *a)
|
||||
/*[clinic end generated code: output=ddfe8a1b5531e7cc input=561e103fe7f8e94f]*/
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static struct PyMethodDef depr_star_init_methods[] = {
|
||||
DEPR_STAR_INIT_CLONE_METHODDEF
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject DeprStarInit = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "_testclinic.DeprStarInit",
|
||||
|
@ -1261,6 +1294,7 @@ static PyTypeObject DeprStarInit = {
|
|||
.tp_new = PyType_GenericNew,
|
||||
.tp_init = depr_star_init,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_methods = depr_star_init_methods,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -92,6 +92,89 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(depr_star_new_clone__doc__,
|
||||
"cloned($self, /, a)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Note: Passing positional arguments to _testclinic.DeprStarNew.cloned()\n"
|
||||
"is deprecated. Parameter \'a\' will become a keyword-only parameter in\n"
|
||||
"Python 3.14.\n"
|
||||
"");
|
||||
|
||||
#define DEPR_STAR_NEW_CLONE_METHODDEF \
|
||||
{"cloned", _PyCFunction_CAST(depr_star_new_clone), METH_FASTCALL|METH_KEYWORDS, depr_star_new_clone__doc__},
|
||||
|
||||
static PyObject *
|
||||
depr_star_new_clone_impl(PyObject *type, PyObject *a);
|
||||
|
||||
static PyObject *
|
||||
depr_star_new_clone(PyObject *type, 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 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 = "cloned",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[1];
|
||||
PyObject *a;
|
||||
|
||||
// Emit compiler warnings when we get to Python 3.14.
|
||||
#if PY_VERSION_HEX >= 0x030e00C0
|
||||
# error \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarNew.cloned' to be keyword-only."
|
||||
#elif PY_VERSION_HEX >= 0x030e00A0
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ( \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarNew.cloned' to be keyword-only.")
|
||||
# else
|
||||
# warning \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarNew.cloned' to be keyword-only."
|
||||
# endif
|
||||
#endif
|
||||
if (nargs == 1) {
|
||||
if (PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Passing positional arguments to _testclinic.DeprStarNew.cloned()"
|
||||
" 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;
|
||||
}
|
||||
a = args[0];
|
||||
return_value = depr_star_new_clone_impl(type, a);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(depr_star_init__doc__,
|
||||
"DeprStarInit(a)\n"
|
||||
"--\n"
|
||||
|
@ -176,6 +259,89 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(depr_star_init_clone__doc__,
|
||||
"cloned($self, /, a)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Note: Passing positional arguments to\n"
|
||||
"_testclinic.DeprStarInit.cloned() is deprecated. Parameter \'a\' will\n"
|
||||
"become a keyword-only parameter in Python 3.14.\n"
|
||||
"");
|
||||
|
||||
#define DEPR_STAR_INIT_CLONE_METHODDEF \
|
||||
{"cloned", _PyCFunction_CAST(depr_star_init_clone), METH_FASTCALL|METH_KEYWORDS, depr_star_init_clone__doc__},
|
||||
|
||||
static PyObject *
|
||||
depr_star_init_clone_impl(PyObject *self, PyObject *a);
|
||||
|
||||
static PyObject *
|
||||
depr_star_init_clone(PyObject *self, 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 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 = "cloned",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[1];
|
||||
PyObject *a;
|
||||
|
||||
// Emit compiler warnings when we get to Python 3.14.
|
||||
#if PY_VERSION_HEX >= 0x030e00C0
|
||||
# error \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarInit.cloned' to be keyword-only."
|
||||
#elif PY_VERSION_HEX >= 0x030e00A0
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ( \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarInit.cloned' to be keyword-only.")
|
||||
# else
|
||||
# warning \
|
||||
"In _testclinic.c, update parameter(s) 'a' in the clinic input of" \
|
||||
" '_testclinic.DeprStarInit.cloned' to be keyword-only."
|
||||
# endif
|
||||
#endif
|
||||
if (nargs == 1) {
|
||||
if (PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Passing positional arguments to "
|
||||
"_testclinic.DeprStarInit.cloned() 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;
|
||||
}
|
||||
a = args[0];
|
||||
return_value = depr_star_init_clone_impl(self, a);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(depr_star_pos0_len1__doc__,
|
||||
"depr_star_pos0_len1($module, /, a)\n"
|
||||
"--\n"
|
||||
|
@ -971,4 +1137,4 @@ depr_star_pos2_len2_with_kwd(PyObject *module, PyObject *const *args, Py_ssize_t
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=18ab056f6cc06d7e input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=7a16fee4d6742d54 input=a9049054013a1b77]*/
|
||||
|
|
|
@ -4888,13 +4888,25 @@ class DSLParser:
|
|||
function_name = fields.pop()
|
||||
module, cls = self.clinic._module_and_class(fields)
|
||||
|
||||
if not (existing_function.kind is self.kind and existing_function.coexist == self.coexist):
|
||||
fail("'kind' of function and cloned function don't match! "
|
||||
"(@classmethod/@staticmethod/@coexist)")
|
||||
function = existing_function.copy(
|
||||
name=function_name, full_name=full_name, module=module,
|
||||
cls=cls, c_basename=c_basename, docstring=''
|
||||
)
|
||||
overrides: dict[str, Any] = {
|
||||
"name": function_name,
|
||||
"full_name": full_name,
|
||||
"module": module,
|
||||
"cls": cls,
|
||||
"c_basename": c_basename,
|
||||
"docstring": "",
|
||||
}
|
||||
if not (existing_function.kind is self.kind and
|
||||
existing_function.coexist == self.coexist):
|
||||
# Allow __new__ or __init__ methods.
|
||||
if existing_function.kind.new_or_init:
|
||||
overrides["kind"] = self.kind
|
||||
# Future enhancement: allow custom return converters
|
||||
overrides["return_converter"] = CReturnConverter()
|
||||
else:
|
||||
fail("'kind' of function and cloned function don't match! "
|
||||
"(@classmethod/@staticmethod/@coexist)")
|
||||
function = existing_function.copy(**overrides)
|
||||
self.function = function
|
||||
self.block.signatures.append(function)
|
||||
(cls or module).functions.append(function)
|
||||
|
|
Loading…
Reference in New Issue