8000 gh-122943: Rework support of var-positional parameter in Argument Clinic by serhiy-storchaka · Pull Request #122945 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-122943: Rework support of var-positional parameter in Argument Clinic #122945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add tests for the array converter.
  • Loading branch information
serhiy-storchaka committed Nov 6, 2024
commit 951fa05c420db06eac561911446aa799bbbd4bfd
125 changes: 56 additions & 69 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@
from libclinic.cli import parse_file, Clinic


def repeat_fn(*functions):
def wrapper(test):
def wrapped(self):
for fn in functions:
with self.subTest(fn=fn):
test(self, fn)
return wrapped
return wrapper

def _make_clinic(*, filename='clinic_tests', limited_capi=False):
clang = CLanguage(filename)
c = Clinic(clang, filename=filename, limited_capi=limited_capi)
Expand Down Expand Up @@ -3378,75 +3387,53 @@ def test_keyword_only_parameter(self):
ac_tester.keyword_only_parameter(1)
self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,))

def test_varpos(self):
# fn(*args)
fn = ac_tester.varpos
self.assertEqual(fn(), ())
self.assertEqual(fn(1, 2), (1, 2))

def test_varpos_no_fastcall(self):
# fn(*args), no fastcall
fn = ac_tester.TestClass.varpos_no_fastcall
self.assertEqual(fn(), ())
self.assertEqual(fn(1, 2), (1, 2))

def test_posonly_varpos(self):
# fn(a, b, /, *args)
fn = ac_tester.posonly_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1)
self.assertRaises(TypeError, fn, 1, b=2)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_varpos_no_fastcall(self):
# fn(a, b, /, *args), no fastcall
fn = ac_tester.TestClass.posonly_varpos_no_fastcall
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1)
self.assertRaises(TypeError, fn, 1, b=2)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_req_opt_varpos(self):
# fn(a, b=False, /, *args)
fn = ac_tester.posonly_req_opt_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, a=1)
self.assertEqual(fn(1), (1, False, ()))
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_req_opt_varpos_no_fastcall(self):
# fn(a, b=False, /, *args), no fastcall
fn = ac_tester.TestClass.posonly_req_opt_varpos_no_fastcall
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, a=1)
self.assertEqual(fn(1), (1, False, ()))
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_poskw_varpos(self):
# fn(a, /, b, *args)
fn = ac_tester.posonly_poskw_varpos
self.assertRaises(TypeError, fn)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, b=2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))
self.assertRaises(TypeError, fn, b=4)
errmsg = re.escape("given by name ('b') and position (2)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, b=4)

def test_posonly_poskw_varpos_no_fastcall(self):
# fn(a, /, b, *args), no fastcall
fn = ac_tester.TestClass.posonly_poskw_varpos_no_fastcall
self.assertRaises(TypeError, fn)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, b=2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))
self.assertRaises(TypeError, fn, b=4)
errmsg = re.escape("given by name ('b') and position (2)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, b=4)
if ac_tester is not None:
@repeat_fn(ac_tester.varpos,
ac_tester.varpos_array,
ac_tester.TestClass.varpos_no_fastcall,
ac_tester.TestClass.varpos_array_no_fastcall)
def test_varpos(self, fn):
# fn(*args)
self.assertEqual(fn(), ())
self.assertEqual(fn(1, 2), (1, 2))

@repeat_fn(ac_tester.posonly_varpos,
ac_tester.posonly_varpos_array,
ac_tester.TestClass.posonly_varpos_no_fastcall,
ac_tester.TestClass.posonly_varpos_array_no_fastcall)
def test_posonly_varpos(self, fn):
# fn(a, b, /, *args)
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1)
self.assertRaises(TypeError, fn, 1, b=2)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

@repeat_fn(ac_tester.posonly_req_opt_varpos,
ac_tester.posonly_req_opt_varpos_array,
ac_tester.TestClass.posonly_req_opt_varpos_no_fastcall,
ac_tester.TestClass.posonly_req_opt_varpos_array_no_fastcall)
def test_posonly_req_opt_varpos(self, fn):
# fn(a, b=False, /, *args)
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, a=1)
self.assertEqual(fn(1), (1, False, ()))
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

@repeat_fn(ac_tester.posonly_poskw_varpos,
ac_tester.posonly_poskw_varpos_array,
ac_tester.TestClass.posonly_poskw_varpos_no_fastcall,
ac_tester.TestClass.posonly_poskw_varpos_array_no_fastcall)
def test_posonly_poskw_varpos(self, fn):
# fn(a, /, b, *args)
self.assertRaises(TypeError, fn)
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, b=2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))
self.assertRaises(TypeError, fn, b=4)
errmsg = re.escape("given by name ('b') and position (2)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, b=4)

def test_poskw_varpos(self):
# fn(a, *args)
Expand Down
193 changes: 193 additions & 0 deletions Modules/_testclinic.c
8000
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ pack_arguments_newref(int argc, ...)
return tuple;
}

static PyObject *
pack_arguments_2pos_varpos(PyObject *a, PyObject *b,
PyObject * const *args, Py_ssize_t args_length)
/*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/
{
PyObject *tuple = _PyTuple_FromArray(args, args_length);
if (tuple == NULL) {
return NULL;
}
PyObject *result = pack_arguments_newref(3, a, b, tuple);
Py_DECREF(tuple);
return result;
}


/* Pack arguments to a tuple.
* `wrapper` is function which converts primitive type to PyObject.
* `arg_type` is type that arguments should be converted to before wrapped. */
Expand Down Expand Up @@ -1123,6 +1138,81 @@ varpos_kwonly_req_opt_impl(PyObject *module, PyObject *args, PyObject *a,
}


/*[clinic input]
varpos_array

*args: array

[clinic start generated code]*/

static PyObject *
varpos_array_impl(PyObject *module, PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=a25f42f39c9b13ad input=97b8bdcf87e019c7]*/
{
return _PyTuple_FromArray(args, args_length);
}


/*[clinic input]
posonly_varpos_array

a: object
b: object
/
*args: array

[clinic start generated code]*/

static PyObject *
posonly_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject * const *args, Py_ssize_t args_length)
/*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}


/*[clinic input]
posonly_req_opt_varpos_array

a: object
b: object = False
/
*args: array

[clinic start generated code]*/

static PyObject *
posonly_req_opt_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=f2f93c77ead93699 input=b01d7728164fd93e]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}


/*[clinic input]
posonly_poskw_varpos_array

a: object
/
b: object
*args: array

[clinic start generated code]*/

static PyObject *
posonly_poskw_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=155811a8b2d65a12 input=5fb08cdc6afb9d7c]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}



/*[clinic input]
gh_32092_oob
Expand Down Expand Up @@ -1352,6 +1442,7 @@ _testclinic_TestClass_defclass_posonly_varpos_impl(PyObject *self,
return pack_arguments_newref(4, cls, a, b, args);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as varpos_no_fastcall
Expand All @@ -1367,6 +1458,7 @@ varpos_no_fastcall_impl(PyTypeObject *type, PyObject *args)
return Py_NewRef(args);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as posonly_varpos_no_fastcall
Expand Down Expand Up @@ -1426,6 +1518,88 @@ posonly_poskw_varpos_no_fastcall_impl(PyTypeObject *type, PyObject *a,
return pack_arguments_newref(3, a, b, args);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as varpos_array_no_fastcall

*args: array

[clinic start generated code]*/

static PyObject *
varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=f99d984346c60d42 input=368d8eea6de48c12]*/
{
return _PyTuple_FromArray(args, args_length);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as posonly_varpos_array_no_fastcall

a: object
b: object
/
*args: array

[clinic start generated code]*/

static PyObject *
posonly_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a,
PyObject *b, PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=1eec4da1fb5b5978 input=7330c8d819a23548]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as posonly_req_opt_varpos_array_no_fastcall

a: object
b: object = False
/
*args: array

[clinic start generated code]*/

static PyObject *
posonly_req_opt_varpos_array_no_fastcall_impl(PyTypeObject *type,
PyObject *a, PyObject *b,
PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=88041c2176135218 input=7f5fd34ee5f9e0bf]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}


/*[clinic input]
@classmethod
_testclinic.TestClass.__new__ as posonly_poskw_varpos_array_no_fastcall

a: object
/
b: object
*args: array

[clinic start generated code]*/

static PyObject *
posonly_poskw_varpos_array_no_fastcall_impl(PyTypeObject *type, PyObject *a,
PyObject *b,
PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=70eda18c3667681e input=2b0fcd7bd9bb865c]*/
{
return pack_arguments_2pos_varpos(a, b, args, args_length);
}

static struct PyMethodDef test_class_methods[] = {
_TESTCLINIC_TESTCLASS_GET_DEFINING_CLASS_METHODDEF
_TESTCLINIC_TESTCLASS_GET_DEFINING_CLASS_ARG_METHODDEF
Expand All @@ -1441,6 +1615,19 @@ static struct PyMethodDef test_class_methods[] = {
{"posonly_poskw_varpos_no_fastcall", _PyCFunction_CAST(posonly_poskw_varpos_no_fastcall),
METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""},

{"varpos_array_no_fastcall",
_PyCFunction_CAST(varpos_array_no_fastcall),
METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""},
{"posonly_varpos_array_no_fastcall",
_PyCFunction_CAST(posonly_varpos_array_no_fastcall),
METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""},
{"posonly_req_opt_varpos_array_no_fastcall",
_PyCFunction_CAST(posonly_req_opt_varpos_array_no_fastcall),
METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""},
{"posonly_poskw_varpos_array_no_fastcall",
_PyCFunction_CAST(posonly_poskw_varpos_array_no_fastcall),
METH_VARARGS|METH_KEYWORDS|METH_CLASS, ""},

{NULL, NULL}
};

Expand Down Expand Up @@ -2106,6 +2293,12 @@ static PyMethodDef tester_methods[] = {
POSKW_VARPOS_KWONLY_OPT2_METHODDEF
VARPOS_KWONLY_OPT_METHODDEF
VARPOS_KWONLY_REQ_OPT_METHODDEF

VARPOS_ARRAY_METHODDEF
POSONLY_VARPOS_ARRAY_METHODDEF
POSONLY_REQ_OPT_VARPOS_ARRAY_METHODDEF
POSONLY_POSKW_VARPOS_ARRAY_METHODDEF

GH_32092_OOB_METHODDEF
GH_32092_KW_PASS_METHODDEF
GH_99233_REFCOUNT_METHODDEF
Expand Down
Loading
Loading
0