Skip to content

Commit d243735

Browse files
serhiy-storchakamiss-islington
authored andcommitted
gh-156219: Fix Argument Clinic for an optional argument with **kwds (GH-156220)
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>
1 parent f71fbc5 commit d243735

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
@@ -4295,6 +4295,18 @@ def test_kwds_with_pos_only(self):
42954295
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y', z='z'), (1, 2, kwds))
42964296
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, **kwds), (1, 2, kwds))
42974297

4298+
def test_kwds_with_optional_pos_only(self):
4299+
with self.assertRaises(TypeError):
4300+
ac_tester.kwds_with_optional_pos_only()
4301+
with self.assertRaises(TypeError):
4302+
ac_tester.kwds_with_optional_pos_only(y='y')
4303+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1), (1, None, {}))
4304+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, 2), (1, 2, {}))
4305+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, y='y'),
4306+
(1, None, {'y': 'y'}))
4307+
self.assertEqual(ac_tester.kwds_with_optional_pos_only(1, 2, y='y'),
4308+
(1, 2, {'y': 'y'}))
4309+
42984310
def test_kwds_with_stararg(self):
42994311
self.assertEqual(ac_tester.kwds_with_stararg(), ((), {}))
43004312
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
@@ -2358,6 +2358,23 @@ kwds_with_pos_only_impl(PyObject *module, PyObject *a, PyObject *b,
23582358
}
23592359

23602360

2361+
/*[clinic input]
2362+
kwds_with_optional_pos_only
2363+
a: object
2364+
b: object = None
2365+
/
2366+
**kwds: dict
2367+
[clinic start generated code]*/
2368+
2369+
static PyObject *
2370+
kwds_with_optional_pos_only_impl(PyObject *module, PyObject *a, PyObject *b,
2371+
PyObject *kwds)
2372+
/*[clinic end generated code: output=25a8458f5acc1a07 input=0b18b9e1670904ec]*/
2373+
{
2374+
return pack_arguments_newref(3, a, b, kwds);
2375+
}
2376+
2377+
23612378
/*[clinic input]
23622379
kwds_with_stararg
23632380
*args: tuple
@@ -2490,6 +2507,7 @@ static PyMethodDef tester_methods[] = {
24902507

24912508
LONE_KWDS_METHODDEF
24922509
KWDS_WITH_POS_ONLY_METHODDEF
2510+
KWDS_WITH_OPTIONAL_POS_ONLY_METHODDEF
24932511
KWDS_WITH_STARARG_METHODDEF
24942512
KWDS_WITH_POS_ONLY_AND_STARARG_METHODDEF
24952513

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
@@ -634,14 +634,24 @@ def parse_var_keyword(self) -> None:
634634
}}}}
635635
""", indent=4))
636636

637+
has_optional = False
637638
for i, p in enumerate(self.parameters):
638639
parse_arg = p.converter.parse_arg(
639640
f'PyTuple_GET_ITEM(args, {i})',
640641
p.get_displayname(i+1),
641642
limited_capi=self.limited_capi,
642643
)
643644
assert parse_arg is not None
645+
if has_optional or p.is_optional():
646+
has_optional = True
647+
parser_code.append(libclinic.normalize_snippet("""
648+
if (%s < %d) {{
649+
goto skip_optional;
650+
}}
651+
""", indent=4) % (nargs, i + 1))
644652
parser_code.append(libclinic.normalize_snippet(parse_arg, indent=4))
653+
if has_optional:
654+
parser_code.append("skip_optional:")
645655

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

0 commit comments

Comments
 (0)