mirror of
https://github.com/python/cpython.git
synced 2025-07-09 20:35:26 +00:00
gh-87106: Fix inspect.signature.bind() handling of positional-only arguments with **kwargs (GH-103404)
This commit is contained in:
parent
a705c1e449
commit
9c15202441
3 changed files with 39 additions and 17 deletions
|
@ -3106,6 +3106,8 @@ class Signature:
|
||||||
parameters_ex = ()
|
parameters_ex = ()
|
||||||
arg_vals = iter(args)
|
arg_vals = iter(args)
|
||||||
|
|
||||||
|
pos_only_param_in_kwargs = []
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Let's iterate through the positional arguments and corresponding
|
# Let's iterate through the positional arguments and corresponding
|
||||||
# parameters
|
# parameters
|
||||||
|
@ -3126,10 +3128,10 @@ class Signature:
|
||||||
break
|
break
|
||||||
elif param.name in kwargs:
|
elif param.name in kwargs:
|
||||||
if param.kind == _POSITIONAL_ONLY:
|
if param.kind == _POSITIONAL_ONLY:
|
||||||
msg = '{arg!r} parameter is positional only, ' \
|
# Raise a TypeError once we are sure there is no
|
||||||
'but was passed as a keyword'
|
# **kwargs param later.
|
||||||
msg = msg.format(arg=param.name)
|
pos_only_param_in_kwargs.append(param)
|
||||||
raise TypeError(msg) from None
|
continue
|
||||||
parameters_ex = (param,)
|
parameters_ex = (param,)
|
||||||
break
|
break
|
||||||
elif (param.kind == _VAR_KEYWORD or
|
elif (param.kind == _VAR_KEYWORD or
|
||||||
|
@ -3211,20 +3213,22 @@ class Signature:
|
||||||
format(arg=param_name)) from None
|
format(arg=param_name)) from None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if param.kind == _POSITIONAL_ONLY:
|
|
||||||
# This should never happen in case of a properly built
|
|
||||||
# Signature object (but let's have this check here
|
|
||||||
# to ensure correct behaviour just in case)
|
|
||||||
raise TypeError('{arg!r} parameter is positional only, '
|
|
||||||
'but was passed as a keyword'. \
|
|
||||||
format(arg=param.name))
|
|
||||||
|
|
||||||
arguments[param_name] = arg_val
|
arguments[param_name] = arg_val
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
if kwargs_param is not None:
|
if kwargs_param is not None:
|
||||||
# Process our '**kwargs'-like parameter
|
# Process our '**kwargs'-like parameter
|
||||||
arguments[kwargs_param.name] = kwargs
|
arguments[kwargs_param.name] = kwargs
|
||||||
|
elif pos_only_param_in_kwargs:
|
||||||
|
raise TypeError(
|
||||||
|
'got some positional-only arguments passed as '
|
||||||
|
'keyword arguments: {arg!r}'.format(
|
||||||
|
arg=', '.join(
|
||||||
|
param.name
|
||||||
|
for param in pos_only_param_in_kwargs
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
'got an unexpected keyword argument {arg!r}'.format(
|
'got an unexpected keyword argument {arg!r}'.format(
|
||||||
|
|
|
@ -5089,15 +5089,30 @@ class TestSignatureBind(unittest.TestCase):
|
||||||
self.assertEqual(self.call(test, 1, 2, foo=4, bar=5),
|
self.assertEqual(self.call(test, 1, 2, foo=4, bar=5),
|
||||||
(1, 2, 3, 4, 5, {}))
|
(1, 2, 3, 4, 5, {}))
|
||||||
|
|
||||||
with self.assertRaisesRegex(TypeError, "but was passed as a keyword"):
|
self.assertEqual(self.call(test, 1, 2, foo=4, bar=5, c_po=10),
|
||||||
self.call(test, 1, 2, foo=4, bar=5, c_po=10)
|
(1, 2, 3, 4, 5, {'c_po': 10}))
|
||||||
|
|
||||||
with self.assertRaisesRegex(TypeError, "parameter is positional only"):
|
self.assertEqual(self.call(test, 1, 2, 30, c_po=31, foo=4, bar=5),
|
||||||
self.call(test, 1, 2, c_po=4)
|
(1, 2, 30, 4, 5, {'c_po': 31}))
|
||||||
|
|
||||||
with self.assertRaisesRegex(TypeError, "parameter is positional only"):
|
self.assertEqual(self.call(test, 1, 2, 30, foo=4, bar=5, c_po=31),
|
||||||
|
(1, 2, 30, 4, 5, {'c_po': 31}))
|
||||||
|
|
||||||
|
self.assertEqual(self.call(test, 1, 2, c_po=4),
|
||||||
|
(1, 2, 3, 42, 50, {'c_po': 4}))
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, "missing 2 required positional arguments"):
|
||||||
self.call(test, a_po=1, b_po=2)
|
self.call(test, a_po=1, b_po=2)
|
||||||
|
|
||||||
|
def without_var_kwargs(c_po=3, d_po=4, /):
|
||||||
|
return c_po, d_po
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"positional-only arguments passed as keyword arguments: 'c_po, d_po'",
|
||||||
|
):
|
||||||
|
self.call(without_var_kwargs, c_po=33, d_po=44)
|
||||||
|
|
||||||
def test_signature_bind_with_self_arg(self):
|
def test_signature_bind_with_self_arg(self):
|
||||||
# Issue #17071: one of the parameters is named "self
|
# Issue #17071: one of the parameters is named "self
|
||||||
def test(a, self, b):
|
def test(a, self, b):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fixed handling in :meth:`inspect.signature.bind` of keyword arguments having
|
||||||
|
the same name as positional-only arguments when a variadic keyword argument
|
||||||
|
(e.g. ``**kwargs``) is present.
|
Loading…
Add table
Add a link
Reference in a new issue