Skip to content

Commit 3aa0e3a

Browse files
gh-155266: Fix Argument Clinic for a lone optional group
If the only parameter of a function was in an optional group, METH_O was generated, which made the argument mandatory and did not pass the flag of the group.
1 parent e12ee02 commit 3aa0e3a

6 files changed

Lines changed: 118 additions & 1 deletion

File tree

Lib/test/clinic.test.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5769,6 +5769,56 @@ Test___init___impl(TestObj *self, PyObject *a, int group_right_1,
57695769
/*[clinic end generated code: output=2bbb8ea60e8f57a6 input=10f5d0f1e8e466ef]*/
57705770

57715771

5772+
/*[clinic input]
5773+
only_optional_group
5774+
[
5775+
a: object
5776+
]
5777+
/
5778+
The only parameter is in an optional group.
5779+
[clinic start generated code]*/
5780+
5781+
PyDoc_STRVAR(only_optional_group__doc__,
5782+
"only_optional_group([a])\n"
5783+
"The only parameter is in an optional group.");
5784+
5785+
#define ONLY_OPTIONAL_GROUP_METHODDEF \
5786+
{"only_optional_group", (PyCFunction)only_optional_group, METH_VARARGS, only_optional_group__doc__},
5787+
5788+
static PyObject *
5789+
only_optional_group_impl(PyObject *module, int group_right_1, PyObject *a);
5790+
5791+
static PyObject *
5792+
only_optional_group(PyObject *module, PyObject *args)
5793+
{
5794+
PyObject *return_value = NULL;
5795+
int group_right_1 = 0;
5796+
PyObject *a = NULL;
5797+
5798+
switch (PyTuple_GET_SIZE(args)) {
5799+
case 0:
5800+
break;
5801+
case 1:
5802+
if (!PyArg_ParseTuple(args, "O:only_optional_group", &a)) {
5803+
goto exit;
5804+
}
5805+
group_right_1 = 1;
5806+
break;
5807+
default:
5808+
PyErr_SetString(PyExc_TypeError, "only_optional_group requires 0 to 1 arguments");
5809+
goto exit;
5810+
}
5811+
return_value = only_optional_group_impl(module, group_right_1, a);
5812+
5813+
exit:
5814+
return return_value;
5815+
}
5816+
5817+
static PyObject *
5818+
only_optional_group_impl(PyObject *module, int group_right_1, PyObject *a)
5819+
/*[clinic end generated code: output=e7546b9441793d7d input=426c64055af7bcab]*/
5820+
5821+
57725822
/*[clinic input]
57735823
group_and_optional_parameter
57745824
[

Lib/test/test_clinic.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4135,6 +4135,14 @@ def test_varpos_kwonly_req_opt(self):
41354135
self.assertEqual(fn(1, a=2, b=3), ((1,), 2, 3, False))
41364136
self.assertEqual(fn(1, a=2, b=3, c=4), ((1,), 2, 3, 4))
41374137

4138+
def test_only_group(self):
4139+
# fn([a])
4140+
fn = ac_tester.only_group
4141+
self.assertEqual(fn(), (False, None))
4142+
self.assertEqual(fn(1), (True, 1))
4143+
self.assertRaises(TypeError, fn, 1, 2)
4144+
self.assertRaises(TypeError, fn, a=1)
4145+
41384146
def test_group_and_opt(self):
41394147
# fn([a, b,] c=None)
41404148
fn = ac_tester.group_and_opt
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix Argument Clinic for a function whose only parameter is in an optional
2+
group.
3+
It generated ``METH_O``, which made the argument mandatory and did not pass
4+
the flag of the group.

Modules/_testclinic.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,24 @@ posonly_poskw_varpos_array_impl(PyObject *module, PyObject *a, PyObject *b,
12371237
}
12381238

12391239

1240+
/*[clinic input]
1241+
only_group
1242+
1243+
[
1244+
a: object
1245+
]
1246+
/
1247+
1248+
[clinic start generated code]*/
1249+
1250+
static PyObject *
1251+
only_group_impl(PyObject *module, int group_right_1, PyObject *a)
1252+
/*[clinic end generated code: output=e92d6c85b72a5897 input=7aca574206712a42]*/
1253+
{
1254+
return pack_arguments_newref(2, group_right_1 ? Py_True : Py_False, a);
1255+
}
1256+
1257+
12401258
/*[clinic input]
12411259
group_and_opt
12421260
@@ -2553,6 +2571,7 @@ static PyMethodDef tester_methods[] = {
25532571
POSONLY_VARPOS_ARRAY_METHODDEF
25542572
POSONLY_REQ_OPT_VARPOS_ARRAY_METHODDEF
25552573
POSONLY_POSKW_VARPOS_ARRAY_METHODDEF
2574+
ONLY_GROUP_METHODDEF
25562575
GROUP_AND_OPT_METHODDEF
25572576
GROUP_AND_TWO_OPT_METHODDEF
25582577
TWO_GROUPS_ON_LEFT_METHODDEF

Modules/clinic/_testclinic.c.h

Lines changed: 36 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/clinic/libclinic/parse_args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ def has_option_groups(self) -> bool:
303303
def use_meth_o(self) -> bool:
304304
return (len(self.parameters) == 1
305305
and self.parameters[0].is_positional_only()
306+
and not self.has_option_groups()
306307
and not self.converters[0].is_optional()
307308
and not self.varpos
308309
and not self.requires_defining_class

0 commit comments

Comments
 (0)