Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/toolz/functoolz.py: 30%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from functools import reduce, partial
2import inspect
3import sys
4from operator import attrgetter, not_
5from importlib import import_module
6from types import MethodType
8from .utils import no_default
10PYPY = hasattr(sys, 'pypy_version_info') and sys.version_info[0] > 2
13__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize',
14 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do',
15 'curry', 'flip', 'excepts')
17PYPY = hasattr(sys, 'pypy_version_info')
20def identity(x):
21 """ Identity function. Return x
23 >>> identity(3)
24 3
25 """
26 return x
29def apply(*func_and_args, **kwargs):
30 """ Applies a function and returns the results
32 >>> def double(x): return 2*x
33 >>> def inc(x): return x + 1
34 >>> apply(double, 5)
35 10
37 >>> tuple(map(apply, [double, inc, double], [10, 500, 8000]))
38 (20, 501, 16000)
39 """
40 if not func_and_args:
41 raise TypeError('func argument is required')
42 func, args = func_and_args[0], func_and_args[1:]
43 return func(*args, **kwargs)
46def thread_first(val, *forms):
47 """ Thread value through a sequence of functions/forms
49 >>> def double(x): return 2*x
50 >>> def inc(x): return x + 1
51 >>> thread_first(1, inc, double)
52 4
54 If the function expects more than one input you can specify those inputs
55 in a tuple. The value is used as the first input.
57 >>> def add(x, y): return x + y
58 >>> def pow(x, y): return x**y
59 >>> thread_first(1, (add, 4), (pow, 2)) # pow(add(1, 4), 2)
60 25
62 So in general
63 thread_first(x, f, (g, y, z))
64 expands to
65 g(f(x), y, z)
67 See Also:
68 thread_last
69 """
70 def evalform_front(val, form):
71 if callable(form):
72 return form(val)
73 if isinstance(form, tuple):
74 func, args = form[0], form[1:]
75 args = (val,) + args
76 return func(*args)
77 return reduce(evalform_front, forms, val)
80def thread_last(val, *forms):
81 """ Thread value through a sequence of functions/forms
83 >>> def double(x): return 2*x
84 >>> def inc(x): return x + 1
85 >>> thread_last(1, inc, double)
86 4
88 If the function expects more than one input you can specify those inputs
89 in a tuple. The value is used as the last input.
91 >>> def add(x, y): return x + y
92 >>> def pow(x, y): return x**y
93 >>> thread_last(1, (add, 4), (pow, 2)) # pow(2, add(4, 1))
94 32
96 So in general
97 thread_last(x, f, (g, y, z))
98 expands to
99 g(y, z, f(x))
101 >>> def iseven(x):
102 ... return x % 2 == 0
103 >>> list(thread_last([1, 2, 3], (map, inc), (filter, iseven)))
104 [2, 4]
106 See Also:
107 thread_first
108 """
109 def evalform_back(val, form):
110 if callable(form):
111 return form(val)
112 if isinstance(form, tuple):
113 func, args = form[0], form[1:]
114 args = args + (val,)
115 return func(*args)
116 return reduce(evalform_back, forms, val)
119def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None):
120 """ Like @property, but returns ``classval`` when used as a class attribute
122 >>> class MyClass(object):
123 ... '''The class docstring'''
124 ... @instanceproperty(classval=__doc__)
125 ... def __doc__(self):
126 ... return 'An object docstring'
127 ... @instanceproperty
128 ... def val(self):
129 ... return 42
130 ...
131 >>> MyClass.__doc__
132 'The class docstring'
133 >>> MyClass.val is None
134 True
135 >>> obj = MyClass()
136 >>> obj.__doc__
137 'An object docstring'
138 >>> obj.val
139 42
140 """
141 if fget is None:
142 return partial(instanceproperty, fset=fset, fdel=fdel, doc=doc,
143 classval=classval)
144 return InstanceProperty(fget=fget, fset=fset, fdel=fdel, doc=doc,
145 classval=classval)
148class InstanceProperty(property):
149 """ Like @property, but returns ``classval`` when used as a class attribute
151 Should not be used directly. Use ``instanceproperty`` instead.
152 """
153 def __init__(self, fget=None, fset=None, fdel=None, doc=None,
154 classval=None):
155 self.classval = classval
156 property.__init__(self, fget=fget, fset=fset, fdel=fdel, doc=doc)
158 def __get__(self, obj, type=None):
159 if obj is None:
160 return self.classval
161 return property.__get__(self, obj, type)
163 def __reduce__(self):
164 state = (self.fget, self.fset, self.fdel, self.__doc__, self.classval)
165 return InstanceProperty, state
168class curry(object):
169 """ Curry a callable function
171 Enables partial application of arguments through calling a function with an
172 incomplete set of arguments.
174 >>> def mul(x, y):
175 ... return x * y
176 >>> mul = curry(mul)
178 >>> double = mul(2)
179 >>> double(10)
180 20
182 Also supports keyword arguments
184 >>> @curry # Can use curry as a decorator
185 ... def f(x, y, a=10):
186 ... return a * (x + y)
188 >>> add = f(a=1)
189 >>> add(2, 3)
190 5
192 See Also:
193 toolz.curried - namespace of curried functions
194 https://toolz.readthedocs.io/en/latest/curry.html
195 """
196 def __init__(self, *args, **kwargs):
197 if not args:
198 raise TypeError('__init__() takes at least 2 arguments (1 given)')
199 func, args = args[0], args[1:]
200 if not callable(func):
201 raise TypeError("Input must be callable")
203 # curry- or functools.partial-like object? Unpack and merge arguments
204 if (
205 hasattr(func, 'func')
206 and hasattr(func, 'args')
207 and hasattr(func, 'keywords')
208 and isinstance(func.args, tuple)
209 ):
210 _kwargs = {}
211 if func.keywords:
212 _kwargs.update(func.keywords)
213 _kwargs.update(kwargs)
214 kwargs = _kwargs
215 args = func.args + args
216 func = func.func
218 if kwargs:
219 self._partial = partial(func, *args, **kwargs)
220 else:
221 self._partial = partial(func, *args)
223 self.__doc__ = getattr(func, '__doc__', None)
224 self.__name__ = getattr(func, '__name__', '<curry>')
225 self.__module__ = getattr(func, '__module__', None)
226 self.__qualname__ = getattr(func, '__qualname__', None)
227 self._sigspec = None
228 self._has_unknown_args = None
230 @instanceproperty
231 def func(self):
232 return self._partial.func
234 @instanceproperty
235 def __signature__(self):
236 sig = inspect.signature(self.func)
237 args = self.args or ()
238 keywords = self.keywords or {}
239 if is_partial_args(self.func, args, keywords, sig) is False:
240 raise TypeError('curry object has incorrect arguments')
242 params = list(sig.parameters.values())
243 skip = 0
244 for param in params[:len(args)]:
245 if param.kind == param.VAR_POSITIONAL:
246 break
247 skip += 1
249 kwonly = False
250 newparams = []
251 for param in params[skip:]:
252 kind = param.kind
253 default = param.default
254 if kind == param.VAR_KEYWORD:
255 pass
256 elif kind == param.VAR_POSITIONAL:
257 if kwonly:
258 continue
259 elif param.name in keywords:
260 default = keywords[param.name]
261 kind = param.KEYWORD_ONLY
262 kwonly = True
263 else:
264 if kwonly:
265 kind = param.KEYWORD_ONLY
266 if default is param.empty:
267 default = no_default
268 newparams.append(param.replace(default=default, kind=kind))
270 return sig.replace(parameters=newparams)
272 @instanceproperty
273 def args(self):
274 return self._partial.args
276 @instanceproperty
277 def keywords(self):
278 return self._partial.keywords
280 @instanceproperty
281 def func_name(self):
282 return self.__name__
284 def __str__(self):
285 return str(self.func)
287 def __repr__(self):
288 return repr(self.func)
290 def __hash__(self):
291 return hash((self.func, self.args,
292 frozenset(self.keywords.items()) if self.keywords
293 else None))
295 def __eq__(self, other):
296 return (isinstance(other, curry) and self.func == other.func and
297 self.args == other.args and self.keywords == other.keywords)
299 def __ne__(self, other):
300 return not self.__eq__(other)
302 def __call__(self, *args, **kwargs):
303 try:
304 return self._partial(*args, **kwargs)
305 except TypeError as exc:
306 if self._should_curry(args, kwargs, exc):
307 return self.bind(*args, **kwargs)
308 raise
310 def _should_curry(self, args, kwargs, exc=None):
311 func = self.func
312 args = self.args + args
313 if self.keywords:
314 kwargs = dict(self.keywords, **kwargs)
315 if self._sigspec is None:
316 sigspec = self._sigspec = _sigs.signature_or_spec(func)
317 self._has_unknown_args = has_varargs(func, sigspec) is not False
318 else:
319 sigspec = self._sigspec
321 if is_partial_args(func, args, kwargs, sigspec) is False:
322 # Nothing can make the call valid
323 return False
324 elif self._has_unknown_args:
325 # The call may be valid and raised a TypeError, but we curry
326 # anyway because the function may have `*args`. This is useful
327 # for decorators with signature `func(*args, **kwargs)`.
328 return True
329 elif not is_valid_args(func, args, kwargs, sigspec):
330 # Adding more arguments may make the call valid
331 return True
332 else:
333 # There was a genuine TypeError
334 return False
336 def bind(self, *args, **kwargs):
337 return type(self)(self, *args, **kwargs)
339 def call(self, *args, **kwargs):
340 return self._partial(*args, **kwargs)
342 def __get__(self, instance, owner):
343 if instance is None:
344 return self
345 return curry(self, instance)
347 def __reduce__(self):
348 func = self.func
349 modname = getattr(func, '__module__', None)
350 qualname = getattr(func, '__qualname__', None)
351 if qualname is None: # pragma: no cover
352 qualname = getattr(func, '__name__', None)
353 is_decorated = None
354 if modname and qualname:
355 attrs = []
356 obj = import_module(modname)
357 for attr in qualname.split('.'):
358 if isinstance(obj, curry):
359 attrs.append('func')
360 obj = obj.func
361 obj = getattr(obj, attr, None)
362 if obj is None:
363 break
364 attrs.append(attr)
365 if isinstance(obj, curry) and obj.func is func:
366 is_decorated = obj is self
367 qualname = '.'.join(attrs)
368 func = '%s:%s' % (modname, qualname)
370 # functools.partial objects can't be pickled
371 userdict = tuple((k, v) for k, v in self.__dict__.items()
372 if k not in ('_partial', '_sigspec'))
373 state = (type(self), func, self.args, self.keywords, userdict,
374 is_decorated)
375 return _restore_curry, state
378def _restore_curry(cls, func, args, kwargs, userdict, is_decorated):
379 if isinstance(func, str):
380 modname, qualname = func.rsplit(':', 1)
381 obj = import_module(modname)
382 for attr in qualname.split('.'):
383 obj = getattr(obj, attr)
384 if is_decorated:
385 return obj
386 func = obj.func
387 obj = cls(func, *args, **(kwargs or {}))
388 obj.__dict__.update(userdict)
389 return obj
392@curry
393def memoize(func, cache=None, key=None):
394 """ Cache a function's result for speedy future evaluation
396 Considerations:
397 Trades memory for speed.
398 Only use on pure functions.
400 >>> def add(x, y): return x + y
401 >>> add = memoize(add)
403 Or use as a decorator
405 >>> @memoize
406 ... def add(x, y):
407 ... return x + y
409 Use the ``cache`` keyword to provide a dict-like object as an initial cache
411 >>> @memoize(cache={(1, 2): 3})
412 ... def add(x, y):
413 ... return x + y
415 Note that the above works as a decorator because ``memoize`` is curried.
417 It is also possible to provide a ``key(args, kwargs)`` function that
418 calculates keys used for the cache, which receives an ``args`` tuple and
419 ``kwargs`` dict as input, and must return a hashable value. However,
420 the default key function should be sufficient most of the time.
422 >>> # Use key function that ignores extraneous keyword arguments
423 >>> @memoize(key=lambda args, kwargs: args)
424 ... def add(x, y, verbose=False):
425 ... if verbose:
426 ... print('Calculating %s + %s' % (x, y))
427 ... return x + y
428 """
429 if cache is None:
430 cache = {}
432 try:
433 may_have_kwargs = has_keywords(func) is not False
434 # Is unary function (single arg, no variadic argument or keywords)?
435 is_unary = is_arity(1, func)
436 except TypeError: # pragma: no cover
437 may_have_kwargs = True
438 is_unary = False
440 if key is None:
441 if is_unary:
442 def key(args, kwargs):
443 return args[0]
444 elif may_have_kwargs:
445 def key(args, kwargs):
446 return (
447 args or None,
448 frozenset(kwargs.items()) if kwargs else None,
449 )
450 else:
451 def key(args, kwargs):
452 return args
454 def memof(*args, **kwargs):
455 k = key(args, kwargs)
456 try:
457 return cache[k]
458 except TypeError:
459 raise TypeError("Arguments to memoized function must be hashable")
460 except KeyError:
461 cache[k] = result = func(*args, **kwargs)
462 return result
464 try:
465 memof.__name__ = func.__name__
466 except AttributeError:
467 pass
468 memof.__doc__ = func.__doc__
469 memof.__wrapped__ = func
470 return memof
473class Compose(object):
474 """ A composition of functions
476 See Also:
477 compose
478 """
479 __slots__ = 'first', 'funcs'
481 def __init__(self, funcs):
482 funcs = tuple(reversed(funcs))
483 self.first = funcs[0]
484 self.funcs = funcs[1:]
486 def __call__(self, *args, **kwargs):
487 ret = self.first(*args, **kwargs)
488 for f in self.funcs:
489 ret = f(ret)
490 return ret
492 def __getstate__(self):
493 return self.first, self.funcs
495 def __setstate__(self, state):
496 self.first, self.funcs = state
498 @instanceproperty(classval=__doc__)
499 def __doc__(self):
500 def composed_doc(*fs):
501 """Generate a docstring for the composition of fs.
502 """
503 if not fs:
504 # Argument name for the docstring.
505 return '*args, **kwargs'
507 return '{f}({g})'.format(f=fs[0].__name__, g=composed_doc(*fs[1:]))
509 try:
510 return (
511 'lambda *args, **kwargs: ' +
512 composed_doc(*reversed((self.first,) + self.funcs))
513 )
514 except AttributeError:
515 # One of our callables does not have a `__name__`, whatever.
516 return 'A composition of functions'
518 @property
519 def __name__(self):
520 try:
521 return '_of_'.join(
522 (f.__name__ for f in reversed((self.first,) + self.funcs))
523 )
524 except AttributeError:
525 return type(self).__name__
527 def __repr__(self):
528 return '{.__class__.__name__}{!r}'.format(
529 self, tuple(reversed((self.first, ) + self.funcs)))
531 def __eq__(self, other):
532 if isinstance(other, Compose):
533 return other.first == self.first and other.funcs == self.funcs
534 return NotImplemented
536 def __ne__(self, other):
537 equality = self.__eq__(other)
538 return NotImplemented if equality is NotImplemented else not equality
540 def __hash__(self):
541 return hash(self.first) ^ hash(self.funcs)
543 # Mimic the descriptor behavior of python functions.
544 # i.e. let Compose be called as a method when bound to a class.
545 # adapted from
546 # docs.python.org/3/howto/descriptor.html#functions-and-methods
547 def __get__(self, obj, objtype=None):
548 return self if obj is None else MethodType(self, obj)
550 # introspection with Signature is only possible from py3.3+
551 @instanceproperty
552 def __signature__(self):
553 base = inspect.signature(self.first)
554 last = inspect.signature(self.funcs[-1])
555 return base.replace(return_annotation=last.return_annotation)
557 __wrapped__ = instanceproperty(attrgetter('first'))
560def compose(*funcs):
561 """ Compose functions to operate in series.
563 Returns a function that applies other functions in sequence.
565 Functions are applied from right to left so that
566 ``compose(f, g, h)(x, y)`` is the same as ``f(g(h(x, y)))``.
568 If no arguments are provided, the identity function (f(x) = x) is returned.
570 >>> inc = lambda i: i + 1
571 >>> compose(str, inc)(3)
572 '4'
574 See Also:
575 compose_left
576 pipe
577 """
578 if not funcs:
579 return identity
580 if len(funcs) == 1:
581 return funcs[0]
582 else:
583 return Compose(funcs)
586def compose_left(*funcs):
587 """ Compose functions to operate in series.
589 Returns a function that applies other functions in sequence.
591 Functions are applied from left to right so that
592 ``compose_left(f, g, h)(x, y)`` is the same as ``h(g(f(x, y)))``.
594 If no arguments are provided, the identity function (f(x) = x) is returned.
596 >>> inc = lambda i: i + 1
597 >>> compose_left(inc, str)(3)
598 '4'
600 See Also:
601 compose
602 pipe
603 """
604 return compose(*reversed(funcs))
607def pipe(data, *funcs):
608 """ Pipe a value through a sequence of functions
610 I.e. ``pipe(data, f, g, h)`` is equivalent to ``h(g(f(data)))``
612 We think of the value as progressing through a pipe of several
613 transformations, much like pipes in UNIX
615 ``$ cat data | f | g | h``
617 >>> double = lambda i: 2 * i
618 >>> pipe(3, double, str)
619 '6'
621 See Also:
622 compose
623 compose_left
624 thread_first
625 thread_last
626 """
627 for func in funcs:
628 data = func(data)
629 return data
632def complement(func):
633 """ Convert a predicate function to its logical complement.
635 In other words, return a function that, for inputs that normally
636 yield True, yields False, and vice-versa.
638 >>> def iseven(n): return n % 2 == 0
639 >>> isodd = complement(iseven)
640 >>> iseven(2)
641 True
642 >>> isodd(2)
643 False
644 """
645 return compose(not_, func)
648class juxt(object):
649 """ Creates a function that calls several functions with the same arguments
651 Takes several functions and returns a function that applies its arguments
652 to each of those functions then returns a tuple of the results.
654 Name comes from juxtaposition: the fact of two things being seen or placed
655 close together with contrasting effect.
657 >>> inc = lambda x: x + 1
658 >>> double = lambda x: x * 2
659 >>> juxt(inc, double)(10)
660 (11, 20)
661 >>> juxt([inc, double])(10)
662 (11, 20)
663 """
664 __slots__ = ['funcs']
666 def __init__(self, *funcs):
667 if len(funcs) == 1 and not callable(funcs[0]):
668 funcs = funcs[0]
669 self.funcs = tuple(funcs)
671 def __call__(self, *args, **kwargs):
672 return tuple(func(*args, **kwargs) for func in self.funcs)
674 def __getstate__(self):
675 return self.funcs
677 def __setstate__(self, state):
678 self.funcs = state
681def do(func, x):
682 """ Runs ``func`` on ``x``, returns ``x``
684 Because the results of ``func`` are not returned, only the side
685 effects of ``func`` are relevant.
687 Logging functions can be made by composing ``do`` with a storage function
688 like ``list.append`` or ``file.write``
690 >>> from toolz import compose
691 >>> from toolz.curried import do
693 >>> log = []
694 >>> inc = lambda x: x + 1
695 >>> inc = compose(inc, do(log.append))
696 >>> inc(1)
697 2
698 >>> inc(11)
699 12
700 >>> log
701 [1, 11]
702 """
703 func(x)
704 return x
707@curry
708def flip(func, a, b):
709 """ Call the function call with the arguments flipped
711 This function is curried.
713 >>> def div(a, b):
714 ... return a // b
715 ...
716 >>> flip(div, 2, 6)
717 3
718 >>> div_by_two = flip(div, 2)
719 >>> div_by_two(4)
720 2
722 This is particularly useful for built in functions and functions defined
723 in C extensions that accept positional only arguments. For example:
724 isinstance, issubclass.
726 >>> data = [1, 'a', 'b', 2, 1.5, object(), 3]
727 >>> only_ints = list(filter(flip(isinstance, int), data))
728 >>> only_ints
729 [1, 2, 3]
730 """
731 return func(b, a)
734def return_none(exc):
735 """ Returns None.
736 """
737 return None
740class excepts(object):
741 """A wrapper around a function to catch exceptions and
742 dispatch to a handler.
744 This is like a functional try/except block, in the same way that
745 ifexprs are functional if/else blocks.
747 Examples
748 --------
749 >>> excepting = excepts(
750 ... ValueError,
751 ... lambda a: [1, 2].index(a),
752 ... lambda _: -1,
753 ... )
754 >>> excepting(1)
755 0
756 >>> excepting(3)
757 -1
759 Multiple exceptions and default except clause.
761 >>> excepting = excepts((IndexError, KeyError), lambda a: a[0])
762 >>> excepting([])
763 >>> excepting([1])
764 1
765 >>> excepting({})
766 >>> excepting({0: 1})
767 1
768 """
769 def __init__(self, exc, func, handler=return_none):
770 self.exc = exc
771 self.func = func
772 self.handler = handler
774 def __call__(self, *args, **kwargs):
775 try:
776 return self.func(*args, **kwargs)
777 except self.exc as e:
778 return self.handler(e)
780 @instanceproperty(classval=__doc__)
781 def __doc__(self):
782 from textwrap import dedent
784 exc = self.exc
785 try:
786 if isinstance(exc, tuple):
787 exc_name = '(%s)' % ', '.join(
788 map(attrgetter('__name__'), exc),
789 )
790 else:
791 exc_name = exc.__name__
793 return dedent(
794 """\
795 A wrapper around {inst.func.__name__!r} that will except:
796 {exc}
797 and handle any exceptions with {inst.handler.__name__!r}.
799 Docs for {inst.func.__name__!r}:
800 {inst.func.__doc__}
802 Docs for {inst.handler.__name__!r}:
803 {inst.handler.__doc__}
804 """
805 ).format(
806 inst=self,
807 exc=exc_name,
808 )
809 except AttributeError:
810 return type(self).__doc__
812 @property
813 def __name__(self):
814 exc = self.exc
815 try:
816 if isinstance(exc, tuple):
817 exc_name = '_or_'.join(map(attrgetter('__name__'), exc))
818 else:
819 exc_name = exc.__name__
820 return '%s_excepting_%s' % (self.func.__name__, exc_name)
821 except AttributeError:
822 return 'excepting'
825def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
826 if sigspec is None:
827 try:
828 sigspec = inspect.signature(func)
829 except (ValueError, TypeError) as e:
830 sigspec = e
831 if isinstance(sigspec, ValueError):
832 return None, builtin_func(*builtin_args)
833 elif not isinstance(sigspec, inspect.Signature):
834 if (
835 func in _sigs.signatures
836 and ((
837 hasattr(func, '__signature__')
838 and hasattr(func.__signature__, '__get__')
839 ))
840 ):
841 val = builtin_func(*builtin_args)
842 return None, val
843 return None, False
844 return sigspec, None
847if PYPY: # pragma: no cover
848 _check_sigspec_orig = _check_sigspec
850 def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
851 # PyPy may lie, so use our registry for builtins instead
852 if func in _sigs.signatures:
853 val = builtin_func(*builtin_args)
854 return None, val
855 return _check_sigspec_orig(sigspec, func, builtin_func, *builtin_args)
858_check_sigspec.__doc__ = """ \
859Private function to aid in introspection compatibly across Python versions.
861If a callable doesn't have a signature (Python 3) or an argspec (Python 2),
862the signature registry in toolz._signatures is used.
863"""
866def num_required_args(func, sigspec=None):
867 sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args,
868 func)
869 if sigspec is None:
870 return rv
871 return sum(1 for p in sigspec.parameters.values()
872 if p.default is p.empty
873 and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY))
876def has_varargs(func, sigspec=None):
877 sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func)
878 if sigspec is None:
879 return rv
880 return any(p.kind == p.VAR_POSITIONAL
881 for p in sigspec.parameters.values())
884def has_keywords(func, sigspec=None):
885 sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func)
886 if sigspec is None:
887 return rv
888 return any(p.default is not p.empty
889 or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD)
890 for p in sigspec.parameters.values())
893def is_valid_args(func, args, kwargs, sigspec=None):
894 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args,
895 func, args, kwargs)
896 if sigspec is None:
897 return rv
898 try:
899 sigspec.bind(*args, **kwargs)
900 except TypeError:
901 return False
902 return True
905def is_partial_args(func, args, kwargs, sigspec=None):
906 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args,
907 func, args, kwargs)
908 if sigspec is None:
909 return rv
910 try:
911 sigspec.bind_partial(*args, **kwargs)
912 except TypeError:
913 return False
914 return True
917def is_arity(n, func, sigspec=None):
918 """ Does a function have only n positional arguments?
920 This function relies on introspection and does not call the function.
921 Returns None if validity can't be determined.
923 >>> def f(x):
924 ... return x
925 >>> is_arity(1, f)
926 True
927 >>> def g(x, y=1):
928 ... return x + y
929 >>> is_arity(1, g)
930 False
931 """
932 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_arity, n, func)
933 if sigspec is None:
934 return rv
935 num = num_required_args(func, sigspec)
936 if num is not None:
937 num = num == n
938 if not num:
939 return False
940 varargs = has_varargs(func, sigspec)
941 if varargs:
942 return False
943 keywords = has_keywords(func, sigspec)
944 if keywords:
945 return False
946 if num is None or varargs is None or keywords is None: # pragma: no cover
947 return None
948 return True
951num_required_args.__doc__ = """ \
952Number of required positional arguments
954 This function relies on introspection and does not call the function.
955 Returns None if validity can't be determined.
957 >>> def f(x, y, z=3):
958 ... return x + y + z
959 >>> num_required_args(f)
960 2
961 >>> def g(*args, **kwargs):
962 ... pass
963 >>> num_required_args(g)
964 0
965 """
967has_varargs.__doc__ = """ \
968Does a function have variadic positional arguments?
970 This function relies on introspection and does not call the function.
971 Returns None if validity can't be determined.
973 >>> def f(*args):
974 ... return args
975 >>> has_varargs(f)
976 True
977 >>> def g(**kwargs):
978 ... return kwargs
979 >>> has_varargs(g)
980 False
981 """
983has_keywords.__doc__ = """ \
984Does a function have keyword arguments?
986 This function relies on introspection and does not call the function.
987 Returns None if validity can't be determined.
989 >>> def f(x, y=0):
990 ... return x + y
992 >>> has_keywords(f)
993 True
994 """
996is_valid_args.__doc__ = """ \
997Is ``func(*args, **kwargs)`` a valid function call?
999 This function relies on introspection and does not call the function.
1000 Returns None if validity can't be determined.
1002 >>> def add(x, y):
1003 ... return x + y
1005 >>> is_valid_args(add, (1,), {})
1006 False
1007 >>> is_valid_args(add, (1, 2), {})
1008 True
1009 >>> is_valid_args(map, (), {})
1010 False
1012 **Implementation notes**
1013 Python 2 relies on ``inspect.getargspec``, which only works for
1014 user-defined functions. Python 3 uses ``inspect.signature``, which
1015 works for many more types of callables.
1017 Many builtins in the standard library are also supported.
1018 """
1020is_partial_args.__doc__ = """ \
1021Can partial(func, *args, **kwargs)(*args2, **kwargs2) be a valid call?
1023 Returns True *only* if the call is valid or if it is possible for the
1024 call to become valid by adding more positional or keyword arguments.
1026 This function relies on introspection and does not call the function.
1027 Returns None if validity can't be determined.
1029 >>> def add(x, y):
1030 ... return x + y
1032 >>> is_partial_args(add, (1,), {})
1033 True
1034 >>> is_partial_args(add, (1, 2), {})
1035 True
1036 >>> is_partial_args(add, (1, 2, 3), {})
1037 False
1038 >>> is_partial_args(map, (), {})
1039 True
1041 **Implementation notes**
1042 Python 2 relies on ``inspect.getargspec``, which only works for
1043 user-defined functions. Python 3 uses ``inspect.signature``, which
1044 works for many more types of callables.
1046 Many builtins in the standard library are also supported.
1047 """
1049from . import _signatures as _sigs