[3.11] gh-103556: [inspect.Signature] disallow pos-or-kw params without default after pos-only with default (GH-103557) (#103675)

This commit is contained in:
Miss Islington (bot) 2023-04-22 08:13:02 -07:00 committed by GitHub
parent 2b5dbd1f23
commit b2fdae9d86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 12 deletions

View file

@ -2978,7 +2978,7 @@ def __init__(self, parameters=None, *, return_annotation=_empty,
if __validate_parameters__:
params = OrderedDict()
top_kind = _POSITIONAL_ONLY
kind_defaults = False
seen_default = False
for param in parameters:
kind = param.kind
@ -2993,21 +2993,19 @@ def __init__(self, parameters=None, *, return_annotation=_empty,
kind.description)
raise ValueError(msg)
elif kind > top_kind:
kind_defaults = False
top_kind = kind
if kind in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD):
if param.default is _empty:
if kind_defaults:
if seen_default:
# No default for this parameter, but the
# previous parameter of the same kind had
# a default
# previous parameter of had a default
msg = 'non-default argument follows default ' \
'argument'
raise ValueError(msg)
else:
# There is a default for this parameter.
kind_defaults = True
seen_default = True
if name in params:
msg = 'duplicate parameter name: {!r}'.format(name)

View file

@ -2301,18 +2301,43 @@ def test_signature_object(self):
self.assertEqual(str(S()), '()')
self.assertEqual(repr(S().parameters), 'mappingproxy(OrderedDict())')
def test(po, pk, pod=42, pkd=100, *args, ko, **kwargs):
def test(po, /, pk, pkd=100, *args, ko, kod=10, **kwargs):
pass
sig = inspect.signature(test)
po = sig.parameters['po'].replace(kind=P.POSITIONAL_ONLY)
pod = sig.parameters['pod'].replace(kind=P.POSITIONAL_ONLY)
self.assertTrue(repr(sig).startswith('<Signature'))
self.assertTrue('(po, /, pk' in repr(sig))
# We need two functions, because it is impossible to represent
# all param kinds in a single one.
def test2(pod=42, /):
pass
sig2 = inspect.signature(test2)
self.assertTrue(repr(sig2).startswith('<Signature'))
self.assertTrue('(pod=42, /)' in repr(sig2))
po = sig.parameters['po']
pod = sig2.parameters['pod']
pk = sig.parameters['pk']
pkd = sig.parameters['pkd']
args = sig.parameters['args']
ko = sig.parameters['ko']
kod = sig.parameters['kod']
kwargs = sig.parameters['kwargs']
S((po, pk, args, ko, kwargs))
S((po, pk, ko, kod))
S((po, pod, ko))
S((po, pod, kod))
S((pod, ko, kod))
S((pod, kod))
S((pod, args, kod, kwargs))
# keyword-only parameters without default values
# can follow keyword-only parameters with default values:
S((kod, ko))
S((kod, ko, kwargs))
S((args, kod, ko))
with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
S((pk, po, args, ko, kwargs))
@ -2333,15 +2358,18 @@ def test(po, pk, pod=42, pkd=100, *args, ko, **kwargs):
with self.assertRaisesRegex(ValueError, 'follows default argument'):
S((pod, po))
with self.assertRaisesRegex(ValueError, 'follows default argument'):
S((pod, pk))
with self.assertRaisesRegex(ValueError, 'follows default argument'):
S((po, pod, pk))
with self.assertRaisesRegex(ValueError, 'follows default argument'):
S((po, pkd, pk))
with self.assertRaisesRegex(ValueError, 'follows default argument'):
S((pkd, pk))
self.assertTrue(repr(sig).startswith('<Signature'))
self.assertTrue('(po, pk' in repr(sig))
def test_signature_object_pickle(self):
def foo(a, b, *, c:1={}, **kw) -> {42:'ham'}: pass
foo_partial = functools.partial(foo, a=1)

View file

@ -0,0 +1,3 @@
Now creating :class:`inspect.Signature` objects with positional-only
parameter with a default followed by a positional-or-keyword parameter
without one is impossible.