123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- import functools
- import inspect
- import itertools
- import operator
- import cytoolz
- from cytoolz.functoolz import (curry, is_valid_args, is_partial_args, is_arity,
- num_required_args, has_varargs, has_keywords)
- from cytoolz._signatures import builtins
- import cytoolz._signatures as _sigs
- from cytoolz.compatibility import PY3
- from cytoolz.utils import raises
- def make_func(param_string, raise_if_called=True):
- if not param_string.startswith('('):
- param_string = '(%s)' % param_string
- if raise_if_called:
- body = 'raise ValueError("function should not be called")'
- else:
- body = 'return True'
- d = {}
- exec('def func%s:\n %s' % (param_string, body), globals(), d)
- return d['func']
- def test_make_func():
- f = make_func('')
- assert raises(ValueError, lambda: f())
- assert raises(TypeError, lambda: f(1))
- f = make_func('', raise_if_called=False)
- assert f()
- assert raises(TypeError, lambda: f(1))
- f = make_func('x, y=1', raise_if_called=False)
- assert f(1)
- assert f(x=1)
- assert f(1, 2)
- assert f(x=1, y=2)
- assert raises(TypeError, lambda: f(1, 2, 3))
- f = make_func('(x, y=1)', raise_if_called=False)
- assert f(1)
- assert f(x=1)
- assert f(1, 2)
- assert f(x=1, y=2)
- assert raises(TypeError, lambda: f(1, 2, 3))
- def test_is_valid(check_valid=is_valid_args, incomplete=False):
- orig_check_valid = check_valid
- check_valid = lambda func, *args, **kwargs: orig_check_valid(func, args, kwargs)
- f = make_func('')
- assert check_valid(f)
- assert check_valid(f, 1) is False
- assert check_valid(f, x=1) is False
- f = make_func('x')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, x=2) is False
- assert check_valid(f, 1, y=2) is False
- assert check_valid(f, 1, 2) is False
- assert check_valid(f, x=1, y=2) is False
- f = make_func('x=1')
- assert check_valid(f)
- assert check_valid(f, 1)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, x=2) is False
- assert check_valid(f, 1, y=2) is False
- assert check_valid(f, 1, 2) is False
- assert check_valid(f, x=1, y=2) is False
- f = make_func('*args')
- assert check_valid(f)
- assert check_valid(f, 1)
- assert check_valid(f, 1, 2)
- assert check_valid(f, x=1) is False
- f = make_func('**kwargs')
- assert check_valid(f)
- assert check_valid(f, x=1)
- assert check_valid(f, x=1, y=2)
- assert check_valid(f, 1) is False
- f = make_func('x, *args')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1)
- assert check_valid(f, 1, 2)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, x=1) is False
- assert check_valid(f, 1, y=1) is False
- f = make_func('x, y=1, **kwargs')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, 2)
- assert check_valid(f, x=1, y=2, z=3)
- assert check_valid(f, 1, 2, y=3) is False
- f = make_func('a, b, c=3, d=4')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, 1, 2)
- assert check_valid(f, 1, c=3) is incomplete
- assert check_valid(f, 1, e=3) is False
- assert check_valid(f, 1, 2, e=3) is False
- assert check_valid(f, 1, 2, b=3) is False
- assert check_valid(1) is False
- def test_is_valid_py3(check_valid=is_valid_args, incomplete=False):
- if not PY3:
- return
- orig_check_valid = check_valid
- check_valid = lambda func, *args, **kwargs: orig_check_valid(func, args, kwargs)
- f = make_func('x, *, y=1')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, y=2)
- assert check_valid(f, 1, 2) is False
- assert check_valid(f, 1, z=2) is False
- f = make_func('x, *args, y=1')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1)
- assert check_valid(f, x=1)
- assert check_valid(f, 1, y=2)
- assert check_valid(f, 1, 2, y=2)
- assert check_valid(f, 1, 2)
- assert check_valid(f, 1, z=2) is False
- f = make_func('*, y=1')
- assert check_valid(f)
- assert check_valid(f, 1) is False
- assert check_valid(f, y=1)
- assert check_valid(f, z=1) is False
- f = make_func('x, *, y')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, x=1) is incomplete
- assert check_valid(f, 1, y=2)
- assert check_valid(f, x=1, y=2)
- assert check_valid(f, 1, 2) is False
- assert check_valid(f, 1, z=2) is False
- assert check_valid(f, 1, y=1, z=2) is False
- f = make_func('x=1, *, y, z=3')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1, z=3) is incomplete
- assert check_valid(f, y=2)
- assert check_valid(f, 1, y=2)
- assert check_valid(f, x=1, y=2)
- assert check_valid(f, x=1, y=2, z=3)
- assert check_valid(f, 1, x=1, y=2) is False
- assert check_valid(f, 1, 3, y=2) is False
- f = make_func('w, x=2, *args, y, z=4')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, 1, y=3)
- f = make_func('a, b, c=3, d=4, *args, e=5, f=6, g, h')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, 1, 2) is incomplete
- assert check_valid(f, 1, 2, g=7) is incomplete
- assert check_valid(f, 1, 2, g=7, h=8)
- assert check_valid(f, 1, 2, 3, 4, 5, 6, 7, 8, 9) is incomplete
- f = make_func('a: int, b: float')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, b=1) is incomplete
- assert check_valid(f, 1, 2)
- f = make_func('(a: int, b: float) -> float')
- assert check_valid(f) is incomplete
- assert check_valid(f, 1) is incomplete
- assert check_valid(f, b=1) is incomplete
- assert check_valid(f, 1, 2)
- f.__signature__ = 34
- assert check_valid(f) is False
- class RaisesValueError(object):
- def __call__(self):
- pass
- @property
- def __signature__(self):
- raise ValueError('Testing Python 3.4')
- f = RaisesValueError()
- assert check_valid(f) is None
- def test_is_partial():
- test_is_valid(check_valid=is_partial_args, incomplete=True)
- test_is_valid_py3(check_valid=is_partial_args, incomplete=True)
- def test_is_valid_curry():
- def check_curry(func, args, kwargs, incomplete=True):
- try:
- curry(func)(*args, **kwargs)
- curry(func, *args)(**kwargs)
- curry(func, **kwargs)(*args)
- curry(func, *args, **kwargs)()
- if not isinstance(func, type(lambda: None)):
- return None
- return incomplete
- except ValueError:
- return True
- except TypeError:
- return False
- check_valid = functools.partial(check_curry, incomplete=True)
- test_is_valid(check_valid=check_valid, incomplete=True)
- test_is_valid_py3(check_valid=check_valid, incomplete=True)
- check_valid = functools.partial(check_curry, incomplete=False)
- test_is_valid(check_valid=check_valid, incomplete=False)
- test_is_valid_py3(check_valid=check_valid, incomplete=False)
- def test_func_keyword():
- def f(func=None):
- pass
- assert is_valid_args(f, (), {})
- assert is_valid_args(f, (None,), {})
- assert is_valid_args(f, (), {'func': None})
- assert is_valid_args(f, (None,), {'func': None}) is False
- assert is_partial_args(f, (), {})
- assert is_partial_args(f, (None,), {})
- assert is_partial_args(f, (), {'func': None})
- assert is_partial_args(f, (None,), {'func': None}) is False
- def test_has_unknown_args():
- assert has_varargs(1) is False
- assert has_varargs(map)
- assert has_varargs(make_func('')) is False
- assert has_varargs(make_func('x, y, z')) is False
- assert has_varargs(make_func('*args'))
- assert has_varargs(make_func('**kwargs')) is False
- assert has_varargs(make_func('x, y, *args, **kwargs'))
- assert has_varargs(make_func('x, y, z=1')) is False
- assert has_varargs(make_func('x, y, z=1, **kwargs')) is False
- if PY3:
- f = make_func('*args')
- f.__signature__ = 34
- assert has_varargs(f) is False
- class RaisesValueError(object):
- def __call__(self):
- pass
- @property
- def __signature__(self):
- raise ValueError('Testing Python 3.4')
- f = RaisesValueError()
- assert has_varargs(f) is None
- def test_num_required_args():
- assert num_required_args(lambda: None) == 0
- assert num_required_args(lambda x: None) == 1
- assert num_required_args(lambda x, *args: None) == 1
- assert num_required_args(lambda x, **kwargs: None) == 1
- assert num_required_args(lambda x, y, *args, **kwargs: None) == 2
- assert num_required_args(map) == 2
- assert num_required_args(dict) is None
- def test_has_keywords():
- assert has_keywords(lambda: None) is False
- assert has_keywords(lambda x: None) is False
- assert has_keywords(lambda x=1: None)
- assert has_keywords(lambda **kwargs: None)
- assert has_keywords(int)
- assert has_keywords(sorted)
- assert has_keywords(max)
- assert has_keywords(map) is False
- assert has_keywords(bytearray) is None
- def test_has_varargs():
- assert has_varargs(lambda: None) is False
- assert has_varargs(lambda *args: None)
- assert has_varargs(lambda **kwargs: None) is False
- assert has_varargs(map)
- if PY3:
- assert has_varargs(max) is None
- def test_is_arity():
- assert is_arity(0, lambda: None)
- assert is_arity(1, lambda: None) is False
- assert is_arity(1, lambda x: None)
- assert is_arity(3, lambda x, y, z: None)
- assert is_arity(1, lambda x, *args: None) is False
- assert is_arity(1, lambda x, **kwargs: None) is False
- assert is_arity(1, all)
- assert is_arity(2, map) is False
- assert is_arity(2, range) is None
- def test_introspect_curry_valid_py3(check_valid=is_valid_args, incomplete=False):
- if not PY3:
- return
- orig_check_valid = check_valid
- check_valid = lambda _func, *args, **kwargs: orig_check_valid(_func, args, kwargs)
- f = cytoolz.curry(make_func('x, y, z=0'))
- assert check_valid(f)
- assert check_valid(f, 1)
- assert check_valid(f, 1, 2)
- assert check_valid(f, 1, 2, 3)
- assert check_valid(f, 1, 2, 3, 4) is False
- assert check_valid(f, invalid_keyword=True) is False
- assert check_valid(f(1))
- assert check_valid(f(1), 2)
- assert check_valid(f(1), 2, 3)
- assert check_valid(f(1), 2, 3, 4) is False
- assert check_valid(f(1), x=2) is False
- assert check_valid(f(1), y=2)
- assert check_valid(f(x=1), 2) is False
- assert check_valid(f(x=1), y=2)
- assert check_valid(f(y=2), 1)
- assert check_valid(f(y=2), 1, z=3)
- assert check_valid(f(y=2), 1, 3) is False
- f = cytoolz.curry(make_func('x, y, z=0'), 1, x=1)
- assert check_valid(f) is False
- assert check_valid(f, z=3) is False
- f = cytoolz.curry(make_func('x, y, *args, z'))
- assert check_valid(f)
- assert check_valid(f, 0)
- assert check_valid(f(1), 0)
- assert check_valid(f(1, 2), 0)
- assert check_valid(f(1, 2, 3), 0)
- assert check_valid(f(1, 2, 3, 4), 0)
- assert check_valid(f(1, 2, 3, 4), z=4)
- assert check_valid(f(x=1))
- assert check_valid(f(x=1), 1) is False
- assert check_valid(f(x=1), y=2)
- def test_introspect_curry_partial_py3():
- test_introspect_curry_valid_py3(check_valid=is_partial_args, incomplete=True)
- def test_introspect_curry_py3():
- if not PY3:
- return
- f = cytoolz.curry(make_func(''))
- assert num_required_args(f) == 0
- assert is_arity(0, f)
- assert has_varargs(f) is False
- assert has_keywords(f) is False
- f = cytoolz.curry(make_func('x'))
- assert num_required_args(f) == 0
- assert is_arity(0, f) is False
- assert is_arity(1, f) is False
- assert has_varargs(f) is False
- assert has_keywords(f) # A side-effect of being curried
- f = cytoolz.curry(make_func('x, y, z=0'))
- assert num_required_args(f) == 0
- assert is_arity(0, f) is False
- assert is_arity(1, f) is False
- assert is_arity(2, f) is False
- assert is_arity(3, f) is False
- assert has_varargs(f) is False
- assert has_keywords(f)
- f = cytoolz.curry(make_func('*args, **kwargs'))
- assert num_required_args(f) == 0
- assert has_varargs(f)
- assert has_keywords(f)
- def test_introspect_builtin_modules():
- mods = [builtins, functools, itertools, operator, cytoolz,
- cytoolz.functoolz, cytoolz.itertoolz, cytoolz.dicttoolz, cytoolz.recipes]
- blacklist = set()
- def add_blacklist(mod, attr):
- if hasattr(mod, attr):
- blacklist.add(getattr(mod, attr))
- add_blacklist(builtins, 'basestring')
- add_blacklist(builtins, 'NoneType')
- add_blacklist(builtins, '__metaclass__')
- add_blacklist(builtins, 'sequenceiterator')
- def is_missing(modname, name, func):
- if name.startswith('_') and not name.startswith('__'):
- return False
- if name.startswith('__pyx_unpickle_') or name.endswith('_cython__'):
- return False
- try:
- if issubclass(func, BaseException):
- return False
- except TypeError:
- pass
- try:
- return (callable(func)
- and func.__module__ is not None
- and modname in func.__module__
- and is_partial_args(func, (), {}) is not True
- and func not in blacklist)
- except AttributeError:
- return False
- missing = {}
- for mod in mods:
- modname = mod.__name__
- for name, func in vars(mod).items():
- if is_missing(modname, name, func):
- if modname not in missing:
- missing[modname] = []
- missing[modname].append(name)
- if missing:
- messages = []
- for modname, names in sorted(missing.items()):
- msg = '{}:\n {}'.format(modname, '\n '.join(sorted(names)))
- messages.append(msg)
- message = 'Missing introspection for the following callables:\n\n'
- raise AssertionError(message + '\n\n'.join(messages))
- def test_inspect_signature_property():
- if not PY3:
- return
- # By adding AddX to our signature registry, we can inspect the class
- # itself and objects of the class. `inspect.signature` doesn't like
- # it when `obj.__signature__` is a property.
- class AddX(object):
- def __init__(self, func):
- self.func = func
- def __call__(self, addx, *args, **kwargs):
- return addx + self.func(*args, **kwargs)
- @property
- def __signature__(self):
- sig = inspect.signature(self.func)
- params = list(sig.parameters.values())
- kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
- newparam = inspect.Parameter('addx', kind)
- params = [newparam] + params
- return sig.replace(parameters=params)
- addx = AddX(lambda x: x)
- sig = inspect.signature(addx)
- assert sig == inspect.Signature(parameters=[
- inspect.Parameter('addx', inspect.Parameter.POSITIONAL_OR_KEYWORD),
- inspect.Parameter('x', inspect.Parameter.POSITIONAL_OR_KEYWORD)])
- assert num_required_args(AddX) is False
- _sigs.signatures[AddX] = (_sigs.expand_sig((0, lambda func: None)),)
- assert num_required_args(AddX) == 1
- del _sigs.signatures[AddX]
- def test_inspect_wrapped_property():
- class Wrapped(object):
- def __init__(self, func):
- self.func = func
- def __call__(self, *args, **kwargs):
- return self.func(*args, **kwargs)
- @property
- def __wrapped__(self):
- return self.func
- func = lambda x: x
- wrapped = Wrapped(func)
- if PY3:
- assert inspect.signature(func) == inspect.signature(wrapped)
- assert num_required_args(Wrapped) is None
- _sigs.signatures[Wrapped] = (_sigs.expand_sig((0, lambda func: None)),)
- assert num_required_args(Wrapped) == 1
|