Coverage for src/blob_dict/dict/valkey.py: 0%
57 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-29 23:07 -0700
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-29 23:07 -0700
1from collections.abc import Iterator
2from datetime import timedelta
3from typing import Any, Literal, cast, override
5from valkey import Valkey
7from ..blob import BytesBlob, StrBlob
8from . import BlobDictBase
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__()
21 self.__client: Valkey = Valkey(
22 **(client_kwargs or {}),
23 decode_responses=True,
24 )
26 self.__ttl_ms: int | None = int(ttl.total_seconds() * 1_000) if ttl else None
28 self.__str_blob_only: bool = str_blob_only
30 @override
31 def __len__(self) -> int:
32 return cast("int", self.__client.dbsize())
34 @override
35 def __contains__(self, key: object) -> bool:
36 return cast("int", self.__client.exists(str(key))) == 1
38 @override
39 def get[T: Any](self, key: str, /, default: BytesBlob | T = None) -> BytesBlob | T:
40 response: Any = self.__client.get(key)
41 if not response:
42 return default
44 s: str = cast("str", response)
45 return (
46 StrBlob(s) if self.__str_blob_only
47 else BytesBlob.from_b64_str(s)
48 )
50 @override
51 def __getitem__(self, key: str, /) -> BytesBlob:
52 blob: BytesBlob | None = self.get(key)
53 if blob is None:
54 raise KeyError
56 return blob
58 @override
59 def __iter__(self) -> Iterator[str]:
60 for key in self.__client.scan_iter(_type="STRING"):
61 yield cast("str", key)
63 @override
64 def clear(self) -> None:
65 self.__client.flushdb()
67 @override
68 def pop[T: Any](
69 self,
70 key: str,
71 /,
72 default: BytesBlob | T | Literal["__DEFAULT"] = "__DEFAULT",
73 ) -> BytesBlob | T:
74 if response := self.get(key):
75 self.__client.delete(key)
76 return response
78 if default == "__DEFAULT":
79 raise KeyError
81 return default
83 @override
84 def __delitem__(self, key: str, /) -> None:
85 number_deleted: int = cast("int", self.__client.delete(key))
86 if number_deleted == 0:
87 raise KeyError
89 __BAD_BLOB_CLASS_ERROR_MESSAGE: str = "Must specify blob of type `StrBlob`"
91 @override
92 def __setitem__(self, key: str, blob: BytesBlob, /) -> None:
93 if self.__str_blob_only and not isinstance(blob, StrBlob):
94 raise TypeError(self.__BAD_BLOB_CLASS_ERROR_MESSAGE)
96 self.__client.set(
97 key,
98 (
99 cast("StrBlob", blob).as_str() if self.__str_blob_only
100 else blob.as_b64_str()
101 ),
102 px=self.__ttl_ms,
103 )