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 | 0a0641ae7165092f8bb064526cf319754905657c (patch) | |
| tree | b09dd63c2a99c3b3ff9f30f46a5bf5ff6f054964 | |
| parent | ea0ad38152d7ebad5daf9f6c2f5053168a63645d (diff) | |
| download | crupest-0a0641ae7165092f8bb064526cf319754905657c.tar.gz crupest-0a0641ae7165092f8bb064526cf319754905657c.tar.bz2 crupest-0a0641ae7165092f8bb064526cf319754905657c.zip | |
HALF WORK: 2024.12.12
| -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, +            ) | 
