feat(redis): add native JSON session storage for 4.1.0
Build & Publish Package / publish (push) Successful in 49s

- Add opt-in SESSION_REDIS_JSON storage for asyncio Redis backends.
- Check JSON command support at startup and reject JSON mode on Trio.
- Read legacy strings and migrate sessions on their next save.
- Validate JSON objects before transactional replacement and expiry.
- Preserve tagged serialization for datetime, tuple, and other types.
- Cover command contracts, error handling, and session round-trips.
- Document server requirements, permissions, and migration limits.
- Normalize indentation and whitespace across code, tests, and examples.
- Bump the package and lockfile to 4.1.0 and update the changelog.
- Refresh the locked pycparser dependency to 3.12.
This commit is contained in:
2026-10-23 20:26:24 +02:00
parent 5dc4a8f6cb
commit 4939bbe987
17 changed files with 1024 additions and 843 deletions
+34 -37
View File
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
"""
quart_session.redis_trio.serialization
~~~~~~~~~~~~~~~~~~~~~~
quart_session.redis_trio.serialization
~~~~~~~~~~~~~~~~~~~~~~
A simple Redis Trio client.
A simple Redis Trio client.
:copyright: (c) 2017 by Bogdan Paul Popa.
:copyright: (c) 2019 by Oleksii Aleksieiev.
:copyright: (c) 2020 by dsc.
:license: BSD, see LICENSE for more details.
:copyright: (c) 2017 by Bogdan Paul Popa.
:copyright: (c) 2019 by Oleksii Aleksieiev.
:copyright: (c) 2020 by dsc.
:license: BSD, see LICENSE for more details.
"""
from collections import namedtuple
@@ -19,42 +19,39 @@ atom = namedtuple("atom", ("value",))
#: The set of characters that must be escaped before being sent as
#: Redis strings.
escapes = {
ord(b"\0"): rb"\x00",
ord(b"\n"): rb"\n",
ord(b"\r"): rb"\r",
ord(b"\\"): rb"\\",
ord(b'"'): rb'\"',
ord(b"\0"): rb"\x00",
ord(b"\n"): rb"\n",
ord(b"\r"): rb"\r",
ord(b"\\"): rb"\\",
ord(b'"'): rb'\"',
}
def serialize(x):
"""Serialize `x` so that it can safely be sent to Redis.
"""Serialize `x` so that it can safely be sent to Redis.
Parameters:
x(object): The value to serialize.
Returns:
bytes: The serialized value.
"""
if isinstance(x, atom):
return x.value
elif isinstance(x, bytes):
return quote(x)
elif isinstance(x, str):
return quote(x.encode("utf-8"))
elif isinstance(x, (float, int)):
return str(x).encode("ascii")
else:
return serialize(str(x))
Parameters:
x(object): The value to serialize.
Returns:
bytes: The serialized value.
"""
if isinstance(x, atom):
return x.value
elif isinstance(x, bytes):
return quote(x)
elif isinstance(x, str):
return quote(x.encode("utf-8"))
elif isinstance(x, (float, int)):
return str(x).encode("ascii")
else:
return serialize(str(x))
def quote(bs):
return b'"' + bytes(escape(bs)) + b'"'
return b'"' + bytes(escape(bs)) + b'"'
def escape(bs):
for c in bs:
if c in escapes:
yield from escapes[c]
else:
yield c
for c in bs:
if c in escapes:
yield from escapes[c]
else:
yield c