feat(redis): add native JSON session storage for 4.1.0
Build & Publish Package / publish (push) Successful in 49s
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:
@@ -1,5 +1,4 @@
|
||||
# Quart-Session
|
||||
|
||||
 [](https://pypi.org/project/Quart-Session/) 
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user