boltons/tests/test_funcutils_fb_py3.py

150 lines
4.4 KiB
Python

import inspect
from collections import defaultdict
import pytest
from boltons.funcutils import wraps, FunctionBuilder
def pita_wrap(flag=False):
def cedar_dec(func):
@wraps(func)
def cedar_wrapper(*a, **kw):
return (flag, func.__name__, func(*a, **kw))
return cedar_wrapper
return cedar_dec
def test_wraps_py3():
@pita_wrap(flag=True)
def annotations(a: int, b: float=1, c: defaultdict=()) -> defaultdict:
return a, b, c
assert annotations(0) == (True, "annotations", (0, 1, ()))
assert annotations.__annotations__ == {'a': int, 'b': float,
'c': defaultdict,
'return': defaultdict}
@pita_wrap(flag=False)
def kwonly_arg(a, *, b, c=2):
return a, b, c
with pytest.raises(TypeError):
kwonly_arg(0)
assert kwonly_arg(0, b=1) == (False, "kwonly_arg", (0, 1, 2))
assert kwonly_arg(0, b=1, c=3) == (False, "kwonly_arg", (0, 1, 3))
@pita_wrap(flag=True)
def kwonly_non_roundtrippable_repr(*, x=lambda y: y + 1):
return x(1)
assert kwonly_non_roundtrippable_repr() == (
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
def test_defaults_dict():
def example(req, test='default', *, loop='lol'):
return loop
fb_example = FunctionBuilder.from_func(example)
assert 'test' in fb_example.args
dd = fb_example.get_defaults_dict()
assert dd['test'] == 'default'
assert dd['loop'] == 'lol'
assert 'req' not in dd
def test_get_arg_names():
def example(req, test='default', *, loop='lol'):
return loop
fb_example = FunctionBuilder.from_func(example)
assert 'test' in fb_example.args
assert fb_example.get_arg_names() == ('req', 'test', 'loop')
assert fb_example.get_arg_names(only_required=True) == ('req',)
@pytest.mark.parametrize('signature,should_match',
[('a, *, b', True),
('a,*,b', True),
('a, * , b', True),
('a, *,\nb', True),
('a, *\n,b', True),
('a, b', False),
('a, *args', False),
('a, *args, **kwargs', False),
('*args', False),
('*args, **kwargs', False)])
def test_FunctionBuilder_KWONLY_MARKER(signature, should_match):
"""
_KWONLY_MARKER matches the keyword-only argument separator,
regardless of whitespace.
Note: it assumes the signature is valid Python.
"""
matched = bool(FunctionBuilder._KWONLY_MARKER.search(signature))
message = "{!r}: should_match was {}, but result was {}".format(
signature, should_match, matched)
assert bool(matched) == should_match, message
def test_FunctionBuilder_add_arg_kwonly():
fb = FunctionBuilder('return_val', doc='returns the value',
body='return val')
broken_func = fb.get_func()
with pytest.raises(NameError):
broken_func()
fb.add_arg('val', default='default_val', kwonly=True)
better_func = fb.get_func()
assert better_func() == 'default_val'
with pytest.raises(ValueError):
fb.add_arg('val')
assert better_func(val='keyword') == 'keyword'
with pytest.raises(TypeError):
assert better_func('positional')
return