redis-dict 2.6.0__py3-none-any.whl → 2.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redis_dict-2.6.0.dist-info → redis_dict-2.7.0.dist-info}/METADATA +48 -4
- redis_dict-2.7.0.dist-info/RECORD +6 -0
- {redis_dict-2.6.0.dist-info → redis_dict-2.7.0.dist-info}/WHEEL +1 -1
- redis_dict.py +43 -13
- redis_dict-2.6.0.dist-info/RECORD +0 -6
- {redis_dict-2.6.0.dist-info → redis_dict-2.7.0.dist-info}/LICENSE +0 -0
- {redis_dict-2.6.0.dist-info → redis_dict-2.7.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: redis-dict
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.7.0
|
4
4
|
Summary: Dictionary with Redis as storage backend
|
5
5
|
Home-page: https://github.com/Attumm/redisdict
|
6
6
|
Author: Melvin Bijman
|
@@ -44,7 +44,7 @@ The library includes utility functions for more complex use cases such as cachin
|
|
44
44
|
## Features
|
45
45
|
|
46
46
|
* Dictionary-like interface: Use familiar Python dictionary syntax to interact with Redis.
|
47
|
-
* Data Type Support: Comprehensive support for various data types
|
47
|
+
* Data Type Support: Comprehensive support for various data types.
|
48
48
|
* Pipelining support: Use pipelines for batch operations to improve performance.
|
49
49
|
* Expiration Support: Enables the setting of expiration times either globally or individually per key, through the use of context managers.
|
50
50
|
* Efficiency and Scalability: RedisDict is designed for use with large datasets and is optimized for efficiency. It retrieves only the data needed for a particular operation, ensuring efficient memory usage and fast performance.
|
@@ -253,9 +253,53 @@ print(dic["d"]) # Output: 4
|
|
253
253
|
For more advanced examples of RedisDict, please refer to the unit-test files in the repository. All features and functionalities are thoroughly tested in [unit tests (here)](https://github.com/Attumm/redis-dict/blob/main/tests.py#L1) Or take a look at load test for batching [load test](https://github.com/Attumm/redis-dict/blob/main/load_test.py#L1).
|
254
254
|
The unit-tests can be as used as a starting point.
|
255
255
|
|
256
|
-
|
256
|
+
## Types
|
257
257
|
|
258
|
-
|
258
|
+
### standard types
|
259
|
+
RedisDict supports a range of Python data types, from basic types to nested structures.
|
260
|
+
Basic types are handled natively, while complex data types like lists and dictionaries, RedisDict uses JSON serialization, specifically avoiding `pickle` due to its [security vulnerabilities](https://docs.python.org/3/library/pickle.html) in distributed computing contexts.
|
261
|
+
Although the library supports nested structures, the recommended best practice is to use RedisDict as a shallow dictionary.
|
262
|
+
This approach optimizes Redis database performance and efficiency by ensuring that each set and get operation efficiently maps to Redis's key-value storage capabilities, while still preserving the library's Pythonic interface.
|
263
|
+
Following types are supported:
|
264
|
+
`str, int, float, bool, NoneType, list, dict, tuple, set, datetime, date, time, timedelta, Decimal, complex, bytes, UUID, OrderedDict, defaultdict, frozenset`
|
265
|
+
```python
|
266
|
+
from redis_dict import RedisDict
|
267
|
+
|
268
|
+
from uuid import UUID
|
269
|
+
from decimal import Decimal
|
270
|
+
from collections import OrderedDict, defaultdict
|
271
|
+
from datetime import datetime, date, time, timedelta
|
272
|
+
|
273
|
+
|
274
|
+
dic = RedisDict()
|
275
|
+
|
276
|
+
dic["string"] = "Hello World"
|
277
|
+
dic["number"] = 42
|
278
|
+
dic["float"] = 3.14
|
279
|
+
dic["bool"] = True
|
280
|
+
dic["None"] = None
|
281
|
+
|
282
|
+
dic["list"] = [1, 2, 3]
|
283
|
+
dic["dict"] = {"a": 1, "b": 2}
|
284
|
+
dic["tuple"] = (1, 2, 3)
|
285
|
+
dic["set"] = {1, 2, 3}
|
286
|
+
|
287
|
+
dic["datetime"] = datetime.date(2024, 1, 1, 12, 30, 45)
|
288
|
+
dic["date"] = date(2024, 1, 1)
|
289
|
+
dic["time"] = time(12, 30, 45)
|
290
|
+
dic["delta"] = timedelta(days=1, hours=2)
|
291
|
+
|
292
|
+
dic["decimal"] = Decimal("3.14159")
|
293
|
+
dic["complex"] = complex(1, 2)
|
294
|
+
dic["bytes"] = bytes([72, 101, 108, 108, 111])
|
295
|
+
dic["uuid"] = UUID('12345678-1234-5678-1234-567812345678')
|
296
|
+
|
297
|
+
dic["ordered"] = OrderedDict([('a', 1), ('b', 2)])
|
298
|
+
dic["default"] = defaultdict(int, {'a': 1, 'b': 2})
|
299
|
+
dic["frozen"] = frozenset([1, 2, 3])
|
300
|
+
```
|
301
|
+
|
302
|
+
### Extending RedisDict with Custom Types
|
259
303
|
|
260
304
|
RedisDict supports custom type serialization. Here's how to add a new type:
|
261
305
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
redis_dict.py,sha256=LmP5C1lIf3KCG_3pfcnw3CZE5KuUUym2U_UqlbFoiVg,38214
|
2
|
+
redis_dict-2.7.0.dist-info/LICENSE,sha256=-QiLwYznh_vNUSz337k0faP9Jl0dgtCIHVZ39Uyl6cA,1070
|
3
|
+
redis_dict-2.7.0.dist-info/METADATA,sha256=8SDlje1sUiquUlKvk27LHRhLTsdwr9b5KTsKBa5XhGk,14300
|
4
|
+
redis_dict-2.7.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
5
|
+
redis_dict-2.7.0.dist-info/top_level.txt,sha256=Wyp5Xvq_imoxvu-c-Le1rbTZ3pYM5BF440H9YAcgBZ8,11
|
6
|
+
redis_dict-2.7.0.dist-info/RECORD,,
|
redis_dict.py
CHANGED
@@ -66,9 +66,14 @@ otherwise, they will be dropped.
|
|
66
66
|
>>> cache.get("1234") is None
|
67
67
|
>>> True
|
68
68
|
"""
|
69
|
+
# Types imports
|
69
70
|
import json
|
71
|
+
from datetime import datetime, time, timedelta, date
|
72
|
+
from decimal import Decimal
|
73
|
+
from uuid import UUID
|
74
|
+
from collections import OrderedDict, defaultdict
|
75
|
+
import base64
|
70
76
|
|
71
|
-
from datetime import timedelta
|
72
77
|
from typing import Any, Callable, Dict, Iterator, Set, List, Tuple, Union, Optional
|
73
78
|
from contextlib import contextmanager
|
74
79
|
|
@@ -189,7 +194,6 @@ class RedisDict:
|
|
189
194
|
expire (Union[int, None]): An optional expiration time for keys, in seconds.
|
190
195
|
|
191
196
|
"""
|
192
|
-
|
193
197
|
decoding_registry: DecodeType = {
|
194
198
|
type('').__name__: str,
|
195
199
|
type(1).__name__: int,
|
@@ -201,6 +205,20 @@ class RedisDict:
|
|
201
205
|
"dict": json.loads,
|
202
206
|
"tuple": _decode_tuple,
|
203
207
|
type(set()).__name__: _decode_set,
|
208
|
+
|
209
|
+
datetime.__name__: datetime.fromisoformat,
|
210
|
+
date.__name__: date.fromisoformat,
|
211
|
+
time.__name__: time.fromisoformat,
|
212
|
+
timedelta.__name__: lambda x: timedelta(seconds=float(x)),
|
213
|
+
|
214
|
+
Decimal.__name__: Decimal,
|
215
|
+
complex.__name__: lambda x: complex(*map(float, x.split(','))),
|
216
|
+
bytes.__name__: base64.b64decode,
|
217
|
+
|
218
|
+
UUID.__name__: UUID,
|
219
|
+
OrderedDict.__name__: lambda x: OrderedDict(json.loads(x)),
|
220
|
+
defaultdict.__name__: lambda x: defaultdict(type(None), json.loads(x)),
|
221
|
+
frozenset.__name__: lambda x: frozenset(json.loads(x)),
|
204
222
|
}
|
205
223
|
|
206
224
|
encoding_registry: EncodeType = {
|
@@ -208,6 +226,17 @@ class RedisDict:
|
|
208
226
|
"dict": json.dumps,
|
209
227
|
"tuple": _encode_tuple,
|
210
228
|
type(set()).__name__: _encode_set,
|
229
|
+
|
230
|
+
datetime.__name__: datetime.isoformat,
|
231
|
+
date.__name__: date.isoformat,
|
232
|
+
time.__name__: time.isoformat,
|
233
|
+
timedelta.__name__: lambda x: str(x.total_seconds()),
|
234
|
+
|
235
|
+
complex.__name__: lambda x: f"{x.real},{x.imag}",
|
236
|
+
bytes.__name__: lambda x: base64.b64encode(x).decode('ascii'),
|
237
|
+
OrderedDict.__name__: lambda x: json.dumps(list(x.items())),
|
238
|
+
defaultdict.__name__: lambda x: json.dumps(dict(x)),
|
239
|
+
frozenset.__name__: lambda x: json.dumps(list(x)),
|
211
240
|
}
|
212
241
|
|
213
242
|
def __init__(self,
|
@@ -269,6 +298,14 @@ class RedisDict:
|
|
269
298
|
return len(val) < self._max_string_size
|
270
299
|
return True
|
271
300
|
|
301
|
+
def _format_value(self, key: str, value: Any) -> str:
|
302
|
+
store_type, key = type(value).__name__, str(key)
|
303
|
+
if not self._valid_input(value, store_type) or not self._valid_input(key, "str"):
|
304
|
+
raise ValueError("Invalid input value or key size exceeded the maximum limit.")
|
305
|
+
encoded_value = self.encoding_registry.get(store_type, lambda x: x)(value) # type: ignore
|
306
|
+
|
307
|
+
return f'{store_type}:{encoded_value}'
|
308
|
+
|
272
309
|
def _store(self, key: str, value: Any) -> None:
|
273
310
|
"""
|
274
311
|
Store a value in Redis with the given key.
|
@@ -285,18 +322,12 @@ class RedisDict:
|
|
285
322
|
Allowing for simple dict set operation, but only cache data that makes sense.
|
286
323
|
|
287
324
|
"""
|
288
|
-
store_type, key = type(value).__name__, str(key)
|
289
|
-
if not self._valid_input(value, store_type) or not self._valid_input(key, "str"):
|
290
|
-
raise ValueError("Invalid input value or key size exceeded the maximum limit.")
|
291
|
-
value = self.encoding_registry.get(store_type, lambda x: x)(value) # type: ignore
|
292
|
-
|
293
|
-
store_value = f'{store_type}:{value}'
|
294
325
|
formatted_key = self._format_key(key)
|
295
|
-
|
326
|
+
formatted_value = self._format_value(key, value)
|
296
327
|
if self.preserve_expiration and self.redis.exists(formatted_key):
|
297
|
-
self.redis.set(formatted_key,
|
328
|
+
self.redis.set(formatted_key, formatted_value, keepttl=True)
|
298
329
|
else:
|
299
|
-
self.redis.set(formatted_key,
|
330
|
+
self.redis.set(formatted_key, formatted_value, ex=self.expire)
|
300
331
|
|
301
332
|
def _load(self, key: str) -> Tuple[bool, Any]:
|
302
333
|
"""
|
@@ -311,8 +342,7 @@ class RedisDict:
|
|
311
342
|
result = self.get_redis.get(self._format_key(key))
|
312
343
|
if result is None:
|
313
344
|
return False, None
|
314
|
-
|
315
|
-
return True, self.decoding_registry.get(type_, lambda x: x)(value)
|
345
|
+
return True, self._transform(result)
|
316
346
|
|
317
347
|
def _transform(self, result: str) -> Any:
|
318
348
|
"""
|
@@ -1,6 +0,0 @@
|
|
1
|
-
redis_dict.py,sha256=50CSZ5dMBBbr-UU9BSvoGgBItD7uce8F5ty1lphaiUw,36901
|
2
|
-
redis_dict-2.6.0.dist-info/LICENSE,sha256=-QiLwYznh_vNUSz337k0faP9Jl0dgtCIHVZ39Uyl6cA,1070
|
3
|
-
redis_dict-2.6.0.dist-info/METADATA,sha256=6QJ93NO1RrVKSYbmbgtFKOazqtKrauPstB-H0hI1vDs,12564
|
4
|
-
redis_dict-2.6.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
5
|
-
redis_dict-2.6.0.dist-info/top_level.txt,sha256=Wyp5Xvq_imoxvu-c-Le1rbTZ3pYM5BF440H9YAcgBZ8,11
|
6
|
-
redis_dict-2.6.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|