Source code for phantom.fn

from __future__ import annotations

import functools
from functools import partial
from typing import Any
from typing import Callable
from typing import TypeVar


def _name(fn: Callable) -> str:
    if isinstance(fn, partial):
        fn = fn.func
    try:
        return fn.__qualname__
    except AttributeError:
        return str(fn)


AA = TypeVar("AA")
AR = TypeVar("AR")
BA = TypeVar("BA")


[docs]def compose2(a: Callable[[AA], AR], b: Callable[[BA], AA]) -> Callable[[BA], AR]: """ Returns a function composed from the two given functions ``a`` and ``b`` such that calling ``compose2(a, b)(x)`` is equivalent to calling ``a(b(x))``. >>> compose2("".join, reversed)("!olleH") 'Hello!' """ a_name = _name(a) b_name = _name(b) def c(arg: BA) -> AR: return a(b(arg)) c.__name__ = f"{a_name}{b_name}" c.__doc__ = f"Function composed as {a_name}({b_name}(_))." return c
A = TypeVar("A")
[docs]def excepts( exception: tuple[type[Exception], ...] | type[Exception], negate: bool = False, ) -> Callable[[Callable[[A], Any]], Callable[[A], bool]]: """ Turn a unary function that raises an exception into a boolean predicate. >>> def validate_positive(number: int) -> None: ... if number < 0: raise ValueError >>> is_positive = excepts(ValueError)(validate_positive) >>> is_positive(0), is_positive(-1) (True, False) """ def decorator(fn: Callable[[A], Any]) -> Callable[[A], bool]: @functools.wraps(fn) def wrapper(arg: A) -> bool: try: fn(arg) except exception: return negate return not negate return wrapper return decorator