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

49 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-09 03:05 -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 __iter__(self) -> Iterator[str]: 

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

53 yield cast("str", key) 

54 

55 @override 

56 def clear(self) -> None: 

57 self.__client.flushdb() 

58 

59 @override 

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

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

62 self.__client.delete(key) 

63 return response 

64 

65 return default 

66 

67 @override 

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

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

70 if number_deleted == 0: 

71 raise KeyError 

72 

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

74 

75 @override 

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

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

78 raise TypeError(self.__BAD_BLOB_CLASS_ERROR_MESSAGE) 

79 

80 self.__client.set( 

81 key, 

82 ( 

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

84 else blob.as_b64_str() 

85 ), 

86 px=self.__ttl_ms, 

87 )