diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pydash/functions.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-4a52a71956a8d46fcb7294ac71734504bb09bcc2.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash/functions.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/pydash/functions.py | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/functions.py b/.venv/lib/python3.12/site-packages/pydash/functions.py new file mode 100644 index 00000000..30a8a607 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/functions.py @@ -0,0 +1,1461 @@ +""" +Functions that wrap other functions. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +from functools import cached_property +from inspect import getfullargspec +import itertools +import time +import typing as t + +from typing_extensions import Concatenate, Literal, ParamSpec, Protocol + +import pydash as pyd +from pydash.helpers import getargcount + + +__all__ = ( + "after", + "ary", + "before", + "conjoin", + "curry", + "curry_right", + "debounce", + "delay", + "disjoin", + "flip", + "flow", + "flow_right", + "iterated", + "juxtapose", + "negate", + "once", + "over_args", + "partial", + "partial_right", + "rearg", + "spread", + "throttle", + "unary", + "wrap", +) + +T = t.TypeVar("T") +T1 = t.TypeVar("T1") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") +T5 = t.TypeVar("T5") +P = ParamSpec("P") + + +class _WithArgCount(Protocol): + func: t.Callable[..., t.Any] + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.func, None) + + +class After(_WithArgCount, t.Generic[P, T]): + """Wrap a function in an after context.""" + + def __init__(self, func: t.Callable[P, T], n: t.SupportsInt) -> None: + try: + n = int(n) + assert n >= 0 + except (ValueError, TypeError, AssertionError): + n = 0 + + self.n = n + self.func = func + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> t.Union[T, None]: + """Return results of :attr:`func` after :attr:`n` calls.""" + self.n -= 1 + + if self.n <= 0: + return self.func(*args, **kwargs) + + return None + + +class Ary(_WithArgCount, t.Generic[T]): + """Wrap a function in an ary context.""" + + def __init__(self, func: t.Callable[..., T], n: t.Union[t.SupportsInt, None]) -> None: + try: + # Type error would be caught + n = int(n) # type: ignore + assert n >= 0 + except (ValueError, TypeError, AssertionError): + n = None + + self.n = n + self.func = func + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> T: + """ + Return results of :attr:`func` with arguments capped to :attr:`n`. + + Only positional arguments are capped. Any number of keyword arguments are allowed. + """ + cut_args = args[: self.n] if self.n is not None else args + + return self.func(*cut_args, **kwargs) # type: ignore + + +class Before(After[P, T], t.Generic[P, T]): + """Wrap a function in a before context.""" + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> t.Union[T, None]: + self.n -= 1 + + if self.n > 0: + return self.func(*args, **kwargs) + + return None + + +class Flow(t.Generic[P, T]): + """Wrap a function in a flow context.""" + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T5], + func5: t.Callable[[T5], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, func1: t.Callable[P, T2], func2: t.Callable[[T2], T], *, from_right: bool = True + ) -> None: ... + + @t.overload + def __init__(self, func1: t.Callable[P, T], *, from_right: bool = True) -> None: ... + + def __init__(self, *funcs, from_right: bool = True) -> None: # type: ignore + self.funcs = funcs + self._from_index = -1 if from_right else 0 + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results of composing :attr:`funcs`.""" + funcs = list(self.funcs) + + result = None + + while funcs: + result = funcs.pop(self._from_index)(*args, **kwargs) + # Incompatible type in assignements but needed here + # type safety is ensured from the `__init__` signature + args = (result,) # type: ignore + kwargs = {} # type: ignore + + # type safety is ensured from the `__init__` signature + return result # type: ignore + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.funcs[self._from_index], None) + + +class Conjoin(t.Generic[T]): + """Wrap a set of functions in a conjoin context.""" + + def __init__(self, *funcs: t.Callable[[T], t.Any]) -> None: + self.funcs = funcs + + def __call__(self, obj: t.Iterable[T]) -> bool: + """Return result of conjoin `obj` with :attr:`funcs` predicates.""" + + def iteratee(item: T) -> bool: + return pyd.every(self.funcs, lambda func: func(item)) + + return pyd.every(obj, iteratee) + + +class Curry(t.Generic[T1, T]): + """Wrap a function in a curry context.""" + + def __init__(self, func, arity, args=None, kwargs=None) -> None: + self.func = func + self.arity = len(getfullargspec(func).args) if arity is None else arity + self.args = () if args is None else args + self.kwargs = {} if kwargs is None else kwargs + + def __call__(self, *args, **kwargs): + """Store `args` and `kwargs` and call :attr:`func` if we've reached or exceeded the function + arity.""" + args = self.compose_args(args) + kwargs.update(self.kwargs) + + if (len(args) + len(kwargs)) >= self.arity: + args_arity = self.arity - len(kwargs) + args = args[: (args_arity if args_arity > 0 else 0)] + curried = self.func(*args, **kwargs) + else: + # NOTE: Use self.__class__ so that subclasses will use their own + # class to generate next iteration of call. + curried = self.__class__(self.func, self.arity, args, kwargs) + + return curried + + def compose_args(self, new_args): + """Combine `self.args` with `new_args` and return.""" + return tuple(list(self.args) + list(new_args)) + + @cached_property + def _argcount(self) -> t.Optional[int]: + argcount = self.arity - len(self.args) - len(self.kwargs) + return argcount if argcount >= 0 else None + + +class CurryOne(Curry[T1, T]): + def __call__(self, arg_one: T1) -> T: + return super().__call__(arg_one) # pragma: no cover + + +class CurryTwo(Curry[T1, CurryOne[T2, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryOne[T2, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryThree(Curry[T1, CurryTwo[T2, T3, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryTwo[T2, T3, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryOne[T3, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryFour(Curry[T1, CurryThree[T2, T3, T4, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryThree[T2, T3, T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryTwo[T3, T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> CurryOne[T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryFive(Curry[T1, CurryFour[T2, T3, T4, T5, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryFour[T2, T3, T4, T5, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryThree[T3, T4, T5, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> CurryTwo[T4, T5, T]: ... + + @t.overload + def __call__( + self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4 + ) -> CurryOne[T5, T]: ... + + @t.overload + def __call__( + self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4, arg_five: T5 + ) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRight(Curry[T5, T]): + """Wrap a function in a curry-right context.""" + + def compose_args(self, new_args): + return tuple(list(new_args) + list(self.args)) + + +class CurryRightOne(CurryRight[T5, T]): + def __call__(self, arg_one: T5) -> T: + return super().__call__(arg_one) # pragma: no cover + + +class CurryRightTwo(CurryRight[T5, CurryRightOne[T4, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightOne[T4, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightThree(CurryRight[T5, CurryRightTwo[T4, T3, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightTwo[T4, T3, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightOne[T3, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightFour(CurryRight[T5, CurryRightThree[T4, T3, T2, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightThree[T4, T3, T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightTwo[T3, T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> CurryRightOne[T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightFive(CurryRight[T5, CurryRightFour[T4, T3, T2, T1, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightFour[T4, T3, T2, T1, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightThree[T3, T2, T1, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> CurryRightTwo[T2, T1, T]: ... + + @t.overload + def __call__( + self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2 + ) -> CurryRightOne[T1, T]: ... + + @t.overload + def __call__( + self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2, arg_five: T1 + ) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class Debounce(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a debounce context.""" + + def __init__( + self, func: t.Callable[P, T], wait: int, max_wait: t.Union[int, Literal[False]] = False + ) -> None: + self.func = func + self.wait = wait + self.max_wait = max_wait + + self.last_result: t.Union[T, None] = None + + # Initialize last_* times to be prior to the wait periods so that func + # is primed to be executed on first call. + self.last_call = pyd.now() - self.wait + self.last_execution = pyd.now() - max_wait if pyd.is_number(max_wait) else None + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """ + Execute :attr:`func` if function hasn't been called within last :attr:`wait` milliseconds or + in last :attr:`max_wait` milliseconds. + + Return results of last successful call. + """ + present = pyd.now() + + if (present - self.last_call) >= self.wait or ( + self.max_wait and (present - self.last_execution) >= self.max_wait # type: ignore + ): + self.last_result = self.func(*args, **kwargs) + self.last_execution = present + + self.last_call = present + + # It will be set after first call, cannot be `None` anymore + return self.last_result # type: ignore + + +class Disjoin(t.Generic[T]): + """Wrap a set of functions in a disjoin context.""" + + def __init__(self, *funcs: t.Callable[[T], t.Any]) -> None: + self.funcs = funcs + + def __call__(self, obj: t.Iterable[T]) -> bool: + """Return result of disjoin `obj` with :attr:`funcs` predicates.""" + + def iteratee(item: T) -> bool: + return pyd.some(self.funcs, lambda func: func(item)) + + return pyd.some(obj, iteratee) + + +class Flip(_WithArgCount): + """Wrap a function in a flip context.""" + + def __init__(self, func: t.Callable[..., t.Any]) -> None: + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*reversed(args), **kwargs) + + +class Iterated(t.Generic[T]): + """Wrap a function in an iterated context.""" + + def __init__(self, func: t.Callable[[T], T]) -> None: + self.func = func + + def _iteration(self, initial: T) -> t.Iterator[T]: + """Iterator that composing :attr:`func` with itself.""" + value = initial + while True: + value = self.func(value) + yield value + + def __call__(self, initial: T, n: int) -> T: + """Return value of calling :attr:`func` `n` times using `initial` as seed value.""" + value = initial + iteration = self._iteration(value) + + for _ in range(n): + value = next(iteration) + + return value + + +class Juxtapose(t.Generic[P, T]): + """Wrap a function in a juxtapose context.""" + + def __init__(self, *funcs: t.Callable[P, T]) -> None: + self.funcs = funcs + + def __call__(self, *objs: P.args, **kwargs: P.kwargs) -> t.List[T]: + return [func(*objs, **kwargs) for func in self.funcs] + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.funcs[0], None) if self.funcs else None + + +class OverArgs(_WithArgCount): + """Wrap a function in an over_args context.""" + + def __init__(self, func: t.Callable[..., t.Any], *transforms: t.Callable[..., t.Any]) -> None: + self.func = func + self.transforms = pyd.flatten(transforms) + + def __call__(self, *args): + args = (self.transforms[idx](args) for idx, args in enumerate(args)) + return self.func(*args) + + +class Negate(_WithArgCount, t.Generic[P]): + """Wrap a function in a negate context.""" + + def __init__(self, func: t.Callable[P, t.Any]) -> None: + self.func = func + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> bool: + """Return negated results of calling :attr:`func`.""" + return not self.func(*args, **kwargs) + + +class Once(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a once context.""" + + def __init__(self, func: t.Callable[P, T]) -> None: + self.func = func + self.result: t.Union[T, None] = None + self.called = False + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results from the first call of :attr:`func`.""" + if not self.called: + self.result = self.func(*args, **kwargs) + self.called = True + + # At this point the result will be set, cannot be `None` anymore + return self.result # type: ignore + + +class Partial(_WithArgCount, t.Generic[T]): + """Wrap a function in a partial context.""" + + def __init__( + self, func: t.Callable[..., T], args: t.Any, kwargs: t.Any = None, from_right: bool = False + ) -> None: + self.func = func + self.args = args + self.kwargs = kwargs or {} + self.from_right = from_right + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> T: + """ + Return results from :attr:`func` with :attr:`args` + `args`. + + Apply arguments from left or right depending on :attr:`from_right`. + """ + if self.from_right: + args = itertools.chain(args, self.args) # type: ignore + else: + args = itertools.chain(self.args, args) # type: ignore + + kwargs = {**self.kwargs, **kwargs} + + return self.func(*args, **kwargs) + + @cached_property + def _argcount(self) -> t.Optional[int]: + func_argcount = getargcount(self.func, None) + if func_argcount is None: + return None + argcount = func_argcount - len(self.args) - len(self.kwargs) + return argcount if argcount >= 0 else None + + +class Rearg(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a rearg context.""" + + def __init__(self, func: t.Callable[P, T], *indexes: int) -> None: + self.func = func + + # Index `indexes` by the index value, so we can do a lookup mapping by walking the function + # arguments. + self.indexes = { + src_index: dest_index for dest_index, src_index in enumerate(pyd.flatten(indexes)) + } + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results from :attr:`func` using rearranged arguments.""" + reargs = {} + rest = [] + + # Walk arguments to ensure each one is added to the final argument list. + for src_index, arg in enumerate(args): + # NOTE: dest_index will range from 0 to len(indexes). + dest_index = self.indexes.get(src_index) + + if dest_index is not None: + # Remap argument index. + reargs[dest_index] = arg + else: + # Argumnet index is not contained in `indexes` so stick in the back. + rest.append(arg) + + args = itertools.chain((reargs[key] for key in sorted(reargs)), rest) # type: ignore + + return self.func(*args, **kwargs) + + +class Spread(t.Generic[T]): + """Wrap a function in a spread context.""" + + def __init__(self, func: t.Callable[..., T]) -> None: + self.func = func + + def __call__(self, args: t.Iterable[t.Any]) -> T: + """Return results from :attr:`func` using array of `args` provided.""" + return self.func(*args) + + +class Throttle(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a throttle context.""" + + def __init__(self, func: t.Callable[P, T], wait: int) -> None: + self.func = func + self.wait = wait + + self.last_result: t.Union[T, None] = None + self.last_execution = pyd.now() - self.wait + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """ + Execute :attr:`func` if function hasn't been called within last :attr:`wait` milliseconds. + + Return results of last successful call. + """ + present = pyd.now() + + if (present - self.last_execution) >= self.wait: + self.last_result = self.func(*args, **kwargs) + self.last_execution = present + + # The last result will be filled on first execution, so it is always `T` + return self.last_result # type: ignore + + +def after(func: t.Callable[P, T], n: t.SupportsInt) -> After[P, T]: + """ + Creates a function that executes `func`, with the arguments of the created function, only after + being called `n` times. + + Args: + func: Function to execute. + n: Number of times `func` must be called before it is executed. + + Returns: + Function wrapped in an :class:`After` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> after_func = after(func, 3) + >>> after_func(1, 2, 3) + >>> after_func(1, 2, 3) + >>> after_func(1, 2, 3) + (1, 2, 3) + >>> after_func(4, 5, 6) + (4, 5, 6) + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `func` first. + """ + return After(func, n) + + +def ary(func: t.Callable[..., T], n: t.Union[t.SupportsInt, None]) -> Ary[T]: + """ + Creates a function that accepts up to `n` arguments ignoring any additional arguments. Only + positional arguments are capped. All keyword arguments are allowed through. + + Args: + func: Function to cap arguments for. + n: Number of arguments to accept. + + Returns: + Function wrapped in an :class:`Ary` context. + + Example: + + >>> func = lambda a, b, c=0, d=5: (a, b, c, d) + >>> ary_func = ary(func, 2) + >>> ary_func(1, 2, 3, 4, 5, 6) + (1, 2, 0, 5) + >>> ary_func(1, 2, 3, 4, 5, 6, c=10, d=20) + (1, 2, 10, 20) + + .. versionadded:: 3.0.0 + """ + return Ary(func, n) + + +def before(func: t.Callable[P, T], n: t.SupportsInt) -> Before[P, T]: + """ + Creates a function that executes `func`, with the arguments of the created function, until it + has been called `n` times. + + Args: + func: Function to execute. + n: Number of times `func` may be executed. + + Returns: + Function wrapped in an :class:`Before` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> before_func = before(func, 3) + >>> before_func(1, 2, 3) + (1, 2, 3) + >>> before_func(1, 2, 3) + (1, 2, 3) + >>> before_func(1, 2, 3) + >>> before_func(1, 2, 3) + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `func` first. + """ + return Before(func, n) + + +def conjoin(*funcs: t.Callable[[T], t.Any]) -> t.Callable[[t.Iterable[T]], bool]: + """ + Creates a function that composes multiple predicate functions into a single predicate that tests + whether **all** elements of an object pass each predicate. + + Args: + *funcs: Function(s) to conjoin. + + Returns: + Function(s) wrapped in a :class:`Conjoin` context. + + Example: + + >>> conjoiner = conjoin(lambda x: isinstance(x, int), lambda x: x > 3) + >>> conjoiner([1, 2, 3]) + False + >>> conjoiner([1.0, 2, 1]) + False + >>> conjoiner([4.0, 5, 6]) + False + >>> conjoiner([4, 5, 6]) + True + + .. versionadded:: 2.0.0 + """ + return Conjoin(*funcs) + + +@t.overload +def curry(func: t.Callable[[T1], T], arity: t.Union[int, None] = None) -> CurryOne[T1, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2], T], arity: t.Union[int, None] = None +) -> CurryTwo[T1, T2, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3], T], arity: t.Union[int, None] = None +) -> CurryThree[T1, T2, T3, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3, T4], T], arity: t.Union[int, None] = None +) -> CurryFour[T1, T2, T3, T4, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3, T4, T5], T], arity: t.Union[int, None] = None +) -> CurryFive[T1, T2, T3, T4, T5, T]: ... + + +def curry(func, arity=None): + """ + Creates a function that accepts one or more arguments of `func` that when invoked either + executes `func` returning its result (if all `func` arguments have been provided) or returns a + function that accepts one or more of the remaining `func` arguments, and so on. + + Args: + func: Function to curry. + arity: Number of function arguments that can be accepted by curried + function. Default is to use the number of arguments that are accepted by `func`. + + Returns: + Function wrapped in a :class:`Curry` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> currier = curry(func) + >>> currier = currier(1) + >>> assert isinstance(currier, Curry) + >>> currier = currier(2) + >>> assert isinstance(currier, Curry) + >>> currier = currier(3) + >>> currier + (1, 2, 3) + + .. versionadded:: 1.0.0 + """ + return Curry(func, arity) + + +@t.overload +def curry_right( + func: t.Callable[[T1], T], arity: t.Union[int, None] = None +) -> CurryRightOne[T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2], T], arity: t.Union[int, None] = None +) -> CurryRightTwo[T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3], T], arity: t.Union[int, None] = None +) -> CurryRightThree[T3, T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3, T4], T], arity: t.Union[int, None] = None +) -> CurryRightFour[T4, T3, T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3, T4, T5], T], +) -> CurryRightFive[T5, T4, T3, T2, T1, T]: ... + + +def curry_right(func, arity=None): + """ + This method is like :func:`curry` except that arguments are applied to `func` in the manner of + :func:`partial_right` instead of :func:`partial`. + + Args: + func: Function to curry. + arity: Number of function arguments that can be accepted by curried + function. Default is to use the number of arguments that are accepted by `func`. + + Returns: + Function wrapped in a :class:`CurryRight` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> currier = curry_right(func) + >>> currier = currier(1) + >>> assert isinstance(currier, CurryRight) + >>> currier = currier(2) + >>> assert isinstance(currier, CurryRight) + >>> currier = currier(3) + >>> currier + (3, 2, 1) + + .. versionadded:: 1.1.0 + """ + return CurryRight(func, arity) + + +def debounce( + func: t.Callable[P, T], wait: int, max_wait: t.Union[int, Literal[False]] = False +) -> Debounce[P, T]: + """ + Creates a function that will delay the execution of `func` until after `wait` milliseconds have + elapsed since the last time it was invoked. Subsequent calls to the debounced function will + return the result of the last `func` call. + + Args: + func: Function to execute. + wait: Milliseconds to wait before executing `func`. + max_wait (optional): Maximum time to wait before executing `func`. + + Returns: + Function wrapped in a :class:`Debounce` context. + + .. versionadded:: 1.0.0 + """ + return Debounce(func, wait, max_wait=max_wait) + + +def delay(func: t.Callable[P, T], wait: int, *args: "P.args", **kwargs: "P.kwargs") -> T: + """ + Executes the `func` function after `wait` milliseconds. Additional arguments will be provided to + `func` when it is invoked. + + Args: + func: Function to execute. + wait: Milliseconds to wait before executing `func`. + *args: Arguments to pass to `func`. + **kwargs: Keyword arguments to pass to `func`. + + Returns: + Return from `func`. + + .. versionadded:: 1.0.0 + """ + time.sleep(wait / 1000.0) + return func(*args, **kwargs) + + +def disjoin(*funcs: t.Callable[[T], t.Any]) -> Disjoin[T]: + """ + Creates a function that composes multiple predicate functions into a single predicate that tests + whether **any** elements of an object pass each predicate. + + Args: + *funcs: Function(s) to disjoin. + + Returns: + Function(s) wrapped in a :class:`Disjoin` context. + + Example: + + >>> disjoiner = disjoin(lambda x: isinstance(x, float),\ + lambda x: isinstance(x, int)) + >>> disjoiner([1, '2', '3']) + True + >>> disjoiner([1.0, '2', '3']) + True + >>> disjoiner(['1', '2', '3']) + False + + .. versionadded:: 2.0.0 + """ + return Disjoin(*funcs) + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3, T4, T5], T]) -> t.Callable[[T5, T4, T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3, T4], T]) -> t.Callable[[T4, T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3], T]) -> t.Callable[[T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2], T]) -> t.Callable[[T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1], T]) -> t.Callable[[T1], T]: ... + + +def flip(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """ + Creates a function that invokes the method with arguments reversed. + + Args: + func: Function to flip arguments for. + + Returns: + Function wrapped in a :class:`Flip` context. + + Example: + + >>> flipped = flip(lambda *args: args) + >>> flipped(1, 2, 3, 4) + (4, 3, 2, 1) + >>> flipped = flip(lambda *args: [i * 2 for i in args]) + >>> flipped(1, 2, 3, 4) + [8, 6, 4, 2] + + .. versionadded:: 4.0.0 + """ + return Flip(func) + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T5], + func5: t.Callable[[T5], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow(func1: t.Callable[P, T2], func2: t.Callable[[T2], T]) -> Flow[P, T]: ... + + +@t.overload +def flow(func1: t.Callable[P, T]) -> Flow[P, T]: ... + + +def flow(*funcs): + """ + Creates a function that is the composition of the provided functions, where each successive + invocation is supplied the return value of the previous. For example, composing the functions + ``f()``, ``g()``, and ``h()`` produces ``h(g(f()))``. + + Args: + *funcs: Function(s) to compose. + + Returns: + Function(s) wrapped in a :class:`Flow` context. + + Example: + + >>> mult_5 = lambda x: x * 5 + >>> div_10 = lambda x: x / 10.0 + >>> pow_2 = lambda x: x**2 + >>> ops = flow(sum, mult_5, div_10, pow_2) + >>> ops([1, 2, 3, 4]) + 25.0 + + .. versionadded:: 2.0.0 + + .. versionchanged:: 2.3.1 + Added :func:`pipe` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``pipe``. + """ + return Flow(*funcs, from_right=False) + + +@t.overload +def flow_right( + func5: t.Callable[[T4], T], + func4: t.Callable[[T3], T4], + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right( + func4: t.Callable[[T3], T], + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right( + func3: t.Callable[[T2], T], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right(func2: t.Callable[[T1], T], func1: t.Callable[P, T1]) -> Flow[P, T]: ... + + +@t.overload +def flow_right(func1: t.Callable[P, T]) -> Flow[P, T]: ... + + +def flow_right(*funcs): + """ + This function is like :func:`flow` except that it creates a function that invokes the provided + functions from right to left. For example, composing the functions ``f()``, ``g()``, and ``h()`` + produces ``f(g(h()))``. + + Args: + *funcs: Function(s) to compose. + + Returns: + Function(s) wrapped in a :class:`Flow` context. + + Example: + + >>> mult_5 = lambda x: x * 5 + >>> div_10 = lambda x: x / 10.0 + >>> pow_2 = lambda x: x**2 + >>> ops = flow_right(mult_5, div_10, pow_2, sum) + >>> ops([1, 2, 3, 4]) + 50.0 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Added :func:`flow_right` and made :func:`compose` an alias. + + .. versionchanged:: 2.3.1 + Added :func:`pipe_right` as alias. + + .. versionchanged:: 4.0.0 + Removed aliases ``pipe_right`` and ``compose``. + """ + return Flow(*funcs, from_right=True) + + +def iterated(func: t.Callable[[T], T]) -> Iterated[T]: + """ + Creates a function that is composed with itself. Each call to the iterated function uses the + previous function call's result as input. Returned :class:`Iterated` instance can be called with + ``(initial, n)`` where `initial` is the initial value to seed `func` with and `n` is the number + of times to call `func`. + + Args: + func: Function to iterate. + + Returns: + Function wrapped in a :class:`Iterated` context. + + Example: + + >>> doubler = iterated(lambda x: x * 2) + >>> doubler(4, 5) + 128 + >>> doubler(3, 9) + 1536 + + .. versionadded:: 2.0.0 + """ + return Iterated(func) + + +def juxtapose(*funcs: t.Callable[P, T]) -> Juxtapose[P, T]: + """ + Creates a function whose return value is a list of the results of calling each `funcs` with the + supplied arguments. + + Args: + *funcs: Function(s) to juxtapose. + + Returns: + Function wrapped in a :class:`Juxtapose` context. + + Example: + + >>> double = lambda x: x * 2 + >>> triple = lambda x: x * 3 + >>> quadruple = lambda x: x * 4 + >>> juxtapose(double, triple, quadruple)(5) + [10, 15, 20] + + + .. versionadded:: 2.0.0 + """ + return Juxtapose(*funcs) + + +def negate(func: t.Callable[P, t.Any]) -> Negate[P]: + """ + Creates a function that negates the result of the predicate `func`. The `func` function is + executed with the arguments of the created function. + + Args: + func: Function to negate execute. + + Returns: + Function wrapped in a :class:`Negate` context. + + Example: + + >>> not_is_number = negate(lambda x: isinstance(x, (int, float))) + >>> not_is_number(1) + False + >>> not_is_number("1") + True + + .. versionadded:: 1.1.0 + """ + return Negate(func) + + +def once(func: t.Callable[P, T]) -> Once[P, T]: + """ + Creates a function that is restricted to execute `func` once. Repeat calls to the function will + return the value of the first call. + + Args: + func: Function to execute. + + Returns: + Function wrapped in a :class:`Once` context. + + Example: + + >>> oncer = once(lambda *args: args[0]) + >>> oncer(5) + 5 + >>> oncer(6) + 5 + + .. versionadded:: 1.0.0 + """ + return Once(func) + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3, T4, T5], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], + transform_five: t.Callable[[T5], T5], +) -> t.Callable[[T1, T2, T3, T4, T5], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3, T4], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], +) -> t.Callable[[T1, T2, T3, T4], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], +) -> t.Callable[[T1, T2, T3], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], +) -> t.Callable[[T1, T2], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1], T], + transform_one: t.Callable[[T1], T1], +) -> t.Callable[[T1], T]: ... + + +def over_args(func, *transforms): + """ + Creates a function that runs each argument through a corresponding transform function. + + Args: + func: Function to wrap. + *transforms: Functions to transform arguments, specified as individual functions + or lists of functions. + + Returns: + Function wrapped in a :class:`OverArgs` context. + + Example: + + >>> squared = lambda x: x**2 + >>> double = lambda x: x * 2 + >>> modder = over_args(lambda x, y: [x, y], squared, double) + >>> modder(5, 10) + [25, 20] + + .. versionadded:: 3.3.0 + + .. versionchanged:: 4.0.0 + Renamed from ``mod_args`` to ``over_args``. + """ + return OverArgs(func, *transforms) + + +def partial(func: t.Callable[..., T], *args: t.Any, **kwargs: t.Any) -> Partial[T]: + """ + Creates a function that, when called, invokes `func` with any additional partial arguments + prepended to those provided to the new function. + + Args: + func: Function to execute. + *args: Partial arguments to prepend to function call. + **kwargs: Partial keyword arguments to bind to function call. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> dropper = partial(lambda array, n: array[n:], [1, 2, 3, 4]) + >>> dropper(2) + [3, 4] + >>> dropper(1) + [2, 3, 4] + >>> myrest = partial(lambda array, n: array[n:], n=1) + >>> myrest([1, 2, 3, 4]) + [2, 3, 4] + + .. versionadded:: 1.0.0 + """ + return Partial(func, args, kwargs) + + +def partial_right(func: t.Callable[..., T], *args: t.Any, **kwargs: t.Any) -> Partial[T]: + """ + This method is like :func:`partial` except that partial arguments are appended to those provided + to the new function. + + Args: + func: Function to execute. + *args: Partial arguments to append to function call. + **kwargs: Partial keyword arguments to bind to function call. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> myrest = partial_right(lambda array, n: array[n:], 1) + >>> myrest([1, 2, 3, 4]) + [2, 3, 4] + + .. versionadded:: 1.0.0 + """ + return Partial(func, args, kwargs, from_right=True) + + +def rearg(func: t.Callable[P, T], *indexes: int) -> Rearg[P, T]: + """ + Creates a function that invokes `func` with arguments arranged according to the specified + indexes where the argument value at the first index is provided as the first argument, the + argument value at the second index is provided as the second argument, and so on. + + Args: + func: Function to rearrange arguments for. + *indexes: The arranged argument indexes. + + Returns: + Function wrapped in a :class:`Rearg` context. + + Example: + + >>> jumble = rearg(lambda *args: args, 1, 2, 3) + >>> jumble(1, 2, 3) + (2, 3, 1) + >>> jumble("a", "b", "c", "d", "e") + ('b', 'c', 'd', 'a', 'e') + + .. versionadded:: 3.0.0 + """ + return Rearg(func, *indexes) + + +def spread(func: t.Callable[..., T]) -> Spread[T]: + """ + Creates a function that invokes `func` with the array of arguments provided to the created + function. + + Args: + func: Function to spread. + + Returns: + Function wrapped in a :class:`Spread` context. + + Example: + + >>> greet = spread(lambda *people: "Hello " + ", ".join(people) + "!") + >>> greet(["Mike", "Don", "Leo"]) + 'Hello Mike, Don, Leo!' + + .. versionadded:: 3.1.0 + """ + return Spread(func) + + +def throttle(func: t.Callable[P, T], wait: int) -> Throttle[P, T]: + """ + Creates a function that, when executed, will only call the `func` function at most once per + every `wait` milliseconds. Subsequent calls to the throttled function will return the result of + the last `func` call. + + Args: + func: Function to throttle. + wait: Milliseconds to wait before calling `func` again. + + Returns: + Results of last `func` call. + + .. versionadded:: 1.0.0 + """ + return Throttle(func, wait) + + +def unary(func: t.Callable[..., T]) -> Ary[T]: + """ + Creates a function that accepts up to one argument, ignoring any additional arguments. + + Args: + func: Function to cap arguments for. + + Returns: + Function wrapped in an :class:`Ary` context. + + Example: + + >>> func = lambda a, b=1, c=0, d=5: (a, b, c, d) + >>> unary_func = unary(func) + >>> unary_func(1, 2, 3, 4, 5, 6) + (1, 1, 0, 5) + >>> unary_func(1, 2, 3, 4, 5, 6, b=0, c=10, d=20) + (1, 0, 10, 20) + + .. versionadded:: 4.0.0 + """ + return Ary(func, 1) + + +def wrap(value: T1, func: t.Callable[Concatenate[T1, P], T]) -> Partial[T]: + """ + Creates a function that provides value to the wrapper function as its first argument. Additional + arguments provided to the function are appended to those provided to the wrapper function. + + Args: + value: Value provided as first argument to function call. + func: Function to execute. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> wrapper = wrap("hello", lambda *args: args) + >>> wrapper(1, 2) + ('hello', 1, 2) + + .. versionadded:: 1.0.0 + """ + return Partial(func, (value,)) |