Skip to content

Commit eb3068d

Browse files
committed
Move serialization to a middleware
1 parent a63628b commit eb3068d

File tree

3 files changed

+89
-78
lines changed

3 files changed

+89
-78
lines changed

docs/extend.rst

+8-3
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@ for ``datetime`` objects could look like:
2222
def decode(self, s):
2323
return datetime.strptime(s, '%Y-%m-%dT%H:%M:%S')
2424
25-
We can use this Serializer like this:
25+
To use the new serializer, we need to use the serialization middleware:
2626

2727
.. code-block:: python
2828
29-
>>> db = TinyDB('db.json')
30-
>>> db.register_serializer(DateTimeSerializer(), 'TinyDate')
29+
>>> from tinydb.storages import JSONStorage
30+
>>> from tinydb.middlewares import SerializationMiddleware
31+
>>>
32+
>>> serialization = SerializationMiddleware()
33+
>>> serialization.register_serializer(DateTimeSerializer(), 'TinyDate')
34+
>>>
35+
>>> db = TinyDB('db.json', storage=serialization)
3136
>>> db.insert({'date': datetime(2000, 1, 1, 12, 0, 0)})
3237
>>> db.all()
3338
[{'date': datetime.datetime(2000, 1, 1, 12, 0)}]

tinydb/database.py

+5-73
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class TinyDB(object):
3030
and getting tables.
3131
"""
3232

33+
DEFAULT_STORAGE = JSONStorage
34+
3335
def __init__(self, *args, **kwargs):
3436
"""
3537
Create a new instance of TinyDB.
@@ -40,10 +42,9 @@ def __init__(self, *args, **kwargs):
4042
:param storage: The class of the storage to use. Will be initialized
4143
with ``args`` and ``kwargs``.
4244
"""
43-
storage = kwargs.pop('storage', JSONStorage)
45+
storage = kwargs.pop('storage', TinyDB.DEFAULT_STORAGE)
4446
#: :type: Storage
4547
self._storage = storage(*args, **kwargs)
46-
self._serializers = {}
4748

4849
self._table_cache = {}
4950
self._table = self.table('_default')
@@ -102,24 +103,6 @@ def purge_tables(self):
102103
self._write({})
103104
self._table_cache.clear()
104105

105-
def register_serializer(self, serializer, name):
106-
"""
107-
Register a new Serializer.
108-
109-
When reading from/writing to the underlying storage, TinyDB
110-
will run all objects through the list of registered serializers
111-
allowing each one to handle objects it recognizes.
112-
113-
.. note:: The name has to be unique among this database instance.
114-
Re-using the same name will overwrite the old serializer.
115-
Also, registering a serializer will be reflected in all
116-
tables when reading/writing them.
117-
118-
:param serializer: an instance of the serializer
119-
:type serializer: tinydb.serialize.Serializer
120-
"""
121-
self._serializers[name] = serializer
122-
123106
def _read(self):
124107
"""
125108
Reading access to the backend.
@@ -142,32 +125,10 @@ def _read_table(self, table):
142125
"""
143126

144127
try:
145-
return self._deserialize(self._read()[table])
128+
return self._read()[table]
146129
except (KeyError, TypeError):
147130
return {}
148131

149-
def _deserialize(self, data):
150-
"""
151-
Deserialize the data passed in with all registered serializers
152-
153-
:param data: the data set to deserialize
154-
:return: the data set with deserialized values as needed
155-
"""
156-
for serializer_name in self._serializers:
157-
serializer = self._serializers[serializer_name]
158-
tag = '{{{}}}:'.format(serializer_name) # E.g: '{TinyDate}:'
159-
160-
for eid in data:
161-
for field in data[eid]:
162-
try:
163-
if data[eid][field].startswith(tag):
164-
encoded = data[eid][field][len(tag):]
165-
data[eid][field] = serializer.decode(encoded)
166-
except AttributeError:
167-
pass # Not a string
168-
169-
return data
170-
171132
def _write(self, tables):
172133
"""
173134
Writing access to the backend.
@@ -193,36 +154,7 @@ def _write_table(self, values, table):
193154
data = self._read()
194155
data[table] = values
195156

196-
# Finally, serialize & store the data
197-
self._write(self._serialize(data))
198-
199-
def _serialize(self, data):
200-
"""
201-
Serialize the data passed in with all registered serializers
202-
203-
:param data: the data set to serialize
204-
:return: the data set with serialized values as needed
205-
"""
206-
207-
for serializer_name in self._serializers:
208-
# If no serializers are registered, this code will just look up
209-
# the serializer list and continue. But if there are serializers,
210-
# the inner loop will run very often.
211-
# For that reason, the lookup of the serialized class is pulled
212-
# out into the outer loop:
213-
214-
serializer = self._serializers[serializer_name]
215-
serializer_class = serializer.OBJ_CLASS
216-
217-
for eid in data:
218-
for field in data[eid]:
219-
if isinstance(data[eid][field], serializer_class):
220-
encoded = serializer.encode(data[eid][field])
221-
tagged = '{{{}}}:{}'.format(serializer_name, encoded)
222-
223-
data[eid][field] = tagged
224-
225-
return data
157+
self._write(data)
226158

227159
# Methods that are executed on the default table
228160
# Because magic methods are not handlet by __getattr__ we need to forward

tinydb/middlewares.py

+76-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Contains the :class:`base class <tinydb.middlewares.Middleware>` for
33
middlewares and implementations.
44
"""
5+
from tinydb import TinyDB
6+
from tinydb.storages import JSONStorage
57

68

79
class Middleware(object):
@@ -17,7 +19,7 @@ class Middleware(object):
1719
example).
1820
"""
1921

20-
def __init__(self, storage_cls):
22+
def __init__(self, storage_cls=TinyDB.DEFAULT_STORAGE):
2123
self._storage_cls = storage_cls
2224
self.storage = None
2325

@@ -84,7 +86,7 @@ class CachingMiddleware(Middleware):
8486
#: The number of write operations to cache before writing to disc
8587
WRITE_CACHE_SIZE = 1000
8688

87-
def __init__(self, storage_cls):
89+
def __init__(self, storage_cls=TinyDB.DEFAULT_STORAGE):
8890
super(CachingMiddleware, self).__init__(storage_cls)
8991

9092
self.cache = None
@@ -116,3 +118,75 @@ def flush(self):
116118
def close(self):
117119
self.flush()
118120
self.storage.close()
121+
122+
123+
class SerializationMiddleware(Middleware):
124+
"""
125+
Provide custom serialization for TinyDB.
126+
127+
This middleware allows users of TinyDB to register custom serializations.
128+
The serialized data will be passed to the wrapped storage and data that
129+
is read from the storage will be deserialized.
130+
"""
131+
132+
def __init__(self, storage_cls=TinyDB.DEFAULT_STORAGE):
133+
super(SerializationMiddleware, self).__init__(storage_cls)
134+
135+
self._serializers = {}
136+
137+
def register_serializer(self, serializer, name):
138+
"""
139+
Register a new Serializer.
140+
141+
When reading from/writing to the underlying storage, TinyDB
142+
will run all objects through the list of registered serializers
143+
allowing each one to handle objects it recognizes.
144+
145+
.. note:: The name has to be unique among this database instance.
146+
Re-using the same name will overwrite the old serializer.
147+
Also, registering a serializer will be reflected in all
148+
tables when reading/writing them.
149+
150+
:param serializer: an instance of the serializer
151+
:type serializer: tinydb.serialize.Serializer
152+
"""
153+
self._serializers[name] = serializer
154+
155+
def read(self):
156+
data = self.storage.read()
157+
158+
for serializer_name in self._serializers:
159+
serializer = self._serializers[serializer_name]
160+
tag = '{{{}}}:'.format(serializer_name) # E.g: '{TinyDate}:'
161+
162+
for eid in data:
163+
for field in data[eid]:
164+
try:
165+
if data[eid][field].startswith(tag):
166+
encoded = data[eid][field][len(tag):]
167+
data[eid][field] = serializer.decode(encoded)
168+
except AttributeError:
169+
pass # Not a string
170+
171+
return data
172+
173+
def write(self, data):
174+
for serializer_name in self._serializers:
175+
# If no serializers are registered, this code will just look up
176+
# the serializer list and continue. But if there are serializers,
177+
# the inner loop will run very often.
178+
# For that reason, the lookup of the serialized class is pulled
179+
# out into the outer loop:
180+
181+
serializer = self._serializers[serializer_name]
182+
serializer_class = serializer.OBJ_CLASS
183+
184+
for eid in data:
185+
for field in data[eid]:
186+
if isinstance(data[eid][field], serializer_class):
187+
encoded = serializer.encode(data[eid][field])
188+
tagged = '{{{}}}:{}'.format(serializer_name, encoded)
189+
190+
data[eid][field] = tagged
191+
192+
self.storage.write(data)

0 commit comments

Comments
 (0)