Skip to content

Commit

Permalink
Add 3.5's apply_defaults method to BoundArguments (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
BlckKnght authored and rbtcollins committed Jan 17, 2019
1 parent c10d403 commit b6094ad
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 19 deletions.
39 changes: 20 additions & 19 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,25 +273,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:
... 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
Expand All @@ -303,6 +286,24 @@ function.

A dict of keyword arguments values. Dynamically computed from the
:attr:`arguments` attribute.

.. 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::
Expand Down
28 changes: 28 additions & 0 deletions funcsigs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,34 @@ def kwargs(self):

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
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 __hash__(self):
msg = "unhashable type: '{0}'".format(self.__class__.__name__)
raise TypeError(msg)
Expand Down
55 changes: 55 additions & 0 deletions tests/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,3 +1000,58 @@ def foo(a): pass
def bar(b): pass
ba4 = inspect.signature(bar).bind(1)
self.assertNotEqual(ba, ba4)

if sys.version_info[0] > 2:
exec("""
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})])
""")

def test_signature_bound_arguments_apply_defaults_common(self):
def foo(a, b=1, *args, **kw): pass
sig = inspect.signature(foo)

ba = sig.bind(20)
ba.apply_defaults()
self.assertEqual(
list(ba.arguments.items()),
[('a', 20), ('b', 1), ('args', ()), ('kw', {})])

# 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()), [])

# Make sure a no-args binding still acquires proper defaults.
def foo(a='spam'): pass
sig = inspect.signature(foo)
ba = sig.bind()
ba.apply_defaults()
self.assertEqual(list(ba.arguments.items()), [('a', 'spam')])

0 comments on commit b6094ad

Please sign in to comment.