Skip to content

Commit 3d4e67c

Browse files
authored
Merge pull request #73 from amirreza8002/msgspec
add support for msgspec serialization
2 parents fd5dd6c + 66e13ce commit 3d4e67c

File tree

8 files changed

+177
-6
lines changed

8 files changed

+177
-6
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ jobs:
9797
uv run pytest tests/*.py --ds=tests.settings.sqlite -x
9898
uv run pytest tests/*.py --ds=tests.settings.sqlite_herd -x
9999
uv run pytest tests/*.py --ds=tests.settings.sqlite_json -x
100+
uv run pytest tests/*.py --ds=tests.settings.sqlite_msgspec_json -x
101+
uv run pytest tests/*.py --ds=tests.settings.sqlite_msgspec_msgpack -x
100102
uv run pytest tests/*.py --ds=tests.settings.sqlite_msgpack -x
101103
uv run pytest tests/*.py --ds=tests.settings.sqlite_sentinel -x
102104
uv run pytest tests/*.py --ds=tests.settings.sqlite_sentinel_opts -x

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
### new
2+
- added support for msgspec serialization (both json and msgpack)
13

24
### Breaking changes
35
- `BackendCommands` and `AsyncBackendCommands` are no longer decorated with `omit_exception`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Any
2+
3+
import msgspec
4+
5+
from django_valkey.serializers.base import BaseSerializer
6+
7+
8+
class MsgSpecJsonSerializer(BaseSerializer):
9+
def dumps(self, value: Any) -> bytes:
10+
return msgspec.json.encode(value)
11+
12+
def loads(self, value: bytes) -> Any:
13+
return msgspec.json.decode(value)
14+
15+
16+
class MsgSpecMsgPackSerializer(BaseSerializer):
17+
def dumps(self, value: Any) -> bytes:
18+
return msgspec.msgpack.encode(value)
19+
20+
def loads(self, value: bytes) -> Any:
21+
return msgspec.msgpack.decode(value)

docs/configure/advanced_configurations.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,12 @@ and you're good to go
202202

203203
### Use Msgpack serializer
204204

205-
to use the msgpack serializer you should first install the msgpack package as explained in :ref:`msgpack`
205+
to use the msgpack serializer you should first install the msgpack package
206+
207+
```shell
208+
pip install django-valkey[msgpack]
209+
```
210+
206211
then configure your settings like this:
207212

208213
```python
@@ -219,8 +224,43 @@ CACHES = {
219224

220225
and done
221226

222-
### Fun fact
223-
you can serialize every type in the python built-ins, and probably non built-ins, but you have to check which serializer supports that type.
227+
### Use msgspec serializers
228+
229+
msgspec comes with two serializers, one for json, one for msgpack
230+
231+
to use msgspec, first install the extra package:
232+
```shell
233+
pip install django-valkey[msgspec]
234+
```
235+
236+
to use the json serializer, configure your backend like this:
237+
238+
```python
239+
CACHES = {
240+
"default": {
241+
# ...
242+
"OPTIONS": {
243+
"SERIALIZER": "django_valkey.serializer.msgspec.MsgSpecJsonSerializer",
244+
# ...
245+
}
246+
}
247+
}
248+
```
249+
250+
to use the msgpack serializer, configure it like this:
251+
252+
```python
253+
CACHES = {
254+
"default": {
255+
# ...
256+
"OPTIONS": {
257+
"SERIALIZER": "django_valkey.serializer.msgspec.MsgSpecMsgPackSerializer",
258+
# ...
259+
}
260+
}
261+
}
262+
```
263+
224264

225265
## Pluggable Compressors
226266

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ brotli = [
6161
"brotli>=1.1.0",
6262
]
6363

64+
msgspec = [
65+
"msgspec>=0.19.0",
66+
]
67+
6468
[dependency-groups]
6569
dev = [
6670
"anyio>=4.9.0",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
SECRET_KEY = "django_tests_secret_key"
2+
3+
CACHES = {
4+
"default": {
5+
"BACKEND": "django_valkey.cache.ValkeyCache",
6+
"LOCATION": ["valkey://127.0.0.1:6379?db=1", "valkey://127.0.0.1:6379?db=1"],
7+
"OPTIONS": {
8+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
9+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecJsonSerializer",
10+
},
11+
},
12+
"doesnotexist": {
13+
"BACKEND": "django_valkey.cache.ValkeyCache",
14+
"LOCATION": "valkey://127.0.0.1:56379?db=1",
15+
"OPTIONS": {
16+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
17+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecJsonSerializer",
18+
},
19+
},
20+
"sample": {
21+
"BACKEND": "django_valkey.cache.ValkeyCache",
22+
"LOCATION": "valkey://127.0.0.1:6379?db=1,valkey://127.0.0.1:6379?db=1",
23+
"OPTIONS": {
24+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
25+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecJsonSerializer",
26+
},
27+
},
28+
"with_prefix": {
29+
"BACKEND": "django_valkey.cache.ValkeyCache",
30+
"LOCATION": "valkey://127.0.0.1:6379?db=1",
31+
"OPTIONS": {
32+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
33+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecJsonSerializer",
34+
},
35+
"KEY_PREFIX": "test-prefix",
36+
},
37+
}
38+
39+
INSTALLED_APPS = ["django.contrib.sessions"]
40+
41+
USE_TZ = False
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
SECRET_KEY = "django_tests_secret_key"
2+
3+
CACHES = {
4+
"default": {
5+
"BACKEND": "django_valkey.cache.ValkeyCache",
6+
"LOCATION": ["valkey://127.0.0.1:6379?db=1", "valkey://127.0.0.1:6379?db=1"],
7+
"OPTIONS": {
8+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
9+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecMsgPackSerializer",
10+
},
11+
},
12+
"doesnotexist": {
13+
"BACKEND": "django_valkey.cache.ValkeyCache",
14+
"LOCATION": "valkey://127.0.0.1:56379?db=1",
15+
"OPTIONS": {
16+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
17+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecMsgPackSerializer",
18+
},
19+
},
20+
"sample": {
21+
"BACKEND": "django_valkey.cache.ValkeyCache",
22+
"LOCATION": "valkey://127.0.0.1:6379?db=1,valkey://127.0.0.1:6379?db=1",
23+
"OPTIONS": {
24+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
25+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecMsgPackSerializer",
26+
},
27+
},
28+
"with_prefix": {
29+
"BACKEND": "django_valkey.cache.ValkeyCache",
30+
"LOCATION": "valkey://127.0.0.1:6379?db=1",
31+
"OPTIONS": {
32+
"CLIENT_CLASS": "django_valkey.client.DefaultClient",
33+
"SERIALIZER": "django_valkey.serializers.msgspec.MsgSpecMsgPackSerializer",
34+
},
35+
"KEY_PREFIX": "test-prefix",
36+
},
37+
}
38+
39+
INSTALLED_APPS = ["django.contrib.sessions"]
40+
41+
USE_TZ = False

tests/test_backend.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
from django_valkey.cluster_cache.client import DefaultClusterClient
1919
from django_valkey.serializers.json import JSONSerializer
2020
from django_valkey.serializers.msgpack import MSGPackSerializer
21+
from django_valkey.serializers.msgspec import (
22+
MsgSpecJsonSerializer,
23+
MsgSpecMsgPackSerializer,
24+
)
2125
from django_valkey.serializers.pickle import PickleSerializer
2226

2327

@@ -128,7 +132,15 @@ def test_save_unicode(self, cache: ValkeyCache):
128132
assert res == "heló"
129133

130134
def test_save_dict(self, cache: ValkeyCache):
131-
if isinstance(cache.client._serializer, (JSONSerializer, MSGPackSerializer)):
135+
if isinstance(
136+
cache.client._serializer,
137+
(
138+
JSONSerializer,
139+
MSGPackSerializer,
140+
MsgSpecJsonSerializer,
141+
MsgSpecMsgPackSerializer,
142+
),
143+
):
132144
# JSONSerializer and MSGPackSerializer use the isoformat for
133145
# datetimes.
134146
now_dt: str | datetime.datetime = datetime.datetime.now().isoformat()
@@ -1099,9 +1111,17 @@ def test_sismember_memoryview(self, cache: ValkeyCache):
10991111
assert cache.sismember("foo", wrong_val) is False
11001112

11011113
def test_sismember_complex(self, cache: ValkeyCache):
1102-
if isinstance(cache.client._serializer, (JSONSerializer, MSGPackSerializer)):
1114+
if isinstance(
1115+
cache.client._serializer,
1116+
(
1117+
JSONSerializer,
1118+
MSGPackSerializer,
1119+
MsgSpecJsonSerializer,
1120+
MsgSpecMsgPackSerializer,
1121+
),
1122+
):
11031123
pytest.skip(
1104-
"JSONSerializer/MSGPackSerializer doesn't support the complex type"
1124+
"JSONSerializer/MSGPackSerializer and neither the msgspec serializers don't support the complex type"
11051125
)
11061126
cache.sadd("foo", 3j)
11071127
assert cache.sismember("foo", 3j) is True

0 commit comments

Comments
 (0)