Skip to content

Commit cc02cd6

Browse files
committed
moved common functionality out of the client class and into functions
functions that are used by almost all the operations, `encode`, `decode`, `make_key` and `make_pattern` are moved out of the client class the reason is the give an easy interface to users who want to use the raw client in their code
1 parent d4db676 commit cc02cd6

File tree

3 files changed

+102
-52
lines changed

3 files changed

+102
-52
lines changed

django_valkey/base_client.py

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import builtins
22
from collections.abc import Iterable
33
import random
4-
import re
54
import socket
6-
from contextlib import suppress
75
from typing import (
86
Any,
97
TYPE_CHECKING,
@@ -22,9 +20,8 @@
2220

2321
from django_valkey import pool
2422
from django_valkey.compressors.identity import IdentityCompressor
25-
from django_valkey.exceptions import CompressorError
2623
from django_valkey.serializers.pickle import PickleSerializer
27-
from django_valkey.util import CacheKey
24+
from django_valkey.util import make_key, make_pattern, encode, decode
2825
from django_valkey.typings import KeyT
2926

3027
if TYPE_CHECKING:
@@ -33,12 +30,6 @@
3330

3431
_main_exceptions = (TimeoutError, ResponseError, ConnectionError, socket.timeout)
3532

36-
special_re = re.compile("([*?[])")
37-
38-
39-
def glob_escape(s: str) -> str:
40-
return special_re.sub(r"[\1]", s)
41-
4233

4334
class BaseClient:
4435
def __init__(
@@ -127,59 +118,34 @@ def decode(self, value: bytes) -> Any:
127118
"""
128119
Decode the given value.
129120
"""
130-
try:
131-
value = int(value) if value.isdigit() else float(value)
132-
except (ValueError, TypeError):
133-
# Handle little values, chosen to be not compressed
134-
with suppress(CompressorError):
135-
value = self._compressor.decompress(value)
136-
value = self._serializer.loads(value)
137-
except AttributeError:
138-
# if value is None:
139-
return value
140-
return value
121+
return decode(value, serializer=self._serializer, compressor=self._compressor)
141122

142123
def encode(self, value: EncodableT) -> bytes | int | float:
143124
"""
144125
Encode the given value.
145126
"""
146-
147-
if type(value) is not int and type(value) is not float:
148-
value = self._serializer.dumps(value)
149-
return self._compressor.compress(value)
150-
151-
return value
127+
return encode(value, serializer=self._serializer, compressor=self._compressor)
152128

153129
def make_key(
154130
self, key: KeyT, version: int | None = None, prefix: str | None = None
155131
) -> KeyT:
156132
"""Return key as a CacheKey instance so it has additional methods"""
157-
if isinstance(key, CacheKey):
158-
return key
159-
160-
if prefix is None:
161-
prefix = self._backend.key_prefix
162-
163-
if version is None:
164-
version = self._backend.version
165-
166-
return CacheKey(self._backend.key_func(key, prefix, version))
133+
return make_key(
134+
key,
135+
version=version or self._backend.version,
136+
prefix=prefix or self._backend.key_prefix,
137+
key_func=self._backend.key_func,
138+
)
167139

168140
def make_pattern(
169141
self, pattern: str, version: int | None = None, prefix: str | None = None
170142
) -> KeyT:
171-
if isinstance(pattern, CacheKey):
172-
return pattern
173-
174-
if prefix is None:
175-
prefix = self._backend.key_prefix
176-
prefix = glob_escape(prefix)
177-
178-
if version is None:
179-
version = self._backend.version
180-
version_str = glob_escape(str(version))
181-
182-
return CacheKey(self._backend.key_func(pattern, prefix, version_str))
143+
return make_pattern(
144+
pattern,
145+
version=version or self._backend.version,
146+
prefix=prefix or self._backend.key_prefix,
147+
key_func=self._backend.key_func,
148+
)
183149

184150
def _decode_iterable_result(
185151
self, result: Any, convert_to_set: bool = True

django_valkey/util.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
from contextlib import suppress
2+
import re
3+
from typing import Any
4+
5+
from django.core.cache.backends.base import get_key_func
6+
7+
from valkey.typing import EncodableT
8+
9+
from django_valkey.compressors.identity import IdentityCompressor
10+
from django_valkey.exceptions import CompressorError
11+
from django_valkey.serializers.pickle import PickleSerializer
12+
from django_valkey.typings import KeyT
13+
14+
115
class CacheKey(str):
216
"""
317
A stub string class that we can use to check if a key was created already.
@@ -9,3 +23,73 @@ def original_key(self) -> str:
923

1024
def default_reverse_key(key: str) -> str:
1125
return key.split(":", 2)[2]
26+
27+
28+
def make_key(
29+
key: KeyT,
30+
version: int | None = 1,
31+
prefix: str | None = "",
32+
key_func=get_key_func(None),
33+
) -> KeyT:
34+
"""Return key as a CacheKey instance so it has additional methods"""
35+
if isinstance(key, CacheKey):
36+
return key
37+
38+
return CacheKey(key_func(key, prefix, version))
39+
40+
41+
special_re = re.compile("([*?[])")
42+
43+
44+
def glob_escape(s: str) -> str:
45+
return special_re.sub(r"[\1]", s)
46+
47+
48+
def make_pattern(
49+
pattern: str,
50+
version: int | None = 1,
51+
prefix: str | None = "",
52+
key_func=get_key_func(None),
53+
) -> KeyT:
54+
if isinstance(pattern, CacheKey):
55+
return pattern
56+
57+
prefix = glob_escape(prefix)
58+
version_str = glob_escape(str(version))
59+
60+
return CacheKey(key_func(pattern, prefix, version_str))
61+
62+
63+
def decode(
64+
value: bytes, serializer=PickleSerializer({}), compressor=IdentityCompressor({})
65+
) -> Any:
66+
"""
67+
Decode the given value with the specified serializer and compressor.
68+
"""
69+
try:
70+
value = int(value) if value.isdigit() else float(value) # type: ignore[assignment]
71+
except (ValueError, TypeError):
72+
# Handle little values, chosen to be not compressed
73+
with suppress(CompressorError):
74+
value = compressor.decompress(value)
75+
value = serializer.loads(value)
76+
except AttributeError:
77+
# if value is None:
78+
return value
79+
return value
80+
81+
82+
def encode(
83+
value: EncodableT,
84+
serializer=PickleSerializer({}),
85+
compressor=IdentityCompressor({}),
86+
) -> bytes | int | float:
87+
"""
88+
Encode the given value using the specified serializer and compressor.
89+
"""
90+
91+
if type(value) is not int and type(value) is not float:
92+
value = serializer.dumps(value)
93+
return compressor.compress(value)
94+
95+
return value

tests/test_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def test_delete_pattern_calls_pipeline_delete_and_execute(
159159

160160

161161
class TestShardClient:
162-
@patch("tests.test_client.DefaultClient.make_pattern")
162+
@patch("django_valkey.base_client.BaseClient.make_pattern")
163163
@patch("tests.test_client.ShardClient.__init__", return_value=None)
164164
def test_delete_pattern_calls_scan_iter_with_count_if_itersize_given(
165165
self, init_mock, make_pattern_mock
@@ -178,7 +178,7 @@ def test_delete_pattern_calls_scan_iter_with_count_if_itersize_given(
178178
count=10, match=make_pattern_mock.return_value
179179
)
180180

181-
@patch("tests.test_client.DefaultClient.make_pattern")
181+
@patch("django_valkey.base_client.BaseClient.make_pattern")
182182
@patch("tests.test_client.ShardClient.__init__", return_value=None)
183183
def test_delete_pattern_calls_scan_iter(self, init_mock, make_pattern_mock):
184184
client = ShardClient()
@@ -194,7 +194,7 @@ def test_delete_pattern_calls_scan_iter(self, init_mock, make_pattern_mock):
194194
match=make_pattern_mock.return_value
195195
)
196196

197-
@patch("tests.test_client.DefaultClient.make_pattern")
197+
@patch("django_valkey.base_client.BaseClient.make_pattern")
198198
@patch("tests.test_client.ShardClient.__init__", return_value=None)
199199
def test_delete_pattern_calls_delete_for_given_keys(
200200
self, init_mock, make_pattern_mock

0 commit comments

Comments
 (0)