summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2015-05-16 17:45:09 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2015-05-16 17:45:09 (GMT)
commitb907a513c8285410d93adc2895508985838fe1a7 (patch)
tree80450ad00573547efc68174d32388ce1b140bbfc /Lib
parent1392f71c3927c9d81969200f5dfff9fb832cde0b (diff)
downloadcpython-b907a513c8285410d93adc2895508985838fe1a7.zip
cpython-b907a513c8285410d93adc2895508985838fe1a7.tar.gz
cpython-b907a513c8285410d93adc2895508985838fe1a7.tar.bz2
Issue 24190: Add inspect.BoundArguments.apply_defaults() method.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/inspect.py30
-rw-r--r--Lib/test/test_inspect.py35
2 files changed, 65 insertions, 0 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 9389f3b..8d2920a 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -2443,6 +2443,36 @@ class BoundArguments:
return kwargs
+ def apply_defaults(self):
+ """Set default values for missing arguments.
+
+ For variable-positional arguments (*args) the default is an
+ empty tuple.
+
+ For variable-keyword arguments (**kwargs) the default is an
+ empty dict.
+ """
+ arguments = self.arguments
+ if not arguments:
+ return
+ new_arguments = []
+ for name, param in self._signature.parameters.items():
+ try:
+ new_arguments.append((name, arguments[name]))
+ except KeyError:
+ if param.default is not _empty:
+ val = param.default
+ elif param.kind is _VAR_POSITIONAL:
+ val = ()
+ elif param.kind is _VAR_KEYWORD:
+ val = {}
+ else:
+ # This BoundArguments was likely produced by
+ # Signature.bind_partial().
+ continue
+ new_arguments.append((name, val))
+ self.arguments = OrderedDict(new_arguments)
+
def __eq__(self, other):
return (self is other or
(issubclass(other.__class__, BoundArguments) and
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index bed3bad..e712efb 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -3153,6 +3153,41 @@ class TestBoundArguments(unittest.TestCase):
ba = sig.bind(20, 30, z={})
self.assertRegex(repr(ba), r'<BoundArguments \(a=20,.*\}\}\)>')
+ def test_signature_bound_arguments_apply_defaults(self):
+ def foo(a, b=1, *args, c:1={}, **kw): pass
+ sig = inspect.signature(foo)
+
+ ba = sig.bind(20)
+ ba.apply_defaults()
+ self.assertEqual(
+ list(ba.arguments.items()),
+ [('a', 20), ('b', 1), ('args', ()), ('c', {}), ('kw', {})])
+
+ # Make sure that we preserve the order:
+ # i.e. 'c' should be *before* 'kw'.
+ ba = sig.bind(10, 20, 30, d=1)
+ ba.apply_defaults()
+ self.assertEqual(
+ list(ba.arguments.items()),
+ [('a', 10), ('b', 20), ('args', (30,)), ('c', {}), ('kw', {'d':1})])
+
+ # Make sure that BoundArguments produced by bind_partial()
+ # are supported.
+ def foo(a, b): pass
+ sig = inspect.signature(foo)
+ ba = sig.bind_partial(20)
+ ba.apply_defaults()
+ self.assertEqual(
+ list(ba.arguments.items()),
+ [('a', 20)])
+
+ # Test no args
+ def foo(): pass
+ sig = inspect.signature(foo)
+ ba = sig.bind()
+ ba.apply_defaults()
+ self.assertEqual(list(ba.arguments.items()), [])
+
class TestSignaturePrivateHelpers(unittest.TestCase):
def test_signature_get_bound_param(self):