redis-dict 2.6.0__py3-none-any.whl → 2.7.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,