summaryrefslogtreecommitdiffstats
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
parent1392f71c3927c9d81969200f5dfff9fb832cde0b (diff)
downloadcpython-b907a513c8285410d93adc2895508985838fe1a7.zip
cpython-b907a513c8285410d93adc2895508985838fe1a7.tar.gz
cpython-b907a513c8285410d93adc2895508985838fe1a7.tar.bz2
Issue 24190: Add inspect.BoundArguments.apply_defaults() method.
-rw-r--r--Doc/library/inspect.rst41
-rw-r--r--Doc/whatsnew/3.5.rst3
-rw-r--r--Lib/inspect.py30
-rw-r--r--Lib/test/test_inspect.py35
-rw-r--r--Misc/NEWS3
5 files changed, 91 insertions, 21 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 444d2be..3ee177d 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -667,27 +667,8 @@ function.
Arguments for which :meth:`Signature.bind` or
:meth:`Signature.bind_partial` relied on a default value are skipped.
- However, if needed, it is easy to include them.
-
- ::
-
- >>> def foo(a, b=10):
- ... pass
-
- >>> sig = signature(foo)
- >>> ba = sig.bind(5)
-
- >>> ba.args, ba.kwargs
- ((5,), {})
-
- >>> for param in sig.parameters.values():
- ... if (param.name not in ba.arguments
- ... and param.default is not param.empty):
- ... ba.arguments[param.name] = param.default
-
- >>> ba.args, ba.kwargs
- ((5, 10), {})
-
+ However, if needed, use :meth:`BoundArguments.apply_defaults` to add
+ them.
.. attribute:: BoundArguments.args
@@ -703,6 +684,24 @@ function.
A reference to the parent :class:`Signature` object.
+ .. method:: BoundArguments.apply_defaults()
+
+ 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.
+
+ ::
+
+ >>> def foo(a, b='ham', *args): pass
+ >>> ba = inspect.signature(foo).bind('spam')
+ >>> ba.apply_defaults()
+ >>> ba.arguments
+ OrderedDict([('a', 'spam'), ('b', 'ham'), ('args', ())])
+
The :attr:`args` and :attr:`kwargs` properties can be used to invoke
functions::
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 7c845cd..92e1142 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -400,6 +400,9 @@ inspect
picklable and hashable. (Contributed by Yury Selivanov in :issue:`20726`
and :issue:`20334`.)
+* New method :meth:`inspect.BoundArguments.apply_defaults`. (Contributed
+ by Yury Selivanov in :issue:`24190`.)
+
* New class method :meth:`inspect.Signature.from_callable`, which makes
subclassing of :class:`~inspect.Signature` easier. (Contributed
by Yury Selivanov and Eric Snow in :issue:`17373`.)
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):
diff --git a/Misc/NEWS b/Misc/NEWS
index 46da169..95c5332 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -131,6 +131,9 @@ Library
- Issue 22547: Implement informative __repr__ for inspect.BoundArguments.
Contributed by Yury Selivanov.
+- Issue 24190: Implement inspect.BoundArgument.apply_defaults() method.
+ Contributed by Yury Selivanov.
+
Tests
-----