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
+41 -34
View File
@@ -1,5 +1,4 @@
# Quart-Session
![pyversions](https://img.shields.io/pypi/pyversions/Quart-Session.svg) [![pypiversion](https://badge.fury.io/py/Quart-Session.svg)](https://pypi.org/project/Quart-Session/) ![PyPI license](https://img.shields.io/pypi/l/Quart-Session.svg)
Quart-Session is an extension for [Quart](https://gitlab.com/pgjones/quart/blob/master/README.rst) that adds support for
@@ -8,7 +7,6 @@ server-side sessions to your application.
Based on [flask-session](https://pypi.org/project/Flask-Session/).
## Quick start
Quart-Session can be installed via pipenv or pip,
```bash
@@ -28,21 +26,19 @@ Session(app)
@app.route('/')
async def hello():
session["foo"] = "bar"
return "session key 'foo' set"
session["foo"] = "bar"
return "session key 'foo' set"
@app.route('/foo')
async def foo():
return session.get("foo", "session key 'foo' not found")
return session.get("foo", "session key 'foo' not found")
app.run()
```
## Features
### Redis
via `redis>=4.4.0`.
```python3
@@ -68,18 +64,42 @@ app.config['SESSION_TYPE'] = 'redis'
@app.before_serving
async def setup():
cache = await aioredis.Redis(
host="foobar.com",
port=6379,
password="foobar"
)
app.config['SESSION_REDIS'] = cache
Session(app)
cache = await aioredis.Redis(
host="foobar.com",
port=6379,
password="foobar"
)
app.config['SESSION_REDIS'] = cache
Session(app)
```
#### Trio
#### Native JSON storage (Redis / Valkey)
By default, session values are JSON-encoded **strings**. To store `session:*`
keys as native JSON documents instead:
```python3
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS_JSON'] = True
```
This requires a Redis or Valkey server with `JSON.GET` and `JSON.SET` support
(e.g. Redis with JSON support or Valkey with a compatible JSON module).
It is opt-in and supported only by the asyncio Redis backend. Unsupported
servers fail during startup rather than silently falling back to strings.
The Redis account needs permission for the JSON commands, `COMMAND INFO`,
`GET`, `DEL`, `EXPIRE`, and transactions.
Existing string sessions remain readable and are converted on their next save;
untouched keys are not migrated. Expiry is applied in the same transaction as
the JSON write. The tagged JSON serializer is retained, including support for
extended Python types. Custom serializers must produce JSON object strings.
Disabling this option does not convert JSON keys back into strings.
For example, inspect the stored object with `JSON.GET session:<sid> $` rather
than `GET session:<sid>`.
#### Trio
Quart-Session comes with [an (experimental) Redis client](quart_session/redis_trio) for use with the [Trio](https://trio.readthedocs.io/en/stable/) eventloop.
```python3
@@ -92,7 +112,6 @@ Session(app)
```
### Memcached
via `aiomcache`.
```python3
@@ -102,7 +121,6 @@ Session(app)
```
### MongoDB
via `motor`.
```python3
@@ -114,7 +132,6 @@ Session(app)
```
### JSON serializer
[flask-session](https://pypi.org/project/Flask-Session/) uses `pickle`
for session data while Quart-Session uses [a JSON serializer](https://gitlab.com/pgjones/quart/blob/37e249b9b146824a8668eaa1daa12392aeb00256/src/quart/json/tag.py#L141)
capable of serializing the usual JSON types, as well as: `Tuple`, `Bytes`,
@@ -132,32 +149,29 @@ app.config['SESSION_TYPE'] = 'redis'
Session(app)
try:
import cPickle as pickle
import cPickle as pickle
except ImportError:
import pickle
import pickle
app.session_interface.serialize = pickle
```
### Back-end usage
At any point you may interface with the session back-end directly:
```python3
from quart_session.sessions import SessionInterface
@app.route("/")
async def hello():
cache: SessionInterface = app.session_interface
await cache.set("random_key", "val", expiry=3600)
data = await cache.get("random_key")
cache: SessionInterface = app.session_interface
await cache.set("random_key", "val", expiry=3600)
data = await cache.get("random_key")
```
The interface will have the `get`, `set`, and `delete` methods available (regardless of
back-end - similar to how [aiocache](https://github.com/argaen/aiocache) works).
### Performance
[flask-session](https://pypi.org/project/Flask-Session/) sets a
session for each incoming request, including static files. From experience,
this often puts unneeded load on underlying session infrastructure,
@@ -167,9 +181,7 @@ Quart-Session only contacts the back-end when a session changed (or created). In
static file serves never emit a `Set-Cookie` header. If you'd like to enable
this though, set `SESSION_STATIC_FILE` to `True`.
### Session pinning
Associates an user's session to his/her IP address. This mitigates cookie stealing via XSS etc, and is handy
for web applications that require extra security.
@@ -187,16 +199,13 @@ likely provides the `X-Forwarded-For` header which you **must** make use of
by explicitly setting `SESSION_REVERSE_PROXY` to `True`.
## Future development
- `FileSystemSessionInterface`
- `GoogleCloudDatastoreSessionInterface`
- Pytest
## Flask-Session
This library works very similarly to [flask-session](https://pypi.org/project/Flask-Session/).
The changes are specified below:
- Quart-Session does not emit a `Set-Cookie` on every request.
- Quart-Session does not emit a `Set-Cookie` on static file serves.
- Quart-Session uses a different serializer: `quart.json.tag.TaggedJSONSerializer` instead of `pickle`.
@@ -205,9 +214,7 @@ The changes are specified below:
- Quart-Session might not have all the back-end interfaces implemented (yet), such as "filesystem".
## Help
Find the Quart folk on [gitter](https://gitter.im/python-quart/lobby) or open an issue.
## License
BSD