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 | 1d370f56d5a7b21fc440e90944f69627fd6cedb4 (patch) | |
tree | b09dd63c2a99c3b3ff9f30f46a5bf5ff6f054964 /tools | |
parent | e710a36af2775118c514954252537ae37f11996c (diff) | |
download | crupest-1d370f56d5a7b21fc440e90944f69627fd6cedb4.tar.gz crupest-1d370f56d5a7b21fc440e90944f69627fd6cedb4.tar.bz2 crupest-1d370f56d5a7b21fc440e90944f69627fd6cedb4.zip |
HALF WORK: 2024.12.12
Diffstat (limited to 'tools')
-rw-r--r-- | tools/cru-py/cru/__init__.py | 4 | ||||
-rw-r--r-- | tools/cru-py/cru/_base.py (renamed from tools/cru-py/cru/_cru.py) | 24 | ||||
-rw-r--r-- | tools/cru-py/cru/_const.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/_decorator.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/_func.py | 12 | ||||
-rw-r--r-- | tools/cru-py/cru/_iter.py | 96 | ||||
-rw-r--r-- | tools/cru-py/cru/_list.py | 104 | ||||
-rw-r--r-- | tools/cru-py/cru/_type.py | 51 |
8 files changed, 179 insertions, 116 deletions
diff --git a/tools/cru-py/cru/__init__.py b/tools/cru-py/cru/__init__.py index 2ae241e..94d0d69 100644 --- a/tools/cru-py/cru/__init__.py +++ b/tools/cru-py/cru/__init__.py @@ -1,7 +1,9 @@ import sys +from ._base import CruException -class CruInitError(Exception): + +class CruInitError(CruException): pass diff --git a/tools/cru-py/cru/_cru.py b/tools/cru-py/cru/_base.py index 0085a80..96466d1 100644 --- a/tools/cru-py/cru/_cru.py +++ b/tools/cru-py/cru/_base.py @@ -1,8 +1,24 @@ -from typing import Any +from typing import Any, NoReturn from ._lang import remove_none +class CruException(Exception): + """Base exception class of all exceptions in cru.""" + + +class CruNamespaceError(CruException): + """Raised when a namespace is not found.""" + + +class CruUnreachableError(CruException): + """Raised when a code path is unreachable.""" + + +def cru_unreachable() -> NoReturn: + raise CruUnreachableError() + + class _Cru: NAME_PREFIXES = ("CRU_", "Cru", "cru_") @@ -30,13 +46,15 @@ class _Cru: if name is None: continue if self.has_name(name): - raise ValueError(f"Name {name} exists in CRU.") + raise CruNamespaceError(f"Name {name} exists in CRU.") @staticmethod def check_name_format(name: str) -> tuple[str, str]: no_prefix_name = _Cru._maybe_remove_prefix(name) if no_prefix_name is None: - raise ValueError(f"Name {name} is not prefixed with {_Cru.NAME_PREFIXES}.") + raise ValueError( + f"Name {name} is not prefixed with any of {_Cru.NAME_PREFIXES}." + ) return name, no_prefix_name @staticmethod diff --git a/tools/cru-py/cru/_const.py b/tools/cru-py/cru/_const.py index bc02c3a..8246b35 100644 --- a/tools/cru-py/cru/_const.py +++ b/tools/cru-py/cru/_const.py @@ -1,7 +1,7 @@ from enum import Enum, auto from typing import Self, TypeGuard, TypeVar -from ._cru import CRU +from ._base import CRU _T = TypeVar("_T") diff --git a/tools/cru-py/cru/_decorator.py b/tools/cru-py/cru/_decorator.py index 432ceca..137fc05 100644 --- a/tools/cru-py/cru/_decorator.py +++ b/tools/cru-py/cru/_decorator.py @@ -9,7 +9,7 @@ from typing import ( cast, ) -from ._cru import CRU +from ._base import CRU _P = ParamSpec("_P") _T = TypeVar("_T") diff --git a/tools/cru-py/cru/_func.py b/tools/cru-py/cru/_func.py index 1b437be..2b317ab 100644 --- a/tools/cru-py/cru/_func.py +++ b/tools/cru-py/cru/_func.py @@ -13,7 +13,7 @@ from typing import ( ) -from ._cru import CRU +from ._base import CRU from ._const import CruPlaceholder _P = ParamSpec("_P") @@ -163,11 +163,11 @@ class _Creators: class CruFunction: - RawBase = _RawBase - Base = _Base - Creators = _Creators - Wrapper = _Wrapper - Decorators = _Dec + RawBase: TypeAlias = _RawBase + Base: TypeAlias = _Base + Creators: TypeAlias = _Creators + Wrapper: TypeAlias = _Wrapper + Decorators: TypeAlias = _Dec CRU.add_objects(CruFunction) diff --git a/tools/cru-py/cru/_iter.py b/tools/cru-py/cru/_iter.py index 83a9513..bf1dfde 100644 --- a/tools/cru-py/cru/_iter.py +++ b/tools/cru-py/cru/_iter.py @@ -18,7 +18,7 @@ from typing import ( cast, ) -from ._cru import CRU +from ._base import CRU, cru_unreachable from ._const import CruNotFound _P = ParamSpec("_P") @@ -238,7 +238,7 @@ class _Generic: pass except StopIteration as stop: return stop.value - raise RuntimeError("Should not reach here") + cru_unreachable() class _Helper: @@ -255,38 +255,49 @@ class _Helper: return wrapper -class _Dec: - - @staticmethod - def wrap(origin: Callable[_P, Iterable[_T]]) -> Callable[_P, _Wrapper[_T]]: - def _wrapped(*args: _P.args, **kwargs: _P.kwargs) -> _Wrapper[_T]: - return _Wrapper(origin(*args, **kwargs)) - - return _wrapped - - class _Creators: + @staticmethod - @_Dec.wrap - def empty() -> Iterable[Never]: - return iter([]) + def empty( + *, + wrapper_type: type["_Wrapper[Never]"] | None = None, + attr: dict[str, Any] | None = None, + ) -> _Wrapper[Never]: + wrapper_type = wrapper_type or _Wrapper + return wrapper_type(iter([]), attr) @staticmethod - @_Dec.wrap - def range(*args) -> Iterable[int]: - return iter(range(*args)) + def range( + *args, + wrapper_type: type["_Wrapper[int]"] | None = None, + attr: dict[str, Any] | None = None, + ) -> _Wrapper[int]: + wrapper_type = wrapper_type or _Wrapper + return wrapper_type(iter(range(*args)), attr) @staticmethod - @_Dec.wrap - def unite(*args: _O) -> Iterable[_O]: - return iter(args) + def unite( + *args: _O, + wrapper_type: type["_Wrapper[_O]"] | None = None, + attr: dict[str, Any] | None = None, + ) -> _Wrapper[_O]: + wrapper_type = wrapper_type or _Wrapper + return wrapper_type(iter(args), attr) @staticmethod - @_Dec.wrap - def concat(*iterables: Iterable[_T]) -> Iterable[_T]: + def _concat(*iterables: Iterable[_T]) -> Iterable[_T]: for iterable in iterables: yield from iterable + @staticmethod + def concat( + *iterables: Iterable[_T], + wrapper_type: type["_Wrapper[_T]"] | None = None, + attr: dict[str, Any] | None = None, + ) -> _Wrapper[_T]: + wrapper_type = wrapper_type or _Wrapper + return wrapper_type(_Creators._concat(*iterables), attr) + class _Wrapper(Generic[_T]): ElementOperation: TypeAlias = Callable[[_V], Any] @@ -297,10 +308,10 @@ class _Wrapper(Generic[_T]): AnyElementTransformer: TypeAlias = ElementTransformer[Any, Any] def __init__( - self, - iterable: Iterable[_T], + self, iterable: Iterable[_T], attr: dict[str, Any] | None = None ) -> None: self._iterable = iterable + self._attr = attr or {} def __iter__(self) -> Iterator[_T]: return self._iterable.__iter__() @@ -309,23 +320,39 @@ class _Wrapper(Generic[_T]): def me(self) -> Iterable[_T]: return self._iterable - _wrap = _Dec.wrap + @property + def my_attr(self) -> dict[str, Any]: + return self._attr + + def create_with_me(self, iterable: Iterable[_O]) -> _Wrapper[_O]: + return type(self)(iterable, self._attr) # type: ignore + + @staticmethod + def _wrap( + f: Callable[Concatenate[_Wrapper[_T], _P], Iterable[_O]] + ) -> Callable[Concatenate[_Wrapper[_T], _P], _Wrapper[_O]]: + def _wrapped( + self: _Wrapper[_T], *args: _P.args, **kwargs: _P.kwargs + ) -> _Wrapper[_O]: + return self.create_with_me(f(self, *args, **kwargs)) + + return _wrapped @_wrap def replace_me(self, iterable: Iterable[_O]) -> Iterable[_O]: return iterable def replace_me_with_empty(self) -> _Wrapper[Never]: - return _Creators.empty() + return _Creators.empty(wrapper_type=type(self), attr=self._attr) # type: ignore def replace_me_with_range(self, *args) -> _Wrapper[int]: - return _Creators.range(*args) + return _Creators.range(*args, attr=self._attr) def replace_me_with_unite(self, *args: _O) -> _Wrapper[_O]: - return _Creators.unite(*args) + return _Creators.unite(*args, attr=self._attr) def replace_me_with_concat(self, *iterables: Iterable[_T]) -> _Wrapper[_T]: - return _Creators.concat(*iterables) + return _Creators.concat(*iterables, attr=self._attr) def to_set(self, discard: Iterable[Any]) -> set[_T]: return set(self.me) - set(discard) @@ -422,11 +449,10 @@ class _Wrapper(Generic[_T]): class CruIterable: - Decorators = _Dec - Generic = _Generic - Helper = _Helper - Creators = _Creators - Wrapper = _Wrapper + Generic: TypeAlias = _Generic + Helper: TypeAlias = _Helper + Creators: TypeAlias = _Creators + Wrapper: TypeAlias = _Wrapper CRU.add_objects(CruIterable, _Wrapper) diff --git a/tools/cru-py/cru/_list.py b/tools/cru-py/cru/_list.py index f1965db..45122bf 100644 --- a/tools/cru-py/cru/_list.py +++ b/tools/cru-py/cru/_list.py @@ -1,77 +1,75 @@ +from __future__ import annotations +from collections.abc import Callable +from typing import Generic, Iterable, Self, TypeAlias, TypeVar +from ._iter import CruIterable -class CruInplaceList(CruList, Generic[_V]): +_T = TypeVar("_T") - def clear(self) -> "CruInplaceList[_V]": - self.clear() - return self - def extend(self, *l: Iterable[_V]) -> "CruInplaceList[_V]": - self.extend(l) - return self +class CruListEdit(CruIterable.Wrapper[_T]): + def done(self) -> CruList[_T]: + l: CruList[_T] = self.my_attr["list"] + l.reset(self) + return l - def reset(self, *l: Iterable[_V]) -> "CruInplaceList[_V]": + +class CruList(list[_T]): + def reset(self, new_values: Iterable[_T]): + if self is new_values: + new_values = list(new_values) self.clear() - self.extend(l) + self.extend(new_values) return self - def transform(self, *f: OptionalElementTransformer) -> "CruInplaceList"[Any]: - return self.reset(super().transform(*f)) - - def transform_if( - self, f: OptionalElementTransformer, p: ElementPredicate[_V] - ) -> "CruInplaceList"[Any]: - return self.reset(super().transform_if(f, p)) + def as_iterable_wrapper(self) -> CruIterable.Wrapper[_T]: + return CruIterable.Wrapper(self) - def remove_by_indices(self, *index: int) -> "CruInplaceList"[_V]: - return self.reset(super().remove_by_indices(*index)) - - def remove_all_if(self, p: ElementPredicate[_V]) -> "CruInplaceList"[_V]: - return self.reset(super().remove_all_if(p)) - - def remove_all_value(self, *r: Any) -> "CruInplaceList"[_V]: - return self.reset(super().remove_all_value(*r)) - - def replace_all_value( - self, old_value: Any, new_value: R - ) -> "CruInplaceList"[_V | R]: - return self.reset(super().replace_all_value(old_value, new_value)) + def as_edit_iterable_wrapper(self) -> CruListEdit[_T]: + return CruListEdit(self, attr={"list": self}) @staticmethod - def make(l: CanBeList[_V]) -> "CruInplaceList"[_V]: - return CruInplaceList(ListOperations.make(l)) - + def make(maybe_list: Iterable[_T] | _T | None) -> CruList[_T]: + if maybe_list is None: + return CruList() + if isinstance(maybe_list, Iterable): + return CruList(maybe_list) + return CruList([maybe_list]) -K = TypeVar("K") +_K = TypeVar("_K") +_KeyGetter: TypeAlias = Callable[[_T], _K] -class CruUniqueKeyInplaceList(Generic[_V, K]): - KeyGetter = Callable[[_V], K] +class CruUniqueKeyList(Generic[_T, _K]): def __init__( - self, get_key: KeyGetter, *, before_add: Callable[[_V], _V] | None = None + self, + key_getter: _KeyGetter[_T, _K], + *, + before_add: Callable[[_T], _T] | None = None, ): super().__init__() - self._get_key = get_key + self._key_getter = key_getter self._before_add = before_add - self._l: CruInplaceList[_V] = CruInplaceList() + self._list: CruList[_T] = CruList() @property - def object_key_getter(self) -> KeyGetter: - return self._get_key + def key_getter(self) -> _KeyGetter[_T, _K]: + return self._key_getter @property - def internal_list(self) -> CruInplaceList[_V]: - return self._l + def internal_list(self) -> CruList[_T]: + return self._list def validate_self(self): - keys = self._l.transform(self._get_key) + keys = self._list.transform(self._key_getter) if len(keys) != len(set(keys)): raise ValueError("Duplicate keys!") - def get_or(self, k: K, fallback: Any = CRU_NOT_FOUND) -> _V | Any: - r = self._l.find_if(lambda i: k == self._get_key(i)) + # TODO: Continue here! + def get_or(self, key: K, fallback: Any = CRU_NOT_FOUND) -> _V | Any: + r = self._l.find_if(lambda i: k == self._key_getter(i)) return r if r is not CRU_NOT_FOUND else fallback def get(self, k: K) -> _V: @@ -84,10 +82,10 @@ class CruUniqueKeyInplaceList(Generic[_V, K]): return self.get_or(k, CRU_NOT_FOUND) is not CRU_NOT_FOUND def has_any_key(self, *k: K) -> bool: - return self._l.any(lambda i: self._get_key(i) in k) + return self._l.any(lambda i: self._key_getter(i) in k) def try_remove(self, k: K) -> bool: - i = self._l.find_index_if(lambda v: k == self._get_key(v)) + i = self._l.find_index_if(lambda v: k == self._key_getter(v)) if i is CRU_NOT_FOUND: return False self._l.remove_by_indices(i) @@ -98,11 +96,11 @@ class CruUniqueKeyInplaceList(Generic[_V, K]): raise KeyError(f"Key {k} not found!") def add(self, v: _V, /, replace: bool = False) -> None: - if self.has_key(self._get_key(v)): + if self.has_key(self._key_getter(v)): if replace: - self.remove(self._get_key(v)) + self.remove(self._key_getter(v)) else: - raise ValueError(f"Key {self._get_key(v)} already exists!") + raise ValueError(f"Key {self._key_getter(v)} already exists!") if self._before_add is not None: v = self._before_add(v) self._l.append(v) @@ -111,12 +109,12 @@ class CruUniqueKeyInplaceList(Generic[_V, K]): self.add(v, True) def extend(self, l: Iterable[_V], /, replace: bool = False) -> None: - if not replace and self.has_any_key([self._get_key(i) for i in l]): + if not replace and self.has_any_key([self._key_getter(i) for i in l]): raise ValueError("Keys already exists!") if self._before_add is not None: l = [self._before_add(i) for i in l] - keys = [self._get_key(i) for i in l] - self._l.remove_all_if(lambda i: self._get_key(i) in keys).extend(l) + keys = [self._key_getter(i) for i in l] + self._l.remove_all_if(lambda i: self._key_getter(i) in keys).extend(l) def clear(self) -> None: self._l.clear() diff --git a/tools/cru-py/cru/_type.py b/tools/cru-py/cru/_type.py index b67ee9a..c60a913 100644 --- a/tools/cru-py/cru/_type.py +++ b/tools/cru-py/cru/_type.py @@ -1,26 +1,31 @@ from types import NoneType from typing import Any -from ._iter 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): +class TypeSet: + def __init__(self, *types: 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: + 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 @@ -31,12 +36,26 @@ class TypeSet(set[type]): 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: + 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) + 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, + ) |