mirror of https://github.com/python/cpython
inspect.Signature: ensure that non-default params don't follow default ones #20427
This commit is contained in:
parent
76c6c593ed
commit
07a9e452ac
|
@ -1924,6 +1924,7 @@ class Signature:
|
||||||
if __validate_parameters__:
|
if __validate_parameters__:
|
||||||
params = OrderedDict()
|
params = OrderedDict()
|
||||||
top_kind = _POSITIONAL_ONLY
|
top_kind = _POSITIONAL_ONLY
|
||||||
|
kind_defaults = False
|
||||||
|
|
||||||
for idx, param in enumerate(parameters):
|
for idx, param in enumerate(parameters):
|
||||||
kind = param.kind
|
kind = param.kind
|
||||||
|
@ -1933,9 +1934,27 @@ class Signature:
|
||||||
msg = 'wrong parameter order: {} before {}'
|
msg = 'wrong parameter order: {} before {}'
|
||||||
msg = msg.format(top_kind, kind)
|
msg = msg.format(top_kind, kind)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
else:
|
elif kind > top_kind:
|
||||||
|
kind_defaults = False
|
||||||
top_kind = kind
|
top_kind = kind
|
||||||
|
|
||||||
|
if (kind in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD) and
|
||||||
|
not param._partial_kwarg):
|
||||||
|
# If we have a positional-only or positional-or-keyword
|
||||||
|
# parameter, that does not have its default value set
|
||||||
|
# by 'functools.partial' or other "partial" signature:
|
||||||
|
if param.default is _empty:
|
||||||
|
if kind_defaults:
|
||||||
|
# No default for this parameter, but the
|
||||||
|
# previous parameter of the same kind had
|
||||||
|
# a default
|
||||||
|
msg = 'non-default argument follows default ' \
|
||||||
|
'argument'
|
||||||
|
raise ValueError(msg)
|
||||||
|
else:
|
||||||
|
# There is a default for this parameter.
|
||||||
|
kind_defaults = True
|
||||||
|
|
||||||
if name in params:
|
if name in params:
|
||||||
msg = 'duplicate parameter name: {!r}'.format(name)
|
msg = 'duplicate parameter name: {!r}'.format(name)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
|
@ -1522,11 +1522,13 @@ class TestSignatureObject(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(str(S()), '()')
|
self.assertEqual(str(S()), '()')
|
||||||
|
|
||||||
def test(po, pk, *args, ko, **kwargs):
|
def test(po, pk, pod=42, pkd=100, *args, ko, **kwargs):
|
||||||
pass
|
pass
|
||||||
sig = inspect.signature(test)
|
sig = inspect.signature(test)
|
||||||
po = sig.parameters['po'].replace(kind=P.POSITIONAL_ONLY)
|
po = sig.parameters['po'].replace(kind=P.POSITIONAL_ONLY)
|
||||||
|
pod = sig.parameters['pod'].replace(kind=P.POSITIONAL_ONLY)
|
||||||
pk = sig.parameters['pk']
|
pk = sig.parameters['pk']
|
||||||
|
pkd = sig.parameters['pkd']
|
||||||
args = sig.parameters['args']
|
args = sig.parameters['args']
|
||||||
ko = sig.parameters['ko']
|
ko = sig.parameters['ko']
|
||||||
kwargs = sig.parameters['kwargs']
|
kwargs = sig.parameters['kwargs']
|
||||||
|
@ -1549,6 +1551,15 @@ class TestSignatureObject(unittest.TestCase):
|
||||||
with self.assertRaisesRegex(ValueError, 'duplicate parameter name'):
|
with self.assertRaisesRegex(ValueError, 'duplicate parameter name'):
|
||||||
S((po, pk, args, kwargs2, ko))
|
S((po, pk, args, kwargs2, ko))
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(ValueError, 'follows default argument'):
|
||||||
|
S((pod, po))
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(ValueError, 'follows default argument'):
|
||||||
|
S((po, pkd, pk))
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(ValueError, 'follows default argument'):
|
||||||
|
S((pkd, pk))
|
||||||
|
|
||||||
def test_signature_immutability(self):
|
def test_signature_immutability(self):
|
||||||
def test(a):
|
def test(a):
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue