Coverage for src/extratools_core/crudl.py: 100%
55 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-06-12 18:57 -0700
« prev ^ index » next coverage.py v7.8.1, created at 2025-06-12 18:57 -0700
1from collections.abc import Callable, Iterable, Iterator, MutableMapping
2from typing import Any, cast
5class CRUDLWrapper[KT: Any, VT: Any]:
6 def __init__(
7 self,
8 mapping: MutableMapping[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 create(self, key: KT, value: VT) -> VT:
17 if key in self.mapping:
18 raise KeyError
20 self.mapping[key] = value
21 return value
23 def read(self, key: KT) -> VT:
24 return self.mapping[key]
26 def update(self, key: KT, value: VT) -> VT:
27 if key not in self.mapping:
28 raise KeyError
30 self.mapping[key] = value
31 return value
33 def delete(self, key: KT) -> VT:
34 default = object()
35 value = self.mapping.pop(key, default)
36 if value == default:
37 raise KeyError
39 return cast("VT", value)
41 def list(
42 self,
43 filter_func: Callable[[KT], bool] | None = None,
44 ) -> Iterable[tuple[KT, VT | None]]:
45 if filter_func is None:
46 if self.__values_in_list:
47 yield from self.mapping.items()
48 else:
49 for key in self.mapping:
50 yield key, None
51 else:
52 for key in filter(filter_func, self.mapping):
53 yield key, self.mapping[key] if self.__values_in_list else None
56class CRUDLDict[KT: Any, VT: Any](MutableMapping[KT, VT]):
57 def __init__(
58 self,
59 *,
60 create_func: Callable[[KT | None, Any], VT | None],
61 read_func: Callable[[KT], VT],
62 update_func: Callable[[KT, Any], VT | None],
63 delete_func: Callable[[KT], VT | None],
64 list_func: Callable[[Any | None], Iterable[tuple[KT, VT | None]]],
65 ) -> None:
66 self.__create_func = create_func
67 self.__read_func = read_func
68 self.__update_func = update_func
69 self.__delete_func = delete_func
70 self.__list_func = list_func
72 def __delitem__(self, key: KT) -> None:
73 self.__delete_func(key)
75 def __getitem__(self, key: KT) -> VT:
76 return self.__read_func(key)
78 def __setitem__(self, key: KT | None, value: VT) -> None:
79 if key is None or key not in self:
80 self.__create_func(key, value)
81 else:
82 self.__update_func(key, value)
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