diff options
Diffstat (limited to 'tools/cru-py/cru/util')
-rw-r--r-- | tools/cru-py/cru/util/__init__.py | 14 | ||||
-rw-r--r-- | tools/cru-py/cru/util/_func.py | 150 | ||||
-rw-r--r-- | tools/cru-py/cru/util/_list.py | 111 | ||||
-rw-r--r-- | tools/cru-py/cru/util/_type.py | 42 |
4 files changed, 317 insertions, 0 deletions
diff --git a/tools/cru-py/cru/util/__init__.py b/tools/cru-py/cru/util/__init__.py new file mode 100644 index 0000000..83ab9f3 --- /dev/null +++ b/tools/cru-py/cru/util/__init__.py @@ -0,0 +1,14 @@ +from ._func import CruFunction, MetaFunction, RawFunctions, WrappedFunctions, PLACEHOLDER +from ._list import CruList, ListOperations, CanBeList +from ._type import TypeSet + +F = CruFunction +WF = WrappedFunctions +L = CruList + +__all__ = [ + "CruFunction", "MetaFunction", "RawFunctions", "WrappedFunctions", "PLACEHOLDER", + "CruList", "ListOperations", "CanBeList", + "TypeSet", + "F", "WF", "L", +] diff --git a/tools/cru-py/cru/util/_func.py b/tools/cru-py/cru/util/_func.py new file mode 100644 index 0000000..5fb49a9 --- /dev/null +++ b/tools/cru-py/cru/util/_func.py @@ -0,0 +1,150 @@ +from collections.abc import Callable, Iterable +from typing import TypeVar, Any, ParamSpec + +from ._list import ListOperations, CruList + +T = TypeVar("T") +R = TypeVar("R") +R1 = TypeVar("R1") +P = ParamSpec("P") +P1 = ParamSpec("P1") + + +class _Placeholder: + pass + + +PLACEHOLDER = _Placeholder() + + +class RawFunctions: + @staticmethod + def ignore(*_v, **_kwargs) -> None: + return None + + @staticmethod + def true_(*_v, **_kwargs) -> True: + return True + + @staticmethod + def false_(*_v, **_kwargs) -> False: + return False + + @staticmethod + def i_dont_care(r: T, *_v, **_kwargs) -> T: + return r + + @staticmethod + def identity(v: T) -> T: + return v + + @staticmethod + def equal(a: Any, b: Any) -> bool: + return a == b + + @staticmethod + def not_equal(a: Any, b: Any) -> bool: + return a != b + + @staticmethod + def not_(v): + return not v + + +class MetaFunction: + @staticmethod + def bind(f: Callable[P, R], *bind_args, **bind_kwargs) -> Callable[P1, R1]: + def bound(*args, **kwargs): + popped = 0 + real_args = [] + for a in bind_args: + if isinstance(a, _Placeholder): + real_args.append(args[popped]) + popped += 1 + else: + real_args.append(a) + real_args.extend(args[popped:]) + return f(*real_args, **(bind_kwargs | kwargs)) + + return bound + + @staticmethod + def chain(*fs: Callable) -> Callable: + if len(fs) == 0: + raise ValueError("At least one function is required!") + rf = fs[0] + for f in fs[1:]: + def n(*args, **kwargs): + r = rf(*args, **kwargs) + r = r if isinstance(r, tuple) else (r,) + return f(*r) + + rf = n + return rf + + @staticmethod + def chain_single(f: Callable[P, R], f1: Callable[P1, R1], *bind_args, **bind_kwargs) -> \ + Callable[ + P, R1]: + return MetaFunction.chain(f, MetaFunction.bind(f1, *bind_args, **bind_kwargs)) + + convert_r = chain_single + + @staticmethod + def neg(f: Callable[P, bool]) -> Callable[P, bool]: + return MetaFunction.convert_r(f, RawFunctions.not_) + + +# Advanced Function Wrapper +class CruFunction: + def __init__(self, f): + self._f = f + + @property + def f(self) -> Callable: + return self._f + + def bind(self, *bind_args, **bind_kwargs) -> "CruFunction": + self._f = MetaFunction.bind(self._f, *bind_args, **bind_kwargs) + return self + + def chain(self, *fs: Callable) -> "CruFunction": + self._f = MetaFunction.chain(self._f, *fs) + return self + + def chain_single(self, f: Callable[P, R], f1: Callable[P1, R1], *bind_args, + **bind_kwargs) -> "CruFunction": + self._f = MetaFunction.chain_single(self._f, f, f1, *bind_args, **bind_kwargs) + return self + + def convert_r(self, f: Callable[P, R], f1: Callable[P1, R1], *bind_args, + **bind_kwargs) -> "CruFunction": + self._f = MetaFunction.convert_r(self._f, f, f1, *bind_args, **bind_kwargs) + return self + + def neg(self) -> "CruFunction": + self._f = MetaFunction.neg(self._f) + return self + + def __call__(self, *args, **kwargs): + return self._f(*args, **kwargs) + + def list_transform(self, l: Iterable[T]) -> CruList[T]: + return CruList(l).transform(self) + + def list_all(self, l: Iterable[T]) -> bool: + return ListOperations.all(l, self) + + def list_any(self, l: Iterable[T]) -> bool: + return ListOperations.any(l, self) + + def list_remove_all_if(self, l: Iterable[T]) -> CruList[T]: + return CruList(ListOperations.remove_all_if(l, self)) + + +class WrappedFunctions: + identity = CruFunction(RawFunctions.identity) + ignore = CruFunction(RawFunctions.ignore) + equal = CruFunction(RawFunctions.equal) + not_equal = CruFunction(RawFunctions.not_equal) + not_ = CruFunction(RawFunctions.not_) diff --git a/tools/cru-py/cru/util/_list.py b/tools/cru-py/cru/util/_list.py new file mode 100644 index 0000000..3b4cb28 --- /dev/null +++ b/tools/cru-py/cru/util/_list.py @@ -0,0 +1,111 @@ +from collections.abc import Iterable, Callable +from typing import TypeVar, ParamSpec, Any, Generic + +T = TypeVar("T") +R = TypeVar("R") +P = ParamSpec("P") + +CanBeList = T | Iterable[T] | None + + +class ListOperations: + + @staticmethod + def foreach(l: Iterable[T], *f: Callable[[T], None] | None) -> None: + if len(f) == 0: return + for v in l: + for f_ in f: + if f_ is not None: + f_(v) + + @staticmethod + def transform(l: Iterable[T], *f: Callable | None) -> list[R]: + r = [] + for v in l: + for f_ in f: + if f_ is not None: + v = f_(v) + r.append(v) + return r + + @staticmethod + def transform_if(l: Iterable[T], f: Callable[[T], R] | None, p: Callable[[T], bool] | None) -> list[R]: + return [(f(v) if f else v) for v in l if (p(v) if p else True)] + + @staticmethod + def all(l: Iterable[T], *p: Callable[[T], bool] | None) -> bool: + if len(p) == 0 or all(v is None for v in p): + raise ValueError("At least one predicate is required!") + for v in l: + for p_ in p: + if p_ is not None and not p_(v): return False + return True + + @staticmethod + def all_is_instance(l: Iterable[T], *t: type) -> bool: + return all(type(v) in t for v in l) + + @staticmethod + def any(l: Iterable[T], *p: Callable[[T], bool] | None) -> bool: + if len(p) == 0 or all(v is None for v in p): + raise ValueError("At least one predicate is required!") + for v in l: + for p_ in p: + if p_ is not None and p_(v): return True + return False + + @staticmethod + def make(v: CanBeList[T], /, none_to_empty_list: bool = True) -> list[T]: + if v is None and none_to_empty_list: return [] + return v if isinstance(v, Iterable) else [v] + + @staticmethod + def remove_all_if(l: Iterable[T], *p: Callable[[], bool] | None) -> list[T]: + def stay(v): + for p_ in p: + if p_ is not None and p_(): return False + return True + + return [v for v in l if stay(v)] + + @staticmethod + def remove_all_value(l: Iterable[T], *r) -> list[T]: + return [v for v in l if v not in r] + + @staticmethod + def replace_all_value(l: Iterable[T], old_value: Any, new_value: R) -> list[T | R]: + return [new_value if v == old_value else v for v in l] + + +class CruList(list, Generic[T]): + + def foreach(self, *f: Callable[[T], None] | None) -> None: + ListOperations.foreach(self, *f) + + def transform(self, *f: Callable[[T], R] | None) -> "CruList"[R]: + return CruList(ListOperations.transform(self, *f)) + + def transform_if(self, f: Callable[[T], R] | None, p: Callable[[T], bool] | None) -> "CruList"[R]: + return CruList(ListOperations.transform_if(self, f, p)) + + def all(self, *p: Callable[[T], bool] | None) -> bool: + return ListOperations.all(self, *p) + + def all_is_instance(self, *t: type) -> bool: + return ListOperations.all_is_instance(self, *t) + + def any(self, *p: Callable[[T], bool] | None) -> bool: + return ListOperations.any(self, *p) + + def remove_all_if(self, *p: Callable[[]]) -> "CruList"[T]: + return CruList(ListOperations.remove_all_if(self, *p)) + + def remove_all_value(self, *r) -> "CruList"[T]: + return CruList(ListOperations.remove_all_value(self, *r)) + + def replace_all_value(self, old_value: Any, new_value: R) -> "CruList"[T | R]: + return CruList(ListOperations.replace_all_value(self, old_value, new_value)) + + @staticmethod + def make(l: CanBeList[T]) -> "CruList"[T]: + return CruList(ListOperations.make(l)) diff --git a/tools/cru-py/cru/util/_type.py b/tools/cru-py/cru/util/_type.py new file mode 100644 index 0000000..dc50def --- /dev/null +++ b/tools/cru-py/cru/util/_type.py @@ -0,0 +1,42 @@ +from types import NoneType +from typing import Any + +from ._list import CanBeList, CruList + +DEFAULT_NONE_ERR = ValueError +DEFAULT_NONE_ERR_MSG = "None is not allowed here." +DEFAULT_TYPE_ERR = ValueError +DEFAULT_TYPE_ERR_MSG = "Type of object is not allowed here." + + +class TypeSet(set[type]): + def __init__(self, *l: type): + l = CruList.make(l).remove_all_value(None, NoneType) + if not l.all_is_instance(type): + raise TypeError("t must be a type or None.") + super().__init__(l) + + def check_value(self, v: Any, /, allow_none: bool, empty_allow_all: bool = True, *, + none_err: type[Exception] = DEFAULT_NONE_ERR, + none_err_msg: str = DEFAULT_NONE_ERR_MSG, + type_err: type[Exception] = DEFAULT_TYPE_ERR, + type_err_msg: str = DEFAULT_TYPE_ERR_MSG) -> None: + if v is None: + if allow_none: + return + else: + raise none_err(none_err_msg) + if len(self) == 0 and empty_allow_all: + return + if type(v) not in self: + raise type_err(type_err_msg) + + def check_value_list(self, l: CanBeList, /, allow_none: bool, empty_allow_all: bool = True, *, + none_err: type[Exception] = DEFAULT_NONE_ERR, + none_err_msg: str = DEFAULT_NONE_ERR_MSG, + type_err: type[Exception] = DEFAULT_TYPE_ERR, + type_err_msg: str = DEFAULT_TYPE_ERR_MSG) -> None: + l = CruList.make(l) + for v in l: + self.check_value(v, allow_none, empty_allow_all, none_err=none_err, none_err_msg=none_err_msg, + type_err=type_err, type_err_msg=type_err_msg) |