diff options
author | crupest <crupest@outlook.com> | 2024-11-11 01:12:29 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2024-12-18 18:31:27 +0800 |
commit | aaa855e3839130a79193f38969f07763f2773c5d (patch) | |
tree | e4cb238df4588f4633d9c1190136895865d51a98 /tools/cru-py/cru | |
parent | 95da3ade5bfa6ef39923cd3fc2a551ad983c1537 (diff) | |
download | crupest-aaa855e3839130a79193f38969f07763f2773c5d.tar.gz crupest-aaa855e3839130a79193f38969f07763f2773c5d.tar.bz2 crupest-aaa855e3839130a79193f38969f07763f2773c5d.zip |
HALF WORK: 2024.11.27
Diffstat (limited to 'tools/cru-py/cru')
-rw-r--r-- | tools/cru-py/cru/__init__.py | 1 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/__init__.py | 63 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_const.py | 49 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_cru.py (renamed from tools/cru-py/cru/util/_cru.py) | 22 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_event.py (renamed from tools/cru-py/cru/util/_event.py) | 0 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_func.py | 259 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_lang.py | 16 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_list.py (renamed from tools/cru-py/cru/util/_list.py) | 511 | ||||
-rw-r--r-- | tools/cru-py/cru/_util/_type.py (renamed from tools/cru-py/cru/util/_type.py) | 0 | ||||
-rw-r--r-- | tools/cru-py/cru/attr.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/excp.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/service/docker.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/util/__init__.py | 25 | ||||
-rw-r--r-- | tools/cru-py/cru/util/_const.py | 56 | ||||
-rw-r--r-- | tools/cru-py/cru/util/_func.py | 126 |
15 files changed, 731 insertions, 403 deletions
diff --git a/tools/cru-py/cru/__init__.py b/tools/cru-py/cru/__init__.py index e36a778..2ae241e 100644 --- a/tools/cru-py/cru/__init__.py +++ b/tools/cru-py/cru/__init__.py @@ -4,6 +4,7 @@ import sys class CruInitError(Exception): pass + def check_python_version(required_version=(3, 11)): if sys.version_info < required_version: raise CruInitError(f"Python version must be >= {required_version}!") diff --git a/tools/cru-py/cru/_util/__init__.py b/tools/cru-py/cru/_util/__init__.py new file mode 100644 index 0000000..481502c --- /dev/null +++ b/tools/cru-py/cru/_util/__init__.py @@ -0,0 +1,63 @@ +from ._const import ( + CruNotFound, + CruUseDefault, + CruDontChange, + CruNoValue, + CruPlaceholder, +) +from ._func import ( + CruFunction, + CruFunctionMeta, + CruRawFunctions, + CruWrappedFunctions, + CruFunctionGenerators, +) +from ._list import ( + CruList, + CruInplaceList, + CruUniqueKeyInplaceList, + ListOperations, + CanBeList, + ElementOperation, + ElementPredicate, + ElementTransformer, + OptionalElementOperation, + ElementPredicate, + OptionalElementTransformer, +) +from ._type import TypeSet + +F = CruFunction +WF = CruWrappedFunctions +FG = CruFunctionGenerators +L = CruList + + +__all__ = [ + "CruNotFound", + "CruUseDefault", + "CruDontChange", + "CruNoValue", + "CruPlaceholder", + "CruFunction", + "CruFunctionMeta", + "CruRawFunctions", + "CruWrappedFunctions", + "CruFunctionGenerators", + "CruList", + "CruInplaceList", + "CruUniqueKeyInplaceList", + "ListOperations", + "CanBeList", + "ElementOperation", + "ElementPredicate", + "ElementTransformer", + "OptionalElementOperation", + "ElementPredicate", + "OptionalElementTransformer", + "TypeSet", + "F", + "WF", + "FG", + "L", +] diff --git a/tools/cru-py/cru/_util/_const.py b/tools/cru-py/cru/_util/_const.py new file mode 100644 index 0000000..bc02c3a --- /dev/null +++ b/tools/cru-py/cru/_util/_const.py @@ -0,0 +1,49 @@ +from enum import Enum, auto +from typing import Self, TypeGuard, TypeVar + +from ._cru import CRU + +_T = TypeVar("_T") + + +class CruConstantBase(Enum): + @classmethod + def check(cls, v: _T | Self) -> TypeGuard[Self]: + return isinstance(v, cls) + + @classmethod + def check_not(cls, v: _T | Self) -> TypeGuard[_T]: + return not cls.check(v) + + @classmethod + def value(cls) -> Self: + return cls.VALUE # type: ignore + + +class CruNotFound(CruConstantBase): + VALUE = auto() + + +class CruUseDefault(CruConstantBase): + VALUE = auto() + + +class CruDontChange(CruConstantBase): + VALUE = auto() + + +class CruNoValue(CruConstantBase): + VALUE = auto() + + +class CruPlaceholder(CruConstantBase): + VALUE = auto() + + +CRU.add_objects( + CruNotFound, + CruUseDefault, + CruDontChange, + CruNoValue, + CruPlaceholder, +) diff --git a/tools/cru-py/cru/util/_cru.py b/tools/cru-py/cru/_util/_cru.py index 61a0ee1..0085a80 100644 --- a/tools/cru-py/cru/util/_cru.py +++ b/tools/cru-py/cru/_util/_cru.py @@ -1,10 +1,12 @@ from typing import Any +from ._lang import remove_none + class _Cru: NAME_PREFIXES = ("CRU_", "Cru", "cru_") - def __init__(self): + def __init__(self) -> None: self._d: dict[str, Any] = {} def all_names(self) -> list[str]: @@ -20,12 +22,13 @@ class _Cru: def _maybe_remove_prefix(name: str) -> str | None: for prefix in _Cru.NAME_PREFIXES: if name.startswith(prefix): - return name[len(prefix):] + return name[len(prefix) :] return None - def _check_name_exist(self, *names: str) -> None: + def _check_name_exist(self, *names: str | None) -> None: for name in names: - if name is None: continue + if name is None: + continue if self.has_name(name): raise ValueError(f"Name {name} exists in CRU.") @@ -41,13 +44,13 @@ class _Cru: return _Cru.check_name_format(o.__name__) def _do_add(self, o, *names: str | None) -> list[str]: - names = set(names) - names.remove(None) - for name in names: + name_list: list[str] = remove_none(names) + for name in name_list: self._d[name] = o - return list(names) + return name_list def add(self, o, name: str | None) -> tuple[str, str | None]: + no_prefix_name: str | None if name is None: name, no_prefix_name = self._check_object_name(o) else: @@ -58,7 +61,8 @@ class _Cru: return name, no_prefix_name def add_with_alias(self, o, name: str | None = None, *aliases: str) -> list[str]: - final_names = [] + final_names: list[str | None] = [] + no_prefix_name: str | None if name is None: name, no_prefix_name = self._check_object_name(o) self._check_name_exist(name, no_prefix_name) diff --git a/tools/cru-py/cru/util/_event.py b/tools/cru-py/cru/_util/_event.py index 813e33f..813e33f 100644 --- a/tools/cru-py/cru/util/_event.py +++ b/tools/cru-py/cru/_util/_event.py diff --git a/tools/cru-py/cru/_util/_func.py b/tools/cru-py/cru/_util/_func.py new file mode 100644 index 0000000..0b8b07a --- /dev/null +++ b/tools/cru-py/cru/_util/_func.py @@ -0,0 +1,259 @@ +from __future__ import annotations + +from collections.abc import Callable +from enum import Flag, auto +from typing import ( + Any, + Generic, + Iterable, + Literal, + ParamSpec, + TypeAlias, + TypeVar, + cast, +) + +from ._cru import CRU +from ._const import CruPlaceholder + +_P = ParamSpec("_P") +_T = TypeVar("_T") + + +class CruRawFunctions: + @staticmethod + def none(*_v, **_kwargs) -> None: + return None + + @staticmethod + def true(*_v, **_kwargs) -> Literal[True]: + return True + + @staticmethod + def false(*_v, **_kwargs) -> Literal[False]: + return False + + @staticmethod + def identity(v: _T) -> _T: + return v + + @staticmethod + def only_you(v: _T, *_v, **_kwargs) -> _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: Any) -> Any: + return not v + + +CruArgsChainableCallable: TypeAlias = Callable[..., Iterable[Any]] +CruKwargsChainableCallable: TypeAlias = Callable[..., Iterable[tuple[str, Any]]] +CruChainableCallable: TypeAlias = Callable[ + ..., tuple[Iterable[Any], Iterable[tuple[str, Any]]] +] + + +class CruFunctionChainMode(Flag): + ARGS = auto() + KWARGS = auto() + BOTH = ARGS | KWARGS + + +class CruFunctionMeta: + @staticmethod + def bind(func: Callable[..., _T], *bind_args, **bind_kwargs) -> Callable[..., _T]: + def bound_func(*args, **kwargs): + popped = 0 + real_args = [] + for arg in bind_args: + if CruPlaceholder.check(arg): + real_args.append(args[popped]) + popped += 1 + else: + real_args.append(arg) + real_args.extend(args[popped:]) + return func(*real_args, **(bind_kwargs | kwargs)) + + return bound_func + + @staticmethod + def chain_with_args( + funcs: Iterable[CruArgsChainableCallable], *bind_args, **bind_kwargs + ) -> CruArgsChainableCallable: + def chained_func(*args): + for func in funcs: + args = CruFunctionMeta.bind(func, *bind_args, **bind_kwargs)(*args) + return args + + return chained_func + + @staticmethod + def chain_with_kwargs( + funcs: Iterable[CruKwargsChainableCallable], *bind_args, **bind_kwargs + ) -> CruKwargsChainableCallable: + def chained_func(**kwargs): + for func in funcs: + kwargs = CruFunctionMeta.bind(func, *bind_args, **bind_kwargs)(**kwargs) + return kwargs + + return chained_func + + @staticmethod + def chain_with_both( + funcs: Iterable[CruChainableCallable], *bind_args, **bind_kwargs + ) -> CruChainableCallable: + def chained_func(*args, **kwargs): + for func in funcs: + args, kwargs = CruFunctionMeta.bind(func, *bind_args, **bind_kwargs)( + *args, **kwargs + ) + return args, kwargs + + return chained_func + + @staticmethod + def chain( + mode: CruFunctionChainMode, + funcs: Iterable[ + CruArgsChainableCallable | CruKwargsChainableCallable | CruChainableCallable + ], + *bind_args, + **bind_kwargs, + ) -> CruArgsChainableCallable | CruKwargsChainableCallable | CruChainableCallable: + if mode == CruFunctionChainMode.ARGS: + return CruFunctionMeta.chain_with_args( + cast(Iterable[CruArgsChainableCallable], funcs), + *bind_args, + **bind_kwargs, + ) + elif mode == CruFunctionChainMode.KWARGS: + return CruFunctionMeta.chain_with_kwargs( + cast(Iterable[CruKwargsChainableCallable], funcs), + *bind_args, + **bind_kwargs, + ) + elif mode == CruFunctionChainMode.BOTH: + return CruFunctionMeta.chain_with_both( + cast(Iterable[CruChainableCallable], funcs), *bind_args, **bind_kwargs + ) + + +# Advanced Function Wrapper +class CruFunction(Generic[_P, _T]): + + def __init__(self, f: Callable[_P, _T]): + self._f = f + + @property + def f(self) -> Callable[_P, _T]: + return self._f + + @property + def func(self) -> Callable[_P, _T]: + return self.f + + def bind(self, *bind_args, **bind_kwargs) -> CruFunction[..., _T]: + self._f = CruFunctionMeta.bind(self._f, *bind_args, **bind_kwargs) + return self + + def _iter_with_self( + self, funcs: Iterable[Callable[..., Any]] + ) -> Iterable[Callable[..., Any]]: + yield self + yield from funcs + + @staticmethod + def chain_with_args( + self, + funcs: Iterable[CruArgsChainableCallable], + *bind_args, + **bind_kwargs, + ) -> CruArgsChainableCallable: + return CruFunction( + CruFunctionMeta.chain_with_args( + self._iter_with_self(funcs), *bind_args, **bind_kwargs + ) + ) + + def chain_with_kwargs( + self, funcs: Iterable[CruKwargsChainableCallable], *bind_args, **bind_kwargs + ) -> CruKwargsChainableCallable: + return CruFunction( + CruFunctionMeta.chain_with_kwargs( + self._iter_with_self(funcs), *bind_args, **bind_kwargs + ) + ) + + def chain_with_both( + self, funcs: Iterable[CruChainableCallable], *bind_args, **bind_kwargs + ) -> CruChainableCallable: + return CruFunction( + CruFunctionMeta.chain_with_both( + self._iter_with_self(funcs), *bind_args, **bind_kwargs + ) + ) + + def chain( + self, + mode: CruFunctionChainMode, + funcs: Iterable[ + CruArgsChainableCallable | CruKwargsChainableCallable | CruChainableCallable + ], + *bind_args, + **bind_kwargs, + ) -> CruArgsChainableCallable | CruKwargsChainableCallable | CruChainableCallable: + return CruFunction( + CruFunctionMeta.chain( + mode, self._iter_with_self(funcs), *bind_args, **bind_kwargs + ) + ) + + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _T: + return self._f(*args, **kwargs) + + @staticmethod + def make_chain( + mode: CruFunctionChainMode, + funcs: Iterable[ + CruArgsChainableCallable | CruKwargsChainableCallable | CruChainableCallable + ], + *bind_args, + **bind_kwargs, + ) -> CruFunction: + return CruFunction( + CruFunctionMeta.chain(mode, funcs, *bind_args, **bind_kwargs) + ) + + +class CruWrappedFunctions: + none = CruFunction(CruRawFunctions.none) + true = CruFunction(CruRawFunctions.true) + false = CruFunction(CruRawFunctions.false) + identity = CruFunction(CruRawFunctions.identity) + only_you = CruFunction(CruRawFunctions.only_you) + equal = CruFunction(CruRawFunctions.equal) + not_equal = CruFunction(CruRawFunctions.not_equal) + not_ = CruFunction(CruRawFunctions.not_) + + +class CruFunctionGenerators: + @staticmethod + def make_isinstance_of_types(*types: type) -> Callable: + return CruFunction(lambda v: type(v) in types) + + +CRU.add_objects( + CruRawFunctions, + CruFunctionMeta, + CruFunction, + CruWrappedFunctions, + CruFunctionGenerators, +) diff --git a/tools/cru-py/cru/_util/_lang.py b/tools/cru-py/cru/_util/_lang.py new file mode 100644 index 0000000..925ba00 --- /dev/null +++ b/tools/cru-py/cru/_util/_lang.py @@ -0,0 +1,16 @@ +from collections.abc import Callable +from typing import Any, Iterable, TypeVar, cast + +T = TypeVar("T") +D = TypeVar("D") + + +def remove_element( + iterable: Iterable[T | None], to_rm: Iterable[Any], des: type[D] | None = None +) -> D: + to_rm = set(to_rm) + return cast(Callable[..., D], des or list)(v for v in iterable if v not in to_rm) + + +def remove_none(iterable: Iterable[T | None], des: type[D] | None = None) -> D: + return cast(Callable[..., D], des or list)(v for v in iterable if v is not None) diff --git a/tools/cru-py/cru/util/_list.py b/tools/cru-py/cru/_util/_list.py index e1c8373..711d5f1 100644 --- a/tools/cru-py/cru/util/_list.py +++ b/tools/cru-py/cru/_util/_list.py @@ -1,85 +1,235 @@ +from __future__ import annotations + from collections.abc import Iterable, Callable from dataclasses import dataclass from enum import Enum -from typing import TypeVar, ParamSpec, Any, Generic, ClassVar, Optional, Union - -from ._const import CRU_NOT_FOUND - +from typing import ( + Generator, + Literal, + Self, + TypeAlias, + TypeVar, + ParamSpec, + Any, + Generic, + ClassVar, + Optional, + Union, + assert_never, + cast, + overload, + override, +) + +from ._const import CruNoValue, CruNotFound + +P = ParamSpec("P") T = TypeVar("T") O = TypeVar("O") -R = TypeVar("R") F = TypeVar("F") -CanBeList = T | Iterable[T] | None +CanBeList: TypeAlias = Iterable[T] | T | None -OptionalIndex = int | None -OptionalType = type | None -ElementOperation = Callable[[T], Any] | None -ElementPredicate = Callable[[T], bool] -ElementTransformer = Callable[[T], R] -SelfElementTransformer = ElementTransformer[T, T] -AnyElementTransformer = ElementTransformer[Any, Any] -OptionalElementOperation = ElementOperation | None -OptionalElementTransformer = ElementTransformer | None -OptionalSelfElementTransformer = ElementTransformer[T, T] -OptionalAnyElementTransformer = AnyElementTransformer | None +OptionalIndex: TypeAlias = int | None +OptionalType: TypeAlias = type | None +ElementOperation: TypeAlias = Callable[[T], Any] +ElementPredicate: TypeAlias = Callable[[T], bool] +ElementTransformer: TypeAlias = Callable[[T], O] +SelfElementTransformer: TypeAlias = ElementTransformer[T, T] +AnyElementTransformer: TypeAlias = ElementTransformer[Any, Any] -def _flatten_with_func(o: T, max_depth: int, is_leave: ElementPredicate[T], - get_children: SelfElementTransformer[T], depth: int = 0) -> Iterable[T]: +def flatten_with_func( + o: T, + max_depth: int, + is_leave: ElementPredicate[T], + get_children: ElementTransformer[T, Iterable[T]], + depth: int = 0, +) -> Iterable[T]: if depth == max_depth or is_leave(o): yield o return for child in get_children(o): - yield from _flatten_with_func(child, max_depth, is_leave, get_children, depth + 1) + yield from flatten_with_func( + child, max_depth, is_leave, get_children, depth + 1 + ) -class _Action(Enum): +class _StepActionKind(Enum): SKIP = 0 + # TODO: Rename this SEND = 1 STOP = 2 AGGREGATE = 3 @dataclass -class _Result(Generic[T]): - Action: ClassVar[type[_Action]] = _Action +class _StepAction(Generic[T]): + value: Iterable[_StepAction[T]] | T | None + kind: _StepActionKind + + @property + def non_aggregate_value(self) -> T: + assert self.kind != _StepActionKind.AGGREGATE + return cast(T, self.value) + + @staticmethod + def skip() -> _StepAction[T]: + return _StepAction(None, _StepActionKind.SKIP) - value: T | O | None - action: Action + @staticmethod + def send(value: T | None) -> _StepAction[T]: + return _StepAction(value, _StepActionKind.SEND) @staticmethod - def skip() -> "_Result"[T]: - return _Result(None, _Action.SKIP) + def stop(value: T | None = None) -> _StepAction[T]: + return _StepAction(value, _StepActionKind.STOP) + + @staticmethod + def aggregate(*results: _StepAction[T]) -> _StepAction[T]: + return _StepAction(results, _StepActionKind.AGGREGATE) + + @staticmethod + def send_last(value: Any) -> _StepAction[T]: + return _StepAction.aggregate(_StepAction.send(value), _StepAction.stop()) + + def flatten(self) -> Iterable[_StepAction[T]]: + return flatten_with_func( + self, + -1, + lambda r: r.kind != _StepActionKind.AGGREGATE, + lambda r: cast(Iterable[_StepAction[T]], r.value), + ) + + +_r_skip = _StepAction.skip +_r_send = _StepAction.send +_r_stop = _StepAction.stop +_r_send_last = _StepAction.send_last +_r_aggregate = _StepAction.aggregate + + +_GeneralStepAction: TypeAlias = _StepAction[T] | T | None +_GeneralStepActionConverter: TypeAlias = Callable[ + [_GeneralStepAction[T]], _StepAction[T] +] +_IterateOperation = Callable[[T, int], _GeneralStepAction[O]] +_IteratePreHook = Callable[[Iterable[T]], _GeneralStepAction[O]] +_IteratePostHook = Callable[[int], _GeneralStepAction[O]] + + +class CruGenericIterableMeta: + StepActionKind = _StepActionKind + StepAction = _StepAction + GeneralStepAction = _GeneralStepAction + GeneralStepActionConverter = _GeneralStepActionConverter + IterateOperation = _IterateOperation + IteratePreHook = _IteratePreHook + IteratePostHook = _IteratePostHook @staticmethod - def send(value: Any) -> "_Result"[T]: - return _Result(value, _Action.SEND) + def _non_result_to_send(value: O | None) -> _StepAction[O]: + return _StepAction.send(value) @staticmethod - def stop(value: Any = None) -> "_Result"[T]: - return _Result(value, _Action.STOP) + def _non_result_to_stop(value: O | None) -> _StepAction[O]: + return _StepAction.stop(value) @staticmethod - def aggregate(*result: "_Result"[T]) -> "_Result"[T]: - return _Result(result, _Action.AGGREGATE) + def _none_pre_iterate() -> _StepAction[O]: + return _r_skip() @staticmethod - def send_last(value: Any) -> "_Result"[T]: - return _Result.aggregate(_Result.send(value), _Result.stop()) + def _none_post_iterate( + _index: int, + ) -> _StepAction[O]: + return _r_skip() - def flatten(self) -> Iterable["_Result"[T]]: - return _flatten_with_func(self, -1, lambda r: r.action != _Action.AGGREGATE, lambda r: r.value) + def iterate( + self, + operation: _IterateOperation[T, O], + fallback_return: O, + pre_iterate: _IteratePreHook[T, O], + post_iterate: _IteratePostHook[O], + convert_non_result: Callable[[O | None], _StepAction[O]], + ) -> Generator[O, None, O]: + pre_result = pre_iterate(self._iterable) + if not isinstance(pre_result, _StepAction): + real_pre_result = convert_non_result(pre_result) + for r in real_pre_result.flatten(): + if r.kind == _StepActionKind.STOP: + return r.non_aggregate_value + elif r.kind == _StepActionKind.SEND: + yield r.non_aggregate_value + for index, element in enumerate(self._iterable): + result = operation(element, index) + if not isinstance(result, _StepAction): + real_result = convert_non_result(result) + for r in real_result.flatten(): + if r.kind == _StepActionKind.STOP: + return r.non_aggregate_value + elif r.kind == _StepActionKind.SEND: + yield r.non_aggregate_value + else: + continue -_r_skip = _Result.skip -_r_send = _Result.send -_r_stop = _Result.stop -_r_send_last = _Result.send_last -_r_aggregate = _Result.aggregate + post_result = post_iterate(index + 1) + if not isinstance(post_result, _StepAction): + real_post_result = convert_non_result(post_result) + for r in real_post_result.flatten(): + if r.kind == _StepActionKind.STOP: + return r.non_aggregate_value + elif r.kind == _StepActionKind.SEND: + yield r.non_aggregate_value + + return fallback_return + + def _new( + self, + operation: _IterateOperation, + fallback_return: O, + /, + pre_iterate: _IteratePreHook[T, O] | None = None, + post_iterate: _IteratePostHook[O] | None = None, + ) -> CruIterableWrapper: + return CruIterableWrapper( + self.iterate( + operation, + fallback_return, + pre_iterate or CruIterableWrapper._none_pre_iterate, + post_iterate or CruIterableWrapper._none_post_iterate, + CruIterableWrapper._non_result_to_send, + ), + self._create_new_upstream(), + ) + + def _result( + self, + operation: _IterateOperation, + fallback_return: O, + /, + result_transform: SelfElementTransformer[O] | None = None, + pre_iterate: _IteratePreHook[T, O] | None = None, + post_iterate: _IteratePostHook[O] | None = None, + ) -> O: + try: + for _ in self.iterate( + operation, + fallback_return, + pre_iterate or CruIterableWrapper._none_pre_iterate, + post_iterate or CruIterableWrapper._none_post_iterate, + CruIterableWrapper._non_result_to_stop, + ): + pass + except StopIteration as stop: + return ( + stop.value if result_transform is None else result_transform(stop.value) + ) + raise RuntimeError("Should not reach here") -class _Defaults: +class IterDefaultResults: @staticmethod def true(_): return True @@ -90,35 +240,30 @@ class _Defaults: @staticmethod def not_found(_): - return CRU_NOT_FOUND - - -def _default_upstream() -> Iterable[Iterable]: - return iter([]) - - -CruIterableUpstream = Iterable[Iterable] -CruIterableOptionalUpstream = CruIterableUpstream | None + return CruNotFound.VALUE class CruIterableCreators: @staticmethod - def with_(o: Any, /, upstreams: CruIterableOptionalUpstream = _default_upstream()) -> "CruIterableWrapper": - return CruIterableWrapper(iter(o), upstreams) + def with_(o: Any) -> CruIterableWrapper: + return CruIterableWrapper(iter(o)) @staticmethod - def empty(upstreams: CruIterableOptionalUpstream = _default_upstream()) -> "CruIterableWrapper": - return CruIterableCreators.with_([], upstreams) + def empty() -> CruIterableWrapper: + return CruIterableCreators.with_([]) @staticmethod - def range(a, b=None, c=None, /, upstreams: CruIterableOptionalUpstream = _default_upstream()) -> \ - "CruIterableWrapper"[int]: + def range( + a, + b=None, + c=None, + ) -> CruIterableWrapper[int]: args = [arg for arg in [a, b, c] if arg is not None] - return CruIterableCreators.with_(range(*args), upstreams) + return CruIterableCreators.with_(range(*args)) @staticmethod - def unite(*args: T, upstreams: CruIterableOptionalUpstream = _default_upstream()) -> "CruIterableWrapper"[T]: - return CruIterableCreators.with_(args, upstreams) + def unite(*args: T) -> CruIterableWrapper[T]: + return CruIterableCreators.with_(args) @staticmethod def _concat(*iterables: Iterable) -> Iterable: @@ -126,138 +271,95 @@ class CruIterableCreators: yield from iterable @staticmethod - def concat(*iterables: Iterable, - upstreams: CruIterableOptionalUpstream = _default_upstream()) -> "CruIterableWrapper": - return CruIterableWrapper(CruIterableCreators._concat(*iterables), upstreams) + def concat(*iterables: Iterable) -> CruIterableWrapper: + return CruIterableWrapper(CruIterableCreators._concat(*iterables)) class CruIterableWrapper(Generic[T]): - Upstream = CruIterableUpstream - OptionalUpstream = CruIterableOptionalUpstream - _Result = _Result[T] - _Operation = Callable[[T, int], _Result | Any | None] - def __init__(self, iterable: Iterable[T], /, upstreams: OptionalUpstream = _default_upstream()) -> None: + def __init__( + self, + iterable: Iterable[T], + ) -> None: self._iterable = iterable - self._upstreams = None if upstreams is None else list(upstreams) + + def __iter__(self): + return self._iterable.__iter__() @property def me(self) -> Iterable[T]: return self._iterable - # TODO: Return Type - @property - def my_upstreams(self) -> Optional["CruIterableWrapper"]: - if self._upstreams is None: - return None - return CruIterableWrapper(iter(self._upstreams)) - - def disable_my_upstreams(self) -> "CruIterableWrapper"[T]: - return CruIterableWrapper(self._iterable, None) - - def clear_my_upstreams(self) -> "CruIterableWrapper"[T]: - return CruIterableWrapper(self._iterable) - - def _create_upstreams_prepend_self(self) -> Upstream: - yield self._iterable - yield self.my_upstreams - - # TODO: Return Type - def _create_new_upstreams(self, append: bool = True) -> Optional["CruIterableWrapper"]: - if not append: return self.my_upstreams - if self.my_upstreams is None: - return None - return CruIterableWrapper(self._create_upstreams_prepend_self()) - - def clone_me(self, /, update_upstreams: bool = True) -> "CruIterableWrapper"[T]: - return CruIterableWrapper(self._iterable, self._create_new_upstreams(update_upstreams)) - - def replace_me_with(self, iterable: Iterable[O], /, update_upstreams: bool = True) -> "CruIterableWrapper"[O]: - return CruIterableCreators.with_(iterable, upstreams=self._create_new_upstreams(update_upstreams)) - - def replace_me_with_empty(self, /, update_upstreams: bool = True) -> "CruIterableWrapper"[O]: - return CruIterableCreators.empty(upstreams=self._create_new_upstreams(update_upstreams)) - - def replace_me_with_range(self, a, b=None, c=None, /, update_upstreams: bool = True) -> "CruIterableWrapper"[int]: - return CruIterableCreators.range(a, b, c, upstreams=self._create_new_upstreams(update_upstreams)) - - def replace_me_with_unite(self, *args: O, update_upstreams: bool = True) -> "CruIterableWrapper"[O]: - return CruIterableCreators.unite(*args, upstreams=self._create_new_upstreams(update_upstreams)) - - def replace_me_with_concat(self, *iterables: Iterable, update_upstreams: bool = True) -> "CruIterableWrapper": - return CruIterableCreators.concat(*iterables, upstreams=self._create_new_upstreams(update_upstreams)) - - @staticmethod - def _non_result_to_yield(value: Any | None) -> _Result: - return _Result.stop(value) - - @staticmethod - def _non_result_to_return(value: Any | None) -> _Result: - return _Result.stop(value) + def replace_me_with(self, iterable: Iterable[O]) -> CruIterableWrapper[O]: + return CruIterableCreators.with_(iterable) - def _real_iterate(self, operation: _Operation, - convert_non_result: Callable[[Any | None], _Result]) -> Iterable: + def replace_me_with_empty(self) -> CruIterableWrapper[O]: + return CruIterableCreators.empty() - for index, element in enumerate(self._iterable): - result = operation(element, index) - if not isinstance(result, _Result): - result = convert_non_result(result) - for result in result.flatten(): - if result.action == _Result.Action.STOP: - return result.value - elif result.action == _Result.Action.SEND: - yield result.value - else: - continue + def replace_me_with_range(self, a, b=None, c=None) -> CruIterableWrapper[int]: + return CruIterableCreators.range(a, b, c) - def _new(self, operation: _Operation) -> "CruIterableWrapper": - return CruIterableWrapper(self._real_iterate(operation, CruIterableWrapper._non_result_to_yield), - self._create_new_upstreams()) + def replace_me_with_unite(self, *args: O) -> CruIterableWrapper[O]: + return CruIterableCreators.unite(*args) - def _result(self, operation: _Operation, - result_transform: OptionalElementTransformer[T, T | O] = None) -> T | O: - try: - self._real_iterate(operation, CruIterableWrapper._non_result_to_return) - except StopIteration as stop: - return stop.value if result_transform is None else result_transform(stop.value) + def replace_me_with_concat(self, *iterables: Iterable) -> CruIterableWrapper: + return CruIterableCreators.concat(*iterables) @staticmethod - def _make_set(iterable: Iterable, discard: Iterable | None) -> set: + def _make_set(iterable: Iterable[O], discard: Iterable[Any] | None) -> set[O]: s = set(iterable) if discard is not None: s = s - set(discard) return s @staticmethod - def _make_list(iterable: Iterable, discard: Iterable | None) -> list: - if discard is None: return list(iterable) + def _make_list(iterable: Iterable[O], discard: Iterable[Any] | None) -> list[O]: + if discard is None: + return list(iterable) return [v for v in iterable if v not in discard] - # noinspection PyMethodMayBeStatic - def _help_make_set(self, iterable: Iterable, discard: Iterable | None = iter([None])) -> set: + def _help_make_set( + self, iterable: Iterable[O], discard: Iterable[Any] | None + ) -> set[O]: return CruIterableWrapper._make_set(iterable, discard) - # noinspection PyMethodMayBeStatic - def _help_make_list(self, iterable: Iterable, discard: Iterable | None = iter([None])) -> list: + def _help_make_list( + self, iterable: Iterable[O], discard: Iterable[Any] | None + ) -> list[O]: return CruIterableWrapper._make_list(iterable, discard) - def to_set(self, discard: Iterable | None = None) -> set[T]: + def to_set(self, discard: Iterable[Any] | None = None) -> set[T]: return CruIterableWrapper._make_set(self.me, discard) - def to_list(self, discard: Iterable | None = None) -> list[T]: + def to_list(self, discard: Iterable[Any] | None = None) -> list[T]: return CruIterableWrapper._make_list(self.me, discard) - def copy(self) -> "CruIterableWrapper": - return CruIterableWrapper(iter(self.to_list()), self._create_new_upstreams()) + def copy(self) -> CruIterableWrapper: + return CruIterableWrapper(iter(self.to_list()), self._create_new_upstream()) - def concat(self, *iterable: Iterable[T]) -> "CruIterableWrapper": - return self.replace_me_with_concat(self.me, *iterable) + def new_start( + self, other: Iterable[O], /, clear_upstream: bool = False + ) -> CruIterableWrapper[O]: + return CruIterableWrapper( + other, None if clear_upstream else self._create_new_upstream() + ) + + @overload + def concat(self) -> Self: ... + + @overload + def concat( + self, *iterable: Iterable[Any], last: Iterable[O] + ) -> CruIterableWrapper[O]: ... + + def concat(self, *iterable: Iterable[Any]) -> CruIterableWrapper[Any]: # type: ignore + return self.new_start(CruIterableCreators.concat(self.me, *iterable)) def all(self, predicate: ElementPredicate[T]) -> bool: """ partial """ - return self._result(lambda v, _: predicate(v) and None, _Defaults.true) + return self._result(lambda v, _: predicate(v) and None, IterDefaultResults.true) def all_isinstance(self, *types: OptionalType) -> bool: """ @@ -270,21 +372,23 @@ class CruIterableWrapper(Generic[T]): """ partial """ - return self._result(lambda v, _: predicate(v) or None, _Defaults.false) + return self._result(lambda v, _: predicate(v) or None, IterDefaultResults.false) - def number(self) -> "CruIterableWrapper": + def number(self) -> CruIterableWrapper: """ partial """ return self._new(lambda _, i: i) - def take(self, predicate: ElementPredicate[T]) -> "CruIterableWrapper": + def take(self, predicate: ElementPredicate[T]) -> CruIterableWrapper: """ complete """ return self._new(lambda v, _: _r_send(v) if predicate(v) else None) - def transform(self, *transformers: OptionalElementTransformer) -> "CruIterableWrapper": + def transform( + self, *transformers: OptionalElementTransformer + ) -> CruIterableWrapper: """ complete """ @@ -297,7 +401,7 @@ class CruIterableWrapper(Generic[T]): return self._new(_transform_element) - def take_n(self, max_count: int, neg_is_clone: bool = True) -> "CruIterableWrapper": + def take_n(self, max_count: int, neg_is_clone: bool = True) -> CruIterableWrapper: """ partial """ @@ -308,17 +412,23 @@ class CruIterableWrapper(Generic[T]): raise ValueError("max_count must be 0 or positive.") elif max_count == 0: return self.drop_all() - return self._new(lambda v, i: _r_send(v) if i < max_count - 1 else _r_send_last(v)) + return self._new( + lambda v, i: _r_send(v) if i < max_count - 1 else _r_send_last(v) + ) - def take_by_indices(self, *indices: OptionalIndex) -> "CruIterableWrapper": + def take_by_indices(self, *indices: OptionalIndex) -> CruIterableWrapper: """ partial """ indices = self._help_make_set(indices) max_index = max(indices) - return self.take_n(max_index + 1)._new(lambda v, i: _r_send(v) if i in indices else None) + return self.take_n(max_index + 1)._new( + lambda v, i: _r_send(v) if i in indices else None + ) - def single_or(self, fallback: Any | None = CRU_NOT_FOUND) -> T | Any | CRU_NOT_FOUND: + def single_or( + self, fallback: Any | None = CRU_NOT_FOUND + ) -> T | Any | CRU_NOT_FOUND: """ partial """ @@ -335,14 +445,18 @@ class CruIterableWrapper(Generic[T]): else: return fallback - def first_or(self, predicate: ElementPredicate[T], fallback: Any | None = CRU_NOT_FOUND) -> T | CRU_NOT_FOUND: + def first_or( + self, predicate: ElementPredicate[T], fallback: Any | None = CRU_NOT_FOUND + ) -> T | CRU_NOT_FOUND: """ partial """ result_iterable = self.take_n(1).single_or() @staticmethod - def first_index(iterable: Iterable[T], predicate: ElementPredicate[T]) -> int | CRU_NOT_FOUND: + def first_index( + iterable: Iterable[T], predicate: ElementPredicate[T] + ) -> int | CRU_NOT_FOUND: """ partial """ @@ -351,7 +465,9 @@ class CruIterableWrapper(Generic[T]): return index @staticmethod - def take_indices(iterable: Iterable[T], predicate: ElementPredicate[T]) -> Iterable[int]: + def take_indices( + iterable: Iterable[T], predicate: ElementPredicate[T] + ) -> Iterable[int]: """ complete """ @@ -360,8 +476,12 @@ class CruIterableWrapper(Generic[T]): yield index @staticmethod - def flatten(o, max_depth=-1, is_leave: ElementPredicate | None = None, - get_children: OptionalElementTransformer = None) -> Iterable: + def flatten( + o, + max_depth=-1, + is_leave: ElementPredicate | None = None, + get_children: OptionalElementTransformer = None, + ) -> Iterable: """ complete """ @@ -369,7 +489,9 @@ class CruIterableWrapper(Generic[T]): is_leave = lambda v: not isinstance(v, Iterable) if get_children is None: get_children = lambda v: v - return CruIterableWrapper._flatten_with_func(o, max_depth, is_leave, get_children) + return CruIterableWrapper._flatten_with_func( + o, max_depth, is_leave, get_children + ) @staticmethod def skip_by_indices(iterable: Iterable[T], *indices: OptionalIndex) -> Iterable[T]: @@ -390,7 +512,7 @@ class CruIterableWrapper(Generic[T]): if not predicate(element): yield element - def drop_all(self) -> "CruIterableWrapper": + def drop_all(self) -> CruIterableWrapper: return self.replace_me_with_empty() @staticmethod @@ -407,7 +529,8 @@ class CruIterableWrapper(Generic[T]): @staticmethod def foreach(iterable: Iterable[T], *f: OptionalElementOperation[T]) -> None: - if len(f) == 0: return + if len(f) == 0: + return for v in iterable: for f_ in f: if f_ is not None: @@ -415,7 +538,8 @@ class CruIterableWrapper(Generic[T]): @staticmethod def make(v: CanBeList[T], /, none_to_empty_list: bool = True) -> list[T]: - if v is None and none_to_empty_list: return [] + if v is None and none_to_empty_list: + return [] return list(v) if isinstance(v, Iterable) else [v] @@ -457,7 +581,9 @@ class ListOperations: return _God.new(iterable, lambda v, _: _God.yield_(v) if predicate(v) else None) @staticmethod - def transform(iterable: Iterable[T], *transformers: OptionalElementTransformer) -> Iterable: + def transform( + iterable: Iterable[T], *transformers: OptionalElementTransformer + ) -> Iterable: """ complete """ @@ -479,7 +605,9 @@ class ListOperations: return iterable elif n == 0: return [] - return range(n)._god_yield(iterable, lambda v, i: _yield(v) if i < n else _return()) + return range(n)._god_yield( + iterable, lambda v, i: _yield(v) if i < n else _return() + ) @staticmethod def take_by_indices(iterable: Iterable[T], *indices: OptionalIndex) -> Iterable[T]: @@ -502,7 +630,9 @@ class ListOperations: return CRU_NOT_FOUND @staticmethod - def first_index(iterable: Iterable[T], predicate: ElementPredicate[T]) -> int | CRU_NOT_FOUND: + def first_index( + iterable: Iterable[T], predicate: ElementPredicate[T] + ) -> int | CRU_NOT_FOUND: """ partial """ @@ -511,7 +641,9 @@ class ListOperations: return index @staticmethod - def take_indices(iterable: Iterable[T], predicate: ElementPredicate[T]) -> Iterable[int]: + def take_indices( + iterable: Iterable[T], predicate: ElementPredicate[T] + ) -> Iterable[int]: """ complete """ @@ -567,7 +699,8 @@ class ListOperations: @staticmethod def foreach(iterable: Iterable[T], *f: OptionalElementOperation[T]) -> None: - if len(f) == 0: return + if len(f) == 0: + return for v in iterable: for f_ in f: if f_ is not None: @@ -575,7 +708,8 @@ class ListOperations: @staticmethod def make(v: CanBeList[T], /, none_to_empty_list: bool = True) -> list[T]: - if v is None and none_to_empty_list: return [] + if v is None and none_to_empty_list: + return [] return list(v) if isinstance(v, Iterable) else [v] @@ -629,7 +763,9 @@ class CruList(list, Generic[T]): def transform(self, *f: OptionalElementTransformer) -> "CruList"[Any]: return CruList(ListOperations.transform(self, *f)) - def transform_if(self, f: OptionalElementTransformer, p: ElementPredicate[T]) -> "CruList"[Any]: + def transform_if( + self, f: OptionalElementTransformer, p: ElementPredicate[T] + ) -> "CruList"[Any]: return CruList(ListOperations.transform_if(self, f, p)) def remove_by_indices(self, *index: int) -> "CruList"[T]: @@ -670,7 +806,9 @@ class CruInplaceList(CruList, Generic[T]): def transform(self, *f: OptionalElementTransformer) -> "CruInplaceList"[Any]: return self.reset(super().transform(*f)) - def transform_if(self, f: OptionalElementTransformer, p: ElementPredicate[T]) -> "CruInplaceList"[Any]: + def transform_if( + self, f: OptionalElementTransformer, p: ElementPredicate[T] + ) -> "CruInplaceList"[Any]: return self.reset(super().transform_if(f, p)) def remove_by_indices(self, *index: int) -> "CruInplaceList"[T]: @@ -682,7 +820,9 @@ class CruInplaceList(CruList, Generic[T]): def remove_all_value(self, *r: Any) -> "CruInplaceList"[T]: return self.reset(super().remove_all_value(*r)) - def replace_all_value(self, old_value: Any, new_value: R) -> "CruInplaceList"[T | R]: + def replace_all_value( + self, old_value: Any, new_value: R + ) -> "CruInplaceList"[T | R]: return self.reset(super().replace_all_value(old_value, new_value)) @staticmethod @@ -696,7 +836,9 @@ K = TypeVar("K") class CruUniqueKeyInplaceList(Generic[T, K]): KeyGetter = Callable[[T], K] - def __init__(self, get_key: KeyGetter, *, before_add: Callable[[T], T] | None = None): + def __init__( + self, get_key: KeyGetter, *, before_add: Callable[[T], T] | None = None + ): super().__init__() self._get_key = get_key self._before_add = before_add @@ -733,7 +875,8 @@ class CruUniqueKeyInplaceList(Generic[T, K]): def try_remove(self, k: K) -> bool: i = self._l.find_index_if(lambda v: k == self._get_key(v)) - if i is CRU_NOT_FOUND: return False + if i is CRU_NOT_FOUND: + return False self._l.remove_by_indices(i) return True diff --git a/tools/cru-py/cru/util/_type.py b/tools/cru-py/cru/_util/_type.py index dc50def..dc50def 100644 --- a/tools/cru-py/cru/util/_type.py +++ b/tools/cru-py/cru/_util/_type.py diff --git a/tools/cru-py/cru/attr.py b/tools/cru-py/cru/attr.py index 32cca8d..a52585a 100644 --- a/tools/cru-py/cru/attr.py +++ b/tools/cru-py/cru/attr.py @@ -3,7 +3,7 @@ from collections.abc import Callable, Iterable from dataclasses import dataclass, field from typing import Any, ClassVar -from .util import CanBeList, TypeSet, F, L, WF, CruUniqueKeyInplaceList, CRU_NOT_FOUND, CRU_USE_DEFAULT, \ +from ._util import CanBeList, TypeSet, F, L, WF, CruUniqueKeyInplaceList, CRU_NOT_FOUND, CRU_USE_DEFAULT, \ CRU_DONT_CHANGE, CRU_PLACEHOLDER diff --git a/tools/cru-py/cru/excp.py b/tools/cru-py/cru/excp.py index 358ad90..9ea204e 100644 --- a/tools/cru-py/cru/excp.py +++ b/tools/cru-py/cru/excp.py @@ -1,7 +1,7 @@ from typing import Any from .attr import CruAttrDefRegistry, CruAttr, CruAttrTable -from .util import CRU_NOT_FOUND, CruList, CRU_USE_DEFAULT +from ._util import CRU_NOT_FOUND, CruList, CRU_USE_DEFAULT CRU_EXCEPTION_ATTR_DEF_REGISTRY = CruAttrDefRegistry() diff --git a/tools/cru-py/cru/service/docker.py b/tools/cru-py/cru/service/docker.py index a57a246..5958f4f 100644 --- a/tools/cru-py/cru/service/docker.py +++ b/tools/cru-py/cru/service/docker.py @@ -1,7 +1,7 @@ import shutil import subprocess -from ..util import L +from .._util import L class DockerController: diff --git a/tools/cru-py/cru/util/__init__.py b/tools/cru-py/cru/util/__init__.py deleted file mode 100644 index ecd9673..0000000 --- a/tools/cru-py/cru/util/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Any - -from ._const import cru_make_unique_object, cru_make_bool_unique_object, CRU_NOT_FOUND, CRU_USE_DEFAULT, \ - CRU_DONT_CHANGE, \ - CRU_PLACEHOLDER -from ._func import CruFunction, CruFunctionMeta, CruRawFunctions, CruWrappedFunctions, CruFunctionGenerators -from ._list import CruList, CruInplaceList, CruUniqueKeyInplaceList, ListOperations, CanBeList, ElementOperation, \ - ElementPredicate, ElementTransformer, OptionalElementOperation, ElementPredicate, OptionalElementTransformer -from ._type import TypeSet - -F = CruFunction -WF = CruWrappedFunctions -FG = CruFunctionGenerators -L = CruList - - -__all__ = [ - "CRU_NOT_FOUND", "CRU_USE_DEFAULT", "CRU_DONT_CHANGE", "CRU_PLACEHOLDER", - "CruFunction", "CruFunctionMeta", "CruRawFunctions", "CruWrappedFunctions", "CruFunctionGenerators", - "CruList", "CruInplaceList", "CruUniqueKeyInplaceList", "ListOperations", - "CanBeList", "ElementOperation", "ElementPredicate", "ElementTransformer", - "OptionalElementOperation", "ElementPredicate", "OptionalElementTransformer", - "TypeSet", - "F", "WF", "FG", "L" -] diff --git a/tools/cru-py/cru/util/_const.py b/tools/cru-py/cru/util/_const.py deleted file mode 100644 index 8140988..0000000 --- a/tools/cru-py/cru/util/_const.py +++ /dev/null @@ -1,56 +0,0 @@ -from typing import Any - -from ._cru import CRU - - -def cru_make_unique_object() -> Any: - class _CruUnique: - _i = False - - def __init__(self): - if self._i: - raise ValueError("_CruAttrNotSet is a singleton!") - self._i = True - - def __copy__(self): - return self - - def __eq__(self, other): - return isinstance(other, _CruUnique) - - v = _CruUnique() - - return v - - -def cru_make_bool_unique_object(b: bool) -> Any: - class _CruBoolUnique: - _i = False - - def __init__(self): - super().__init__(b) - if self._i: - raise ValueError("_CruAttrNotSet is a singleton!") - self._i = True - - def __copy__(self): - return self - - def __eq__(self, other): - return isinstance(other, _CruBoolUnique) or b == other - - def __bool__(self): - return b - - v = _CruBoolUnique() - - return v - - -CRU_NOT_FOUND = cru_make_bool_unique_object(False) -CRU_USE_DEFAULT = cru_make_unique_object() -CRU_DONT_CHANGE = cru_make_unique_object() -CRU_PLACEHOLDER = cru_make_unique_object() - -CRU.add_objects(cru_make_unique_object, cru_make_bool_unique_object, CRU_NOT_FOUND, CRU_USE_DEFAULT, - CRU_DONT_CHANGE, CRU_PLACEHOLDER) diff --git a/tools/cru-py/cru/util/_func.py b/tools/cru-py/cru/util/_func.py deleted file mode 100644 index d9f8044..0000000 --- a/tools/cru-py/cru/util/_func.py +++ /dev/null @@ -1,126 +0,0 @@ -from collections.abc import Callable -from typing import TypeVar - -from ._cru import CRU -from ._const import CRU_PLACEHOLDER - -T = TypeVar("T") - -_PLACEHOLDER = CRU_PLACEHOLDER - -class CruRawFunctions: - @staticmethod - def none(*_v, **_kwargs) -> None: - return None - - @staticmethod - def true(*_v, **_kwargs) -> True: - return True - - @staticmethod - def false(*_v, **_kwargs) -> False: - return False - - @staticmethod - def identity(v: T) -> T: - return v - - @staticmethod - def only_you(v: T, *_v, **_kwargs) -> T: - return v - - @staticmethod - def equal(a, b) -> bool: - return a == b - - @staticmethod - def not_equal(a, b) -> bool: - return a != b - - @staticmethod - def not_(v): - return not v - - -class CruFunctionMeta: - @staticmethod - def bind(func: Callable, *bind_args, **bind_kwargs) -> Callable: - def bound_func(*args, **kwargs): - popped = 0 - real_args = [] - for arg in bind_args: - if arg is _PLACEHOLDER: - real_args.append(args[popped]) - popped += 1 - else: - real_args.append(arg) - real_args.extend(args[popped:]) - return func(*real_args, **(bind_kwargs | kwargs)) - - return bound_func - - @staticmethod - def chain(*funcs: Callable) -> Callable: - if len(funcs) == 0: - raise ValueError("At least one function is required!") - - final_func = funcs[0] - for func in funcs[1:]: - func_copy = func - - def chained_func(*args, **kwargs): - results = final_func(*args, **kwargs) - results = results if isinstance(results, tuple) else (results,) - return func_copy(*results) - - final_func = chained_func - - return final_func - - -# Advanced Function Wrapper -class CruFunction: - def __init__(self, f: Callable): - self._f = f - - @property - def f(self) -> Callable: - return self._f - - @property - def func(self) -> Callable: - return self.f - - def bind(self, *bind_args, **bind_kwargs) -> "CruFunction": - self._f = CruFunctionMeta.bind(self._f, *bind_args, **bind_kwargs) - return self - - def chain(self, *funcs: Callable) -> "CruFunction": - self._f = CruFunctionMeta.chain(self._f, *funcs) - return self - - def __call__(self, *args, **kwargs): - return self._f(*args, **kwargs) - - @staticmethod - def make_chain(base_func: Callable, *funcs: Callable) -> "CruFunction": - return CruFunction(base_func).chain(*funcs) - - -class CruWrappedFunctions: - none = CruFunction(CruRawFunctions.none) - true = CruFunction(CruRawFunctions.true) - false = CruFunction(CruRawFunctions.false) - identity = CruFunction(CruRawFunctions.identity) - only_you = CruFunction(CruRawFunctions.only_you) - equal = CruFunction(CruRawFunctions.equal) - not_equal = CruFunction(CruRawFunctions.not_equal) - not_ = CruFunction(CruRawFunctions.not_) - - -class CruFunctionGenerators: - @staticmethod - def make_isinstance_of_types(*types: type) -> Callable: - return CruFunction(lambda v: type(v) in types) - -CRU.add_objects(CruRawFunctions, CruFunctionMeta, CruFunction, CruWrappedFunctions, CruFunctionGenerators) |