fix kwonly arg removal in funcutils.FunctionBuilder, thus fixing funcutils.wraps, which fixes #123

This commit is contained in:
Mahmoud Hashemi 2017-04-13 23:58:21 -07:00
parent b58ef52a3a
commit 14208cf6e3
2 changed files with 52 additions and 8 deletions

View File

@ -561,16 +561,24 @@ class FunctionBuilder(object):
Raises a :exc:`ValueError` if the argument is not present.
"""
args = self.args
d_dict = self.get_defaults_dict()
try:
self.args.remove(arg_name)
args.remove(arg_name)
except ValueError:
exc = MissingArgument('arg %r not found in %s argument list: %r'
% (arg_name, self.name, self.args))
exc.arg_name = arg_name
raise exc
d_dict.pop(arg_name, None)
self.defaults = tuple([d_dict[a] for a in self.args if a in d_dict])
try:
self.kwonlyargs.remove(arg_name)
except (AttributeError, ValueError):
# py2, or py3 and missing from both
exc = MissingArgument('arg %r not found in %s argument list:'
' %r' % (arg_name, self.name, args))
exc.arg_name = arg_name
raise exc
else:
self.kwonlydefaults.pop(arg_name, None)
else:
d_dict.pop(arg_name, None)
self.defaults = tuple([d_dict[a] for a in args if a in d_dict])
return
def _compile(self, src, execdict):

View File

@ -1,7 +1,10 @@
from boltons.funcutils import wraps, FunctionBuilder
import inspect
import pytest
from boltons.funcutils import wraps, FunctionBuilder
def pita_wrap(flag=False):
@ -42,6 +45,39 @@ def test_wraps_py3():
True, 'kwonly_non_roundtrippable_repr', 2)
def test_remove_kwonly_arg():
# example adapted from https://github.com/mahmoud/boltons/issues/123
def darkhelm_inject_loop(func):
sig = inspect.signature(func)
loop_param = sig.parameters['loop'].replace(default=None)
sig = sig.replace(parameters=[loop_param])
def add_loop(args, kwargs):
bargs = sig.bind(*args, **kwargs)
bargs.apply_defaults()
if bargs.arguments['loop'] is None:
bargs.arguments['loop'] = "don't look at me, I just use gevent"
return bargs.arguments
def wrapper(*args, **kwargs):
return func(**add_loop(args, kwargs))
return wraps(func, injected=['loop'])(wrapper)
@darkhelm_inject_loop
def example(test='default', *, loop='lol'):
return loop
fb_example = FunctionBuilder.from_func(example)
assert 'test' in fb_example.args
assert fb_example.get_defaults_dict()['test'] == 'default'
assert 'loop' not in fb_example.kwonlyargs
assert 'loop' not in fb_example.kwonlydefaults
@pytest.mark.parametrize('signature,should_match',
[('a, *, b', True),
('a,*,b', True),