]> granicus.if.org Git - python/commitdiff
inspect.Signature.bind: Fix pos-only params with defaults; fix *args in named args...
authorYury Selivanov <yselivanov@sprymix.com>
Tue, 28 Jan 2014 22:27:39 +0000 (17:27 -0500)
committerYury Selivanov <yselivanov@sprymix.com>
Tue, 28 Jan 2014 22:27:39 +0000 (17:27 -0500)
Initial patch by Yann Kaiser (epsy).

Lib/inspect.py
Lib/test/test_inspect.py

index 0e41626e029355734699b4160e56e1e613286f58..c3fecb8b8cb5bba3f703bba4d87792541d9421e4 100644 (file)
@@ -2258,6 +2258,8 @@ class Signature:
                         parameters_ex = (param,)
                         break
                     else:
+                        # No default, not VAR_KEYWORD, not VAR_POSITIONAL,
+                        # not in `kwargs`
                         if partial:
                             parameters_ex = (param,)
                             break
@@ -2296,19 +2298,17 @@ class Signature:
         # keyword arguments
         kwargs_param = None
         for param in itertools.chain(parameters_ex, parameters):
-            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))
-
             if param.kind == _VAR_KEYWORD:
                 # Memorize that we have a '**kwargs'-like parameter
                 kwargs_param = param
                 continue
 
+            if param.kind == _VAR_POSITIONAL:
+                # Named arguments don't refer to '*args'-like parameters.
+                # We only arrive here if the positional arguments ended
+                # before reaching the last parameter before *args.
+                continue
+
             param_name = param.name
             try:
                 arg_val = kwargs.pop(param_name)
index 1d251b92e3fae6b3b1c1f7dbbbcbce2fa5c0f58c..1325c7a9511e14201da5907d9c694b645f7f8ff5 100644 (file)
@@ -2516,6 +2516,15 @@ class TestSignatureBind(unittest.TestCase):
         self.assertEqual(self.call(test, 1, 2, 4, 5, bar=6),
                          (1, 2, 4, 5, 6, {}))
 
+        self.assertEqual(self.call(test, 1, 2),
+                         (1, 2, 3, 42, 50, {}))
+
+        self.assertEqual(self.call(test, 1, 2, foo=4, bar=5),
+                         (1, 2, 3, 4, 5, {}))
+
+        with self.assertRaisesRegex(TypeError, "but was passed as a keyword"):
+            self.call(test, 1, 2, foo=4, bar=5, c_po=10)
+
         with self.assertRaisesRegex(TypeError, "parameter is positional only"):
             self.call(test, 1, 2, c_po=4)
 
@@ -2532,6 +2541,22 @@ class TestSignatureBind(unittest.TestCase):
         ba = sig.bind(1, self=2, b=3)
         self.assertEqual(ba.args, (1, 2, 3))
 
+    def test_signature_bind_vararg_name(self):
+        def test(a, *args):
+            return a, args
+        sig = inspect.signature(test)
+
+        with self.assertRaisesRegex(TypeError, "too many keyword arguments"):
+            sig.bind(a=0, args=1)
+
+        def test(*args, **kwargs):
+            return args, kwargs
+        self.assertEqual(self.call(test, args=1), ((), {'args': 1}))
+
+        sig = inspect.signature(test)
+        ba = sig.bind(args=1)
+        self.assertEqual(ba.arguments, {'kwargs': {'args': 1}})
+
 
 class TestBoundArguments(unittest.TestCase):
     def test_signature_bound_arguments_unhashable(self):