Skip to content

Commit 5f60547

Browse files
miss-islingtonserhiy-storchakaerlend-aasland
authored
[3.15] gh-156219: Fix Argument Clinic for an optional argument with **kwds (GH-156220) (GH-156222)
The generated code read an optional positional argument even if it was not passed. (cherry picked from commit 4b9a1ca) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Erlend E. Aasland <erlend@python.org>
1 parent c0b9c09 commit 5f60547

5 files changed

Lines changed: 90 additions & 1 deletion

File tree

‎Lib/test/test_clinic.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,18 @@ def test_kwds_with_pos_only(self):
46154615
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y', z='z'), (1, 2, kwds))
46164616
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, **kwds), (1, 2, kwds))
46174617

4618+
def test_kwds_with_optional_pos_only(self):
4619+
with self.assertRaises(TypeError):
4620+
ac_tester.kwds_with_optional_pos_only()
4621+
with self.assertRaises(TypeError):
4622+
ac_tester.kwds_with_optional_pos_only(y='y')
4623+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1), (1, None, {}))
4624+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, 2), (1, 2, {}))
4625+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, y='y'),
4626+
(1, None, {'y': 'y'}))
4627+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, 2, y='y'),
4628+
(1, 2, {'y': 'y'}))
4629+
46184630
def test_kwds_with_stararg(self):
46194631
self.assertEqual(ac_tester.kwds_with_stararg(), ((), {}))
46204632
self.assertEqual(ac_tester.kwds_with_stararg(1, 2), ((1, 2), {}))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix Argument Clinic generating code which reads an optional positional
2+
argument which was not passed, if the function has a ``**kwds`` parameter.

‎Modules/_testclinic.c‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,6 +2404,23 @@ kwds_with_pos_only_impl(PyObject *module, PyObject *a, PyObject *b,
24042404
}
24052405

24062406

2407+
/*[clinic input]
2408+
kwds_with_optional_pos_only
2409+
a: object
2410+
b: object = None
2411+
/
2412+
**kwds: dict
2413+
[clinic start generated code]*/
2414+
2415+
static PyObject *
2416+
kwds_with_optional_pos_only_impl(PyObject *module, PyObject *a, PyObject *b,
2417+
PyObject *kwds)
2418+
/*[clinic end generated code: output=25a8458f5acc1a07 input=0b18b9e1670904ec]*/
2419+
{
2420+
return pack_arguments_newref(3, a, b, kwds);
2421+
}
2422+
2423+
24072424
/*[clinic input]
24082425
kwds_with_stararg
24092426
*args: tuple
@@ -2538,6 +2555,7 @@ static PyMethodDef tester_methods[] = {
25382555

25392556
LONE_KWDS_METHODDEF
25402557
KWDS_WITH_POS_ONLY_METHODDEF
2558+
KWDS_WITH_OPTIONAL_POS_ONLY_METHODDEF
25412559
KWDS_WITH_STARARG_METHODDEF
25422560
KWDS_WITH_POS_ONLY_AND_STARARG_METHODDEF
25432561

‎Modules/clinic/_testclinic_kwds.c.h‎

Lines changed: 48 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,14 +653,24 @@ def parse_var_keyword(self) -> None:
653653
}}}}
654654
""", indent=4))
655655

656+
has_optional = False
656657
for i, p in enumerate(self.parameters):
657658
parse_arg = p.converter.parse_arg(
658659
f'PyTuple_GET_ITEM(args, {i})',
659660
p.get_displayname(i+1),
660661
limited_capi=self.limited_capi,
661662
)
662663
assert parse_arg is not None
664+
if has_optional or p.is_optional():
665+
has_optional = True
666+
parser_code.append(libclinic.normalize_snippet("""
667+
if (%s < %d) {{
668+
goto skip_optional;
669+
}}
670+
""", indent=4) % (nargs, i + 1))
663671
parser_code.append(libclinic.normalize_snippet(parse_arg, indent=4))
672+
if has_optional:
673+
parser_code.append("skip_optional:")
664674

665675
if self.varpos:
666676
parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4))

0 commit comments

Comments
 (0)