diff options
author | Nikita Sobolev <mail@sobolevn.me> | 2023-04-22 05:45:10 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-22 05:45:10 (GMT) |
commit | 6b58d419a1bd62ac94274d108d59980a3eb6f6e0 (patch) | |
tree | e35b14b85f06898b64bc70d3c84a62de2cd7c69b /Lib | |
parent | 7d20783d45a9c78379fe79229b57e4c31610a623 (diff) | |
download | cpython-6b58d419a1bd62ac94274d108d59980a3eb6f6e0.zip cpython-6b58d419a1bd62ac94274d108d59980a3eb6f6e0.tar.gz cpython-6b58d419a1bd62ac94274d108d59980a3eb6f6e0.tar.bz2 |
gh-103556: [inspect.Signature] disallow pos-or-kw params without default after pos-only with default (#103557)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/inspect.py | 10 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 40 |
2 files changed, 38 insertions, 12 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py index 4242b40..6d1d7b7 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3006,7 +3006,7 @@ class Signature: if __validate_parameters__: params = OrderedDict() top_kind = _POSITIONAL_ONLY - kind_defaults = False + seen_default = False for param in parameters: kind = param.kind @@ -3021,21 +3021,19 @@ class Signature: 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) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 6b342b1..42e3d70 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -2463,18 +2463,43 @@ class TestSignatureObject(unittest.TestCase): 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)) @@ -2496,14 +2521,17 @@ class TestSignatureObject(unittest.TestCase): 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) |