aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cru-py/cru/util')
-rw-r--r--tools/cru-py/cru/util/__init__.py14
-rw-r--r--tools/cru-py/cru/util/_func.py150
-rw-r--r--tools/cru-py/cru/util/_list.py111
-rw-r--r--tools/cru-py/cru/util/_type.py42
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)