Coverage for src/extratools_core/crudl.py: 100%
64 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-06-19 04:20 -0700
« prev ^ index » next coverage.py v7.8.1, created at 2025-06-19 04:20 -0700
1from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping
2from typing import Any, cast
5class RLWrapper[KT: Any, VT: Any]:
6 def __init__(
7 self,
8 mapping: Mapping[KT, VT],
9 *,
10 values_in_list: bool = False,
11 ) -> None:
12 self.mapping = mapping
14 self.__values_in_list = values_in_list
16 def read(self, key: KT) -> VT:
17 return self.mapping[key]
19 def list(
20 self,
21 filter_func: Callable[[KT], bool] | None = None,
22 ) -> Iterable[tuple[KT, VT | None]]:
23 if filter_func is None:
24 if self.__values_in_list:
25 yield from self.mapping.items()
26 else:
27 for key in self.mapping:
28 yield key, None
29 else:
30 for key in filter(filter_func, self.mapping):
31 yield key, self.mapping[key] if self.__values_in_list else None
34class CRUDLWrapper[KT: Any, VT: Any](RLWrapper[KT, VT]):
35 def __init__(
36 self,
37 mapping: MutableMapping[KT, VT],
38 *,
39 values_in_list: bool = False,
40 ) -> None:
41 super().__init__(
42 mapping,
43 values_in_list=values_in_list,
44 )
46 self.mapping = mapping
48 def create(self, key: KT, value: VT) -> VT:
49 if key in self.mapping:
50 raise KeyError
52 self.mapping[key] = value
53 return value
55 def update(self, key: KT, value: VT) -> VT:
56 if key not in self.mapping:
57 raise KeyError
59 self.mapping[key] = value
60 return value
62 def delete(self, key: KT) -> VT:
63 default = object()
64 value = self.mapping.pop(key, default)
65 if value == default:
66 raise KeyError
68 return cast("VT", value)
71class RLDict[KT: Any, VT: Any](Mapping[KT, VT]):
72 def __init__(
73 self,
74 *,
75 read_func: Callable[[KT], VT],
76 list_func: Callable[[Any | None], Iterable[tuple[KT, VT | None]]],
77 ) -> None:
78 self.__read_func = read_func
79 self.__list_func = list_func
81 def __getitem__(self, key: KT) -> VT:
82 return self.__read_func(key)
84 def __iter__(self) -> Iterator[KT]:
85 for key, _ in self.__list_func(None):
86 yield key
88 def __len__(self) -> int:
89 # Cannot use `count` in `toolz` as itself depends on this function
90 count = 0
91 for _ in self:
92 count += 1
93 return count
96class CRUDLDict[KT: Any, VT: Any](MutableMapping[KT, VT], RLDict[KT, VT]):
97 def __init__(
98 self,
99 *,
100 create_func: Callable[[KT | None, Any], VT | None],
101 read_func: Callable[[KT], VT],
102 update_func: Callable[[KT, Any], VT | None],
103 delete_func: Callable[[KT], VT | None],
104 list_func: Callable[[Any | None], Iterable[tuple[KT, VT | None]]],
105 ) -> None:
106 RLDict.__init__(
107 self,
108 read_func=read_func,
109 list_func=list_func,
110 )
112 self.__create_func = create_func
113 self.__read_func = read_func
114 self.__update_func = update_func
115 self.__delete_func = delete_func
116 self.__list_func = list_func
118 def __delitem__(self, key: KT) -> None:
119 self.__delete_func(key)
121 def __setitem__(self, key: KT | None, value: VT) -> None:
122 if key is None or key not in self:
123 self.__create_func(key, value)
124 else:
125 self.__update_func(key, value)