Skip to content

Commit 0c22ec6

Browse files
committed
Make it easier to override serializer
1 parent 1e3a1cb commit 0c22ec6

File tree

10 files changed

+70
-33
lines changed

10 files changed

+70
-33
lines changed

docs/config_guide.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Configuration Guide
2+
===================
3+
4+
.. include:: config_example.rst
5+
6+
.. include:: config_nonpermanent.rst
7+
8+
.. include:: config_cleanup.rst
9+
10+
.. include:: config_exceptions.rst
11+
12+
.. include:: config_serialization.rst
13+
14+
.. include:: config_flask.rst

docs/config.rst renamed to docs/config_reference.rst

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,8 @@
1-
Configuration
2-
=============
3-
4-
.. include:: config_example.rst
5-
6-
.. include:: config_nonpermanent.rst
7-
8-
.. include:: config_cleanup.rst
9-
10-
.. include:: config_exceptions.rst
11-
12-
.. include:: config_serialization.rst
1+
Configuration Reference
2+
=========================
133

144
.. include:: config_flask.rst
15-
16-
5+
176
Flask-Session configuration values
187
----------------------------------
198

@@ -79,8 +68,8 @@ These are specific to Flask-Session.
7968
``SESSION_ID_LENGTH``
8069

8170

82-
Storage configuration
83-
---------------------
71+
Storage configuration values
72+
----------------------------
8473

8574

8675
Redis

docs/config_serialization.rst

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,41 @@ Serialization
55

66
Flask-session versions below 1.0.0 use pickle serialization (or fallback) for session storage. While not a direct vulnerability, it is a potential security risk. If you are using a version below 1.0.0, it is recommended to upgrade to the latest version as soon as it's available.
77

8-
From 0.7.0 the serializer is msgspec, which is configurable using ``SESSION_SERIALIZATION_FORMAT``. The default format is ``'msgpack'`` which has 30% storage reduction compared to ``'json'``. The ``'json'`` format may be helpful for debugging, easier viewing or compatibility. Switching between the two should be seamless, even for existing sessions.
8+
From 0.7.0 the serializer is msgspec. The format it uses is configurable with ``SESSION_SERIALIZATION_FORMAT``. The default format is ``'msgpack'`` which has 30% storage reduction compared to ``'json'``. The ``'json'`` format may be helpful for debugging, easier viewing or compatibility. Switching between the two should be seamless, even for existing sessions.
99

1010
All sessions that are accessed or modified while using 0.7.0 will convert to a msgspec format. Once using 1.0.0, any sessions that are still in pickle format will be cleared upon access.
1111

1212
The msgspec library has speed and memory advantages over other libraries. However, if you want to use a different library (such as pickle or orjson), you can override the :attr:`session_interface.serializer`.
13+
14+
If you encounter a TypeError such as: "Encoding objects of type <type> is unsupported", you may be attempting to serialize an unsupported type. In this case, you can either convert the object to a supported type or use a different serializer.
15+
16+
Casting to a supported type:
17+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
18+
19+
.. code-block:: python
20+
21+
session["status"] = str(LazyString('done')))
22+
23+
24+
.. note::
25+
26+
Flask's flash method uses the session to store messages so you must also pass supported types to the flash method.
27+
28+
29+
For a detailed list of supported types by the msgspec serializer, please refer to the official documentation at `msgspec supported types <https://jcristharif.com/msgspec/supported-types.html>`_.
30+
31+
Overriding the serializer:
32+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
33+
34+
.. code-block:: python
35+
36+
from flask_session import Session
37+
import orjson
38+
39+
app = Flask(__name__)
40+
Session(app)
41+
42+
# Override the serializer
43+
app.session_interface.serializer = orjson
44+
45+
Any serializer that has a ``dumps`` and ``loads`` method can be used.

docs/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Table of Contents
77
introduction
88
installation
99
usage
10-
config
10+
config_guide
11+
config_reference
1112
security
1213
api
1314
contributing

src/flask_session/base.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ class Serializer(ABC):
9898
"""Baseclass for session serialization."""
9999

100100
@abstractmethod
101-
def decode(self, serialized_data: bytes) -> dict:
101+
def dumps(self, serialized_data: bytes) -> dict:
102102
"""Deserialize the session data."""
103103
raise NotImplementedError()
104104

105105
@abstractmethod
106-
def encode(self, session: ServerSideSession) -> bytes:
106+
def loads(self, session: ServerSideSession) -> bytes:
107107
"""Serialize the session data."""
108108
raise NotImplementedError()
109109

@@ -126,15 +126,15 @@ def __init__(self, app: Flask, format: str):
126126
else:
127127
raise ValueError(f"Unsupported serialization format: {format}")
128128

129-
def encode(self, session: ServerSideSession) -> bytes:
129+
def dumps(self, data: dict) -> bytes:
130130
"""Serialize the session data."""
131131
try:
132-
return self.encoder.encode(dict(session))
132+
return self.encoder.encode(data)
133133
except Exception as e:
134134
self.app.logger.error(f"Failed to serialize session data: {e}")
135135
raise
136136

137-
def decode(self, serialized_data: bytes) -> dict:
137+
def loads(self, serialized_data: bytes) -> dict:
138138
"""Deserialize the session data."""
139139
# TODO: Remove the pickle fallback in 1.0.0
140140
with suppress(msgspec.DecodeError):

src/flask_session/dynamodb/dynamodb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _retrieve_session_data(self, store_id: str) -> Optional[dict]:
101101
document = self.store.get_item(Key={"id": store_id}).get("Item")
102102
if document:
103103
serialized_session_data = want_bytes(document.get("val").value)
104-
return self.serializer.decode(serialized_session_data)
104+
return self.serializer.loads(serialized_session_data)
105105
return None
106106

107107
def _delete_session(self, store_id: str) -> None:
@@ -112,7 +112,7 @@ def _upsert_session(
112112
) -> None:
113113
storage_expiration_datetime = datetime.utcnow() + session_lifetime
114114
# Serialize the session data
115-
serialized_session_data = self.serializer.encode(session)
115+
serialized_session_data = self.serializer.dumps(dict(session))
116116

117117
self.store.update_item(
118118
Key={

src/flask_session/memcached/memcached.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _retrieve_session_data(self, store_id: str) -> Optional[dict]:
100100
# Get the saved session (item) from the database
101101
serialized_session_data = self.client.get(store_id)
102102
if serialized_session_data:
103-
return self.serializer.decode(serialized_session_data)
103+
return self.serializer.loads(serialized_session_data)
104104
return None
105105

106106
def _delete_session(self, store_id: str) -> None:
@@ -112,7 +112,7 @@ def _upsert_session(
112112
storage_time_to_live = total_seconds(session_lifetime)
113113

114114
# Serialize the session data
115-
serialized_session_data = self.serializer.encode(session)
115+
serialized_session_data = self.serializer.dumps(dict(session))
116116

117117
# Update existing or create new session in the database
118118
self.client.set(

src/flask_session/mongodb/mongodb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def _retrieve_session_data(self, store_id: str) -> Optional[dict]:
7777
document = self.store.find_one({"id": store_id})
7878
if document:
7979
serialized_session_data = want_bytes(document["val"])
80-
return self.serializer.decode(serialized_session_data)
80+
return self.serializer.loads(serialized_session_data)
8181
return None
8282

8383
def _delete_session(self, store_id: str) -> None:
@@ -92,7 +92,7 @@ def _upsert_session(
9292
storage_expiration_datetime = datetime.utcnow() + session_lifetime
9393

9494
# Serialize the session data
95-
serialized_session_data = self.serializer.encode(session)
95+
serialized_session_data = self.serializer.dumps(dict(session))
9696

9797
# Update existing or create new session in the database
9898
if self.use_deprecated_method:

src/flask_session/redis/redis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _retrieve_session_data(self, store_id: str) -> Optional[dict]:
6363
# Get the saved session (value) from the database
6464
serialized_session_data = self.client.get(store_id)
6565
if serialized_session_data:
66-
return self.serializer.decode(serialized_session_data)
66+
return self.serializer.loads(serialized_session_data)
6767
return None
6868

6969
def _delete_session(self, store_id: str) -> None:
@@ -75,7 +75,7 @@ def _upsert_session(
7575
storage_time_to_live = total_seconds(session_lifetime)
7676

7777
# Serialize the session data
78-
serialized_session_data = self.serializer.encode(session)
78+
serialized_session_data = self.serializer.dumps(dict(session))
7979

8080
# Update existing or create new session in the database
8181
self.client.set(

src/flask_session/sqlalchemy/sqlalchemy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def _retrieve_session_data(self, store_id: str) -> Optional[dict]:
149149

150150
if record:
151151
serialized_session_data = want_bytes(record.data)
152-
return self.serializer.decode(serialized_session_data)
152+
return self.serializer.loads(serialized_session_data)
153153
return None
154154

155155
@retry_query()
@@ -168,7 +168,7 @@ def _upsert_session(
168168
storage_expiration_datetime = datetime.utcnow() + session_lifetime
169169

170170
# Serialize session data
171-
serialized_session_data = self.serializer.encode(session)
171+
serialized_session_data = self.serializer.dumps(dict(session))
172172

173173
# Update existing or create new session in the database
174174
try:

0 commit comments

Comments
 (0)