[Python-checkins] gh-106368: Increase Argument Clinic test coverage (#106728)

erlend-aasland webhook-mailer at python.org
Thu Jul 13 18:18:36 EDT 2023


https://github.com/python/cpython/commit/ec45c513d389510930a62631a21a1dbb3f3aabb7
commit: ec45c513d389510930a62631a21a1dbb3f3aabb7
branch: main
author: Erlend E. Aasland <erlend at python.org>
committer: erlend-aasland <erlend.aasland at protonmail.com>
date: 2023-07-13T22:18:32Z
summary:

gh-106368: Increase Argument Clinic test coverage (#106728)

- improve output_parameter() coverage
- improve coverage for Function.kind

files:
M Lib/test/clinic.test.c
M Lib/test/test_clinic.py

diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c
index da97c4bdd7e8e..2fd8760415dc7 100644
--- a/Lib/test/clinic.test.c
+++ b/Lib/test/clinic.test.c
@@ -3,6 +3,10 @@ output preset block
 [clinic start generated code]*/
 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c81ac2402d06a8b]*/
 
+/*[clinic input]
+class Test "TestObj *" "TestType"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=fc7e50384d12b83f]*/
 
 /*[clinic input]
 test_object_converter
@@ -61,6 +65,58 @@ test_object_converter_impl(PyObject *module, PyObject *a, PyObject *b,
 /*[clinic end generated code: output=886f4f9b598726b6 input=005e6a8a711a869b]*/
 
 
+/*[clinic input]
+cloned = test_object_converter
+Check the clone feature.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(cloned__doc__,
+"cloned($module, a, b, c, d, /)\n"
+"--\n"
+"\n"
+"Check the clone feature.");
+
+#define CLONED_METHODDEF    \
+    {"cloned", _PyCFunction_CAST(cloned), METH_FASTCALL, cloned__doc__},
+
+static PyObject *
+cloned_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c,
+            PyUnicode_Object *d);
+
+static PyObject *
+cloned(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    PyObject *a;
+    PyObject *b;
+    PyObject *c;
+    PyUnicode_Object *d;
+
+    if (!_PyArg_CheckPositional("cloned", nargs, 4, 4)) {
+        goto exit;
+    }
+    a = args[0];
+    if (!PyUnicode_FSConverter(args[1], &b)) {
+        goto exit;
+    }
+    if (!PyUnicode_Check(args[2])) {
+        _PyArg_BadArgument("cloned", "argument 3", "str", args[2]);
+        goto exit;
+    }
+    c = args[2];
+    d = (PyUnicode_Object *)args[3];
+    return_value = cloned_impl(module, a, b, c, d);
+
+exit:
+    return return_value;
+}
+
+static PyObject *
+cloned_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c,
+            PyUnicode_Object *d)
+/*[clinic end generated code: output=026b483e27c38065 input=0543614019d6fcc7]*/
+
+
 /*[clinic input]
 test_object_converter_one_arg
 
@@ -4265,3 +4321,500 @@ static PyObject *
 mangle2_impl(PyObject *module, PyObject *args, PyObject *kwargs,
              PyObject *return_value)
 /*[clinic end generated code: output=2ebb62aaefe7590a input=391766fee51bad7a]*/
+
+
+/*[clinic input]
+Test.cls_with_param
+    cls: defining_class
+    /
+    a: int
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_cls_with_param__doc__,
+"cls_with_param($self, /, a)\n"
+"--\n"
+"\n");
+
+#define TEST_CLS_WITH_PARAM_METHODDEF    \
+    {"cls_with_param", _PyCFunction_CAST(Test_cls_with_param), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_with_param__doc__},
+
+static PyObject *
+Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a);
+
+static PyObject *
+Test_cls_with_param(TestObj *self, PyTypeObject *cls, 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 = "cls_with_param",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[1];
+    int a;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    a = _PyLong_AsInt(args[0]);
+    if (a == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = Test_cls_with_param_impl(self, cls, a);
+
+exit:
+    return return_value;
+}
+
+static PyObject *
+Test_cls_with_param_impl(TestObj *self, PyTypeObject *cls, int a)
+/*[clinic end generated code: output=00218e7f583e6c81 input=af158077bd237ef9]*/
+
+
+/*[clinic input]
+Test.__init__
+Empty init method.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test___init____doc__,
+"Test()\n"
+"--\n"
+"\n"
+"Empty init method.");
+
+static int
+Test___init___impl(TestObj *self);
+
+static int
+Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    int return_value = -1;
+    PyTypeObject *base_tp = TestType;
+
+    if ((Py_IS_TYPE(self, base_tp) ||
+         Py_TYPE(self)->tp_new == base_tp->tp_new) &&
+        !_PyArg_NoPositional("Test", args)) {
+        goto exit;
+    }
+    if ((Py_IS_TYPE(self, base_tp) ||
+         Py_TYPE(self)->tp_new == base_tp->tp_new) &&
+        !_PyArg_NoKeywords("Test", kwargs)) {
+        goto exit;
+    }
+    return_value = Test___init___impl((TestObj *)self);
+
+exit:
+    return return_value;
+}
+
+static int
+Test___init___impl(TestObj *self)
+/*[clinic end generated code: output=f6a35c85bc5b408f input=4ea79fee54d0c3ff]*/
+
+
+/*[clinic input]
+ at classmethod
+Test.__new__
+Empty new method.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test__doc__,
+"Test()\n"
+"--\n"
+"\n"
+"Empty new method.");
+
+static PyObject *
+Test_impl(PyTypeObject *type);
+
+static PyObject *
+Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    PyTypeObject *base_tp = TestType;
+
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoPositional("Test", args)) {
+        goto exit;
+    }
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoKeywords("Test", kwargs)) {
+        goto exit;
+    }
+    return_value = Test_impl(type);
+
+exit:
+    return return_value;
+}
+
+static PyObject *
+Test_impl(PyTypeObject *type)
+/*[clinic end generated code: output=68a117adc057940f input=6fe98a19f097907f]*/
+
+
+/*[clinic input]
+Test.cls_no_params
+    cls: defining_class
+    /
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_cls_no_params__doc__,
+"cls_no_params($self, /)\n"
+"--\n"
+"\n");
+
+#define TEST_CLS_NO_PARAMS_METHODDEF    \
+    {"cls_no_params", _PyCFunction_CAST(Test_cls_no_params), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, Test_cls_no_params__doc__},
+
+static PyObject *
+Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls);
+
+static PyObject *
+Test_cls_no_params(TestObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    if (nargs) {
+        PyErr_SetString(PyExc_TypeError, "cls_no_params() takes no arguments");
+        return NULL;
+    }
+    return Test_cls_no_params_impl(self, cls);
+}
+
+static PyObject *
+Test_cls_no_params_impl(TestObj *self, PyTypeObject *cls)
+/*[clinic end generated code: output=cc8845f22cff3dcb input=e7e2e4e344e96a11]*/
+
+
+/*[clinic input]
+Test.metho_not_default_return_converter -> int
+    a: object
+    /
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_metho_not_default_return_converter__doc__,
+"metho_not_default_return_converter($self, a, /)\n"
+"--\n"
+"\n");
+
+#define TEST_METHO_NOT_DEFAULT_RETURN_CONVERTER_METHODDEF    \
+    {"metho_not_default_return_converter", (PyCFunction)Test_metho_not_default_return_converter, METH_O, Test_metho_not_default_return_converter__doc__},
+
+static int
+Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a);
+
+static PyObject *
+Test_metho_not_default_return_converter(TestObj *self, PyObject *a)
+{
+    PyObject *return_value = NULL;
+    int _return_value;
+
+    _return_value = Test_metho_not_default_return_converter_impl(self, a);
+    if ((_return_value == -1) && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = PyLong_FromLong((long)_return_value);
+
+exit:
+    return return_value;
+}
+
+static int
+Test_metho_not_default_return_converter_impl(TestObj *self, PyObject *a)
+/*[clinic end generated code: output=3350de11bd538007 input=428657129b521177]*/
+
+
+/*[clinic input]
+Test.an_metho_arg_named_arg
+    arg: int
+        Name should be mangled to 'arg_' in generated output.
+    /
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_an_metho_arg_named_arg__doc__,
+"an_metho_arg_named_arg($self, arg, /)\n"
+"--\n"
+"\n"
+"\n"
+"\n"
+"  arg\n"
+"    Name should be mangled to \'arg_\' in generated output.");
+
+#define TEST_AN_METHO_ARG_NAMED_ARG_METHODDEF    \
+    {"an_metho_arg_named_arg", (PyCFunction)Test_an_metho_arg_named_arg, METH_O, Test_an_metho_arg_named_arg__doc__},
+
+static PyObject *
+Test_an_metho_arg_named_arg_impl(TestObj *self, int arg);
+
+static PyObject *
+Test_an_metho_arg_named_arg(TestObj *self, PyObject *arg_)
+{
+    PyObject *return_value = NULL;
+    int arg;
+
+    arg = _PyLong_AsInt(arg_);
+    if (arg == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = Test_an_metho_arg_named_arg_impl(self, arg);
+
+exit:
+    return return_value;
+}
+
+static PyObject *
+Test_an_metho_arg_named_arg_impl(TestObj *self, int arg)
+/*[clinic end generated code: output=7d590626642194ae input=2a53a57cf5624f95]*/
+
+
+/*[clinic input]
+Test.__init__
+    *args: object
+    /
+Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test___init____doc__,
+"Test(*args)\n"
+"--\n"
+"\n"
+"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE.");
+
+static int
+Test___init___impl(TestObj *self, PyObject *args);
+
+static int
+Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    int return_value = -1;
+    PyTypeObject *base_tp = TestType;
+    PyObject *__clinic_args = NULL;
+
+    if ((Py_IS_TYPE(self, base_tp) ||
+         Py_TYPE(self)->tp_new == base_tp->tp_new) &&
+        !_PyArg_NoKeywords("Test", kwargs)) {
+        goto exit;
+    }
+    if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
+        goto exit;
+    }
+    __clinic_args = PyTuple_GetSlice(0, -1);
+    return_value = Test___init___impl((TestObj *)self, __clinic_args);
+
+exit:
+    Py_XDECREF(__clinic_args);
+    return return_value;
+}
+
+static int
+Test___init___impl(TestObj *self, PyObject *args)
+/*[clinic end generated code: output=0ed1009fe0dcf98d input=96c3ddc0cd38fc0c]*/
+
+
+/*[clinic input]
+ at classmethod
+Test.__new__
+    *args: object
+    /
+Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test__doc__,
+"Test(*args)\n"
+"--\n"
+"\n"
+"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE.");
+
+static PyObject *
+Test_impl(PyTypeObject *type, PyObject *args);
+
+static PyObject *
+Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    PyTypeObject *base_tp = TestType;
+    PyObject *__clinic_args = NULL;
+
+    if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+        !_PyArg_NoKeywords("Test", kwargs)) {
+        goto exit;
+    }
+    if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
+        goto exit;
+    }
+    __clinic_args = PyTuple_GetSlice(0, -1);
+    return_value = Test_impl(type, __clinic_args);
+
+exit:
+    Py_XDECREF(__clinic_args);
+    return return_value;
+}
+
+static PyObject *
+Test_impl(PyTypeObject *type, PyObject *args)
+/*[clinic end generated code: output=8b219f6633e2a2e9 input=26a672e2e9750120]*/
+
+
+/*[clinic input]
+Test.__init__
+    a: object
+Init method with positional or keyword arguments.
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test___init____doc__,
+"Test(a)\n"
+"--\n"
+"\n"
+"Init method with positional or keyword arguments.");
+
+static int
+Test___init___impl(TestObj *self, PyObject *a);
+
+static int
+Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    int return_value = -1;
+    #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 = "Test",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[1];
+    PyObject * const *fastargs;
+    Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+    PyObject *a;
+
+    fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf);
+    if (!fastargs) {
+        goto exit;
+    }
+    a = fastargs[0];
+    return_value = Test___init___impl((TestObj *)self, a);
+
+exit:
+    return return_value;
+}
+
+static int
+Test___init___impl(TestObj *self, PyObject *a)
+/*[clinic end generated code: output=0b9ca79638ab3ecb input=a8f9222a6ab35c59]*/
+
+
+/*[clinic input]
+ at classmethod
+Test.class_method
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_class_method__doc__,
+"class_method($type, /)\n"
+"--\n"
+"\n");
+
+#define TEST_CLASS_METHOD_METHODDEF    \
+    {"class_method", (PyCFunction)Test_class_method, METH_NOARGS|METH_CLASS, Test_class_method__doc__},
+
+static PyObject *
+Test_class_method_impl(PyTypeObject *type);
+
+static PyObject *
+Test_class_method(PyTypeObject *type, PyObject *Py_UNUSED(ignored))
+{
+    return Test_class_method_impl(type);
+}
+
+static PyObject *
+Test_class_method_impl(PyTypeObject *type)
+/*[clinic end generated code: output=47fb7ecca1abcaaa input=43bc4a0494547b80]*/
+
+
+/*[clinic input]
+ at staticmethod
+Test.static_method
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_static_method__doc__,
+"static_method()\n"
+"--\n"
+"\n");
+
+#define TEST_STATIC_METHOD_METHODDEF    \
+    {"static_method", (PyCFunction)Test_static_method, METH_NOARGS|METH_STATIC, Test_static_method__doc__},
+
+static PyObject *
+Test_static_method_impl();
+
+static PyObject *
+Test_static_method(void *null, PyObject *Py_UNUSED(ignored))
+{
+    return Test_static_method_impl();
+}
+
+static PyObject *
+Test_static_method_impl()
+/*[clinic end generated code: output=82524a63025cf7ab input=dae892fac55ae72b]*/
+
+
+/*[clinic input]
+ at coexist
+Test.meth_coexist
+[clinic start generated code]*/
+
+PyDoc_STRVAR(Test_meth_coexist__doc__,
+"meth_coexist($self, /)\n"
+"--\n"
+"\n");
+
+#define TEST_METH_COEXIST_METHODDEF    \
+    {"meth_coexist", (PyCFunction)Test_meth_coexist, METH_NOARGS|METH_COEXIST, Test_meth_coexist__doc__},
+
+static PyObject *
+Test_meth_coexist_impl(TestObj *self);
+
+static PyObject *
+Test_meth_coexist(TestObj *self, PyObject *Py_UNUSED(ignored))
+{
+    return Test_meth_coexist_impl(self);
+}
+
+static PyObject *
+Test_meth_coexist_impl(TestObj *self)
+/*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 685ba58642a5a..975840333e590 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -1013,6 +1013,43 @@ def test_defining_class_param_cannot_be_optional(self):
         out = self.parse_function_should_fail(block)
         self.assertEqual(out, expected_error_msg)
 
+    def test_slot_methods_cannot_access_defining_class(self):
+        block = """
+            module foo
+            class Foo "" ""
+            Foo.__init__
+                cls: defining_class
+                a: object
+        """
+        msg = "Slot methods cannot access their defining class."
+        with self.assertRaisesRegex(ValueError, msg):
+            self.parse_function(block)
+
+    def test_new_must_be_a_class_method(self):
+        expected_error_msg = (
+            "Error on line 0:\n"
+            "__new__ must be a class method!\n"
+        )
+        out = self.parse_function_should_fail("""
+            module foo
+            class Foo "" ""
+            Foo.__new__
+        """)
+        self.assertEqual(out, expected_error_msg)
+
+    def test_init_must_be_a_normal_method(self):
+        expected_error_msg = (
+            "Error on line 0:\n"
+            "__init__ must be a normal method, not a class or static method!\n"
+        )
+        out = self.parse_function_should_fail("""
+            module foo
+            class Foo "" ""
+            @classmethod
+            Foo.__init__
+        """)
+        self.assertEqual(out, expected_error_msg)
+
     def test_unused_param(self):
         block = self.parse("""
             module foo



More information about the Python-checkins mailing list