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

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

2from typing import Any, cast 

3 

4 

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 

13 

14 self.__values_in_list = values_in_list 

15 

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

17 if key in self.mapping: 

18 raise KeyError 

19 

20 self.mapping[key] = value 

21 return value 

22 

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

24 return self.mapping[key] 

25 

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

27 if key not in self.mapping: 

28 raise KeyError 

29 

30 self.mapping[key] = value 

31 return value 

32 

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

34 default = object() 

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

36 if value == default: 

37 raise KeyError 

38 

39 return cast("VT", value) 

40 

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 

54 

55 

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 

71 

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

73 self.__delete_func(key) 

74 

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

76 return self.__read_func(key) 

77 

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) 

83 

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

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

86 yield key 

87 

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