Coverage for src/blob_dict/dict/valkey.py: 0%

55 statements  

« prev     ^ index     » next       coverage.py v7.8.1, created at 2025-05-21 20:53 -0700

1from collections.abc import Iterator 

2from datetime import timedelta 

3from typing import Any, cast, override 

4 

5from valkey import Valkey 

6 

7from ..blob import BytesBlob, StrBlob 

8from . import BlobDictBase 

9 

10 

11class ValkeyBlobDict(BlobDictBase): 

12 def __init__( 

13 self, 

14 *, 

15 ttl: timedelta | None = None, 

16 str_blob_only: bool = False, 

17 client_kwargs: dict[str, Any] | None = None, 

18 ) -> None: 

19 super().__init__() 

20 

21 self.__client: Valkey = Valkey( 

22 **(client_kwargs or {}), 

23 decode_responses=True, 

24 ) 

25 

26 self.__ttl_ms: int | None = int(ttl.total_seconds() * 1_000) if ttl else None 

27 

28 self.__str_blob_only: bool = str_blob_only 

29 

30 @override 

31 def __len__(self) -> int: 

32 return cast("int", self.__client.dbsize()) 

33 

34 @override 

35 def __contains__(self, key: str) -> bool: 

36 return cast("int", self.__client.exists(key)) == 1 

37 

38 @override 

39 def get(self, key: str, /, default: BytesBlob | None = None) -> BytesBlob | None: 

40 response: Any = self.__client.get(key) 

41 if not response: 

42 return default 

43 

44 s: str = cast("str", response) 

45 return ( 

46 StrBlob(s) if self.__str_blob_only 

47 else BytesBlob.from_b64_str(s) 

48 ) 

49 

50 @override 

51 def __getitem__(self, key: str, /) -> BytesBlob: 

52 blob: BytesBlob | None = self.get(key) 

53 if blob is None: 

54 raise KeyError 

55 

56 return blob 

57 

58 @override 

59 def __iter__(self) -> Iterator[str]: 

60 for key in self.__client.scan_iter(_type="STRING"): 

61 yield cast("str", key) 

62 

63 @override 

64 def clear(self) -> None: 

65 self.__client.flushdb() 

66 

67 @override 

68 def pop(self, key: str, /, default: BytesBlob | None = None) -> BytesBlob | None: 

69 if response := self.get(key): 

70 self.__client.delete(key) 

71 return response 

72 

73 return default 

74 

75 @override 

76 def __delitem__(self, key: str, /) -> None: 

77 number_deleted: int = cast("int", self.__client.delete(key)) 

78 if number_deleted == 0: 

79 raise KeyError 

80 

81 __BAD_BLOB_CLASS_ERROR_MESSAGE: str = "Must specify blob of type `StrBlob`" 

82 

83 @override 

84 def __setitem__(self, key: str, blob: BytesBlob, /) -> None: 

85 if self.__str_blob_only and not isinstance(blob, StrBlob): 

86 raise TypeError(self.__BAD_BLOB_CLASS_ERROR_MESSAGE) 

87 

88 self.__client.set( 

89 key, 

90 ( 

91 cast("StrBlob", blob).as_str() if self.__str_blob_only 

92 else blob.as_b64_str() 

93 ), 

94 px=self.__ttl_ms, 

95 )