Coverage for src/extratools_core/crudl.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.8.1, created at 2025-06-11 20:59 -0700

1from collections.abc import Callable, Iterable, Iterator, MutableMapping 

2from typing import Any, cast 

3 

4 

5class CRUDLWrapper[KT: Any, VT: Any]: 

6 def __init__(self, mapping: MutableMapping[KT, VT]) -> None: 

7 self.mapping = mapping 

8 

9 def create(self, key: KT, value: VT) -> VT: 

10 if key in self.mapping: 

11 raise KeyError 

12 

13 self.mapping[key] = value 

14 return value 

15 

16 def read(self, key: KT) -> VT: 

17 return self.mapping[key] 

18 

19 def update(self, key: KT, value: VT) -> VT: 

20 if key not in self.mapping: 

21 raise KeyError 

22 

23 self.mapping[key] = value 

24 return value 

25 

26 def delete(self, key: KT) -> VT: 

27 default = object() 

28 value = self.mapping.pop(key, default) 

29 if value == default: 

30 raise KeyError 

31 

32 return cast("VT", value) 

33 

34 def list(self, filter_func: Callable[[KT], bool] | None = None) -> Iterable[tuple[KT, VT]]: 

35 if filter_func is None: 

36 yield from self.mapping.items() 

37 else: 

38 for key in filter(filter_func, self.mapping): 

39 yield key, self.mapping[key] 

40 

41 

42class CRUDLDict[KT: Any, VT: Any](MutableMapping[KT, VT]): 

43 def __init__( 

44 self, 

45 *, 

46 create_func: Callable[[KT | None, Any], VT | None], 

47 read_func: Callable[[KT], VT], 

48 update_func: Callable[[KT, Any], VT | None], 

49 delete_func: Callable[[KT], VT | None], 

50 list_func: Callable[[Any | None], Iterable[tuple[KT, VT | None]]], 

51 ) -> None: 

52 self.__create_func = create_func 

53 self.__read_func = read_func 

54 self.__update_func = update_func 

55 self.__delete_func = delete_func 

56 self.__list_func = list_func 

57 

58 def __delitem__(self, key: KT) -> None: 

59 self.__delete_func(key) 

60 

61 def __getitem__(self, key: KT) -> VT: 

62 return self.__read_func(key) 

63 

64 def __setitem__(self, key: KT | None, value: VT) -> None: 

65 if key is None or key not in self: 

66 self.__create_func(key, value) 

67 else: 

68 self.__update_func(key, value) 

69 

70 def __iter__(self) -> Iterator[KT]: 

71 for key, _ in self.__list_func(None): 

72 yield key 

73 

74 def __len__(self) -> int: 

75 # Cannot use `count` in `toolz` as itself depends on this function 

76 count = 0 

77 for _ in self: 

78 count += 1 

79 return count