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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: redis-dict
3
- Version: 2.6.0
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, including strings, integers, floats, booleans, lists, dictionaries, sets, and tuples.
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
- ### Extending Types
256
+ ## Types
257
257
 
258
- ## Extending RedisDict with Custom Types
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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, store_value, keepttl=True)
328
+ self.redis.set(formatted_key, formatted_value, keepttl=True)
298
329
  else:
299
- self.redis.set(formatted_key, store_value, ex=self.expire)
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
- type_, value = result.split(':', 1)
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,,