redis-dict 1.6.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- redis_dict-2.0.0.dist-info/METADATA +163 -0
- redis_dict-2.0.0.dist-info/RECORD +6 -0
- {redis_dict-1.6.0.dist-info → redis_dict-2.0.0.dist-info}/WHEEL +1 -1
- redis_dict.py +545 -77
- redis_dict-1.6.0.dist-info/METADATA +0 -142
- redis_dict-1.6.0.dist-info/RECORD +0 -6
- {redis_dict-1.6.0.dist-info → redis_dict-2.0.0.dist-info}/LICENSE +0 -0
- {redis_dict-1.6.0.dist-info → redis_dict-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: redis-dict
|
3
|
+
Version: 2.0.0
|
4
|
+
Summary: Dictionary with Redis as storage backend
|
5
|
+
Home-page: https://github.com/Attumm/redisdict
|
6
|
+
Author: Melvin Bijman
|
7
|
+
Author-email: bijman.m.m@gmail.com
|
8
|
+
License: MIT
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
10
|
+
Classifier: Intended Audience :: Developers
|
11
|
+
Classifier: Topic :: Database
|
12
|
+
Classifier: Topic :: System :: Distributed Computing
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
15
|
+
Classifier: Programming Language :: Python :: 3.3
|
16
|
+
Classifier: Programming Language :: Python :: 3.4
|
17
|
+
Classifier: Programming Language :: Python :: 3.5
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
20
|
+
Description-Content-Type: text/markdown
|
21
|
+
License-File: LICENSE
|
22
|
+
Requires-Dist: redis
|
23
|
+
|
24
|
+
# Redis-dict
|
25
|
+
[](https://travis-ci.com/Attumm/redis-dict)
|
26
|
+
[](https://pepy.tech/project/redis-dict)
|
27
|
+
|
28
|
+
RedisDict is a Python library that allows you to interact with Redis as if it were a Python dictionary. It provides a simple, convenient, and user-friendly interface for managing key-value pairs in Redis. The library is designed to work seamlessly with different data types such as strings, integers, floats, booleans, None, lists, and dictionaries. It also offers additional utility functions and features for more complex use cases.
|
29
|
+
|
30
|
+
RedisDict was developed to address the challenges associated with handling extremely large datasets, which often exceed the capacity of local memory. This package offers a powerful and efficient solution for managing vast amounts of data by leveraging the capabilities of Redis and providing a familiar Python dictionary-like interface for seamless integration into your projects.
|
31
|
+
|
32
|
+
|
33
|
+
RedisDict stores data in Redis using key-value pairs, adhering to [Redis best practices](https://redislabs.com/redis-best-practices/data-storage-patterns/) for data storage patterns. This design not only ensures optimal performance and reliability but also enables interoperability with non-Python programs, granting them seamless access to the data stored in Redis.
|
34
|
+
|
35
|
+
## Example
|
36
|
+
Redis is an exceptionally fast database when used appropriately. RedisDict leverages Redis for efficient key-value storage, enabling high-performance data management.
|
37
|
+
|
38
|
+
```python
|
39
|
+
>>> from redis_dict import RedisDict
|
40
|
+
>>> dic = RedisDict(namespace='bar')
|
41
|
+
>>> 'foo' in dic
|
42
|
+
False
|
43
|
+
>>> dic['foo'] = 42
|
44
|
+
>>> dic['foo']
|
45
|
+
42
|
46
|
+
>>> 'foo' in dic
|
47
|
+
True
|
48
|
+
>>> dic["baz"] = "a string"
|
49
|
+
>>> print(dic)
|
50
|
+
{'foo': 42, 'baz': 'a string'}
|
51
|
+
|
52
|
+
```
|
53
|
+
In Redis our example looks like this.
|
54
|
+
```
|
55
|
+
127.0.0.1:6379> KEYS "*"
|
56
|
+
1) "bar:foo"
|
57
|
+
2) "bar:baz"
|
58
|
+
```
|
59
|
+
|
60
|
+
## Features
|
61
|
+
|
62
|
+
* Dictionary-like interface: Use familiar Python dictionary syntax to interact with Redis.
|
63
|
+
* Data Type Support: Comprehensive support for various data types, including strings, integers, floats, booleans, lists, dictionaries, sets, and tuples.
|
64
|
+
* Pipelining support: Use pipelines for batch operations to improve performance.
|
65
|
+
* Expiration support: Set expiration times for keys using context managers.
|
66
|
+
* 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.
|
67
|
+
* Namespace Management: Provides simple and efficient namespace handling to help organize and manage data in Redis, streamlining data access and manipulation.
|
68
|
+
* Distributed Computing: With its ability to seamlessly connect to other instances or servers with access to the same Redis instance, RedisDict enables easy distributed computing.
|
69
|
+
* Multi-get and multi-delete: Perform batch operations for getting and deleting multiple keys at once.
|
70
|
+
* Custom data types: Add custom types and transformations to suit your specific needs.
|
71
|
+
|
72
|
+
#### Caveats and Experimental Support
|
73
|
+
|
74
|
+
Please note that the following data types have experimental support in RedisDict:
|
75
|
+
* List
|
76
|
+
* Tuple
|
77
|
+
* Set
|
78
|
+
* Dictionary
|
79
|
+
|
80
|
+
These data types are supported through JSON serialization, which means that if your lists or dictionaries can be serialized using JSON, this feature should work as expected. However, this may not be the optimal solution for all use cases. As such, use these features at your discretion and consider potential limitations.
|
81
|
+
|
82
|
+
If you encounter a need for additional support or improvements for these or other reference types, please feel free to open an issue on the GitHub repository. Your feedback and contributions are greatly appreciated.
|
83
|
+
|
84
|
+
### Distributed computing
|
85
|
+
You can use RedisDict for distributed computing by starting multiple RedisDict instances on different servers or instances that have access to the same Redis instance:
|
86
|
+
```python
|
87
|
+
# On server 1
|
88
|
+
r = RedisDict(namespace="example", **redis_config)
|
89
|
+
r["foo"] = "bar"
|
90
|
+
|
91
|
+
|
92
|
+
# On server 2
|
93
|
+
r1 = RedisDict(namespace="example", **redis_config)
|
94
|
+
print(r1["foo"])
|
95
|
+
"bar"
|
96
|
+
|
97
|
+
```
|
98
|
+
|
99
|
+
## Advance features examples
|
100
|
+
|
101
|
+
#### Expiration
|
102
|
+
|
103
|
+
Redis provides a valuable feature that enables keys to expire. RedisDict supports this feature in the following ways:
|
104
|
+
1. Set a default expiration time when creating a RedisDict instance:
|
105
|
+
```python
|
106
|
+
r_dic = RedisDict(namespace='app_name', expire=10)
|
107
|
+
```
|
108
|
+
In this example, the keys will have a default expiration time of 10 seconds.
|
109
|
+
|
110
|
+
2. Temporarily set the default expiration time within the scope using a context manager:
|
111
|
+
```python
|
112
|
+
seconds = 60
|
113
|
+
with r_dic.expire_at(seconds):
|
114
|
+
r_dic['gone_in_sixty_seconds'] = 'foo'
|
115
|
+
```
|
116
|
+
In this example, the key 'gone_in_sixty_seconds' will expire after 60 seconds. The default expiration time for other keys outside the context manager remains unchanged.
|
117
|
+
|
118
|
+
Please note that the default expiration time is set to None, which indicates that the keys will not expire unless explicitly specified.
|
119
|
+
|
120
|
+
#### Batching
|
121
|
+
Efficiently batch your requests using the Pipeline feature, which can be easily utilized with a context manager.
|
122
|
+
|
123
|
+
For example, let's store the first ten items of the Fibonacci sequence using a single round trip to Redis:
|
124
|
+
[example](https://github.com/Attumm/redis-dict/blob/main/assert_test.py#L1) larger script using pipelining with batching
|
125
|
+
|
126
|
+
```python
|
127
|
+
def fib(n):
|
128
|
+
a, b = 0, 1
|
129
|
+
for _ in range(n):
|
130
|
+
yield a
|
131
|
+
a, b = (a+b), a
|
132
|
+
|
133
|
+
with r_dic.pipeline():
|
134
|
+
for index, item in enumerate(fib(10)):
|
135
|
+
r_dic[str(index)] = item
|
136
|
+
```
|
137
|
+
|
138
|
+
By employing the pipeline context manager, you can reduce network overhead and improve the performance of your application when executing multiple Redis operations in a single batch.
|
139
|
+
|
140
|
+
### Namespaces
|
141
|
+
RedisDict employs namespaces by default, providing an organized and efficient way to manage data across multiple projects. By using a dedicated RedisDict instance for each project, you can easily identify which data belongs to which application when inspecting Redis directly.
|
142
|
+
|
143
|
+
This approach also minimizes the risk of key collisions between different applications, preventing hard-to-debug issues. By leveraging namespaces, RedisDict ensures a cleaner and more maintainable data management experience for developers working on multiple projects.
|
144
|
+
|
145
|
+
### Additional Examples
|
146
|
+
For more advanced examples of RedisDict, please refer to the test files in the repository. All features and functionalities are thoroughly tested in either[ `assert_test.py` (here)](https://github.com/Attumm/redis-dict/blob/main/assert_test.py#L1) or in the [unit tests (here)](https://github.com/Attumm/redis-dict/blob/main/tests.py#L1).
|
147
|
+
The test can be used as starting point for new projects
|
148
|
+
|
149
|
+
### Tests
|
150
|
+
|
151
|
+
The RedisDict library includes a comprehensive suite of tests that ensure its correctness and resilience. The test suite covers various data types, edge cases, and error handling scenarios. It also employs the Hypothesis library for property-based testing, which provides fuzz testing to evaluate the implementation
|
152
|
+
|
153
|
+
## Installation
|
154
|
+
```sh
|
155
|
+
pip install redis-dict
|
156
|
+
```
|
157
|
+
|
158
|
+
### Note
|
159
|
+
Please be aware that this project is currently being utilized by various organizations in their production environments. If you have any questions or concerns, feel free to raise issues
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
### Note This project only uses redis as dependency
|
@@ -0,0 +1,6 @@
|
|
1
|
+
redis_dict.py,sha256=q_bTwNil5veFqPRd5cAFi6nle-yZxpZLKcyxJi-p72o,24054
|
2
|
+
redis_dict-2.0.0.dist-info/LICENSE,sha256=-QiLwYznh_vNUSz337k0faP9Jl0dgtCIHVZ39Uyl6cA,1070
|
3
|
+
redis_dict-2.0.0.dist-info/METADATA,sha256=QWsNGQIsonqIKh_FCKa3i-U6vMjcf_WS7IwDlvQ0hHY,8302
|
4
|
+
redis_dict-2.0.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
5
|
+
redis_dict-2.0.0.dist-info/top_level.txt,sha256=Wyp5Xvq_imoxvu-c-Le1rbTZ3pYM5BF440H9YAcgBZ8,11
|
6
|
+
redis_dict-2.0.0.dist-info/RECORD,,
|
redis_dict.py
CHANGED
@@ -1,139 +1,426 @@
|
|
1
1
|
import json
|
2
|
+
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union, Optional
|
2
3
|
|
3
4
|
from redis import StrictRedis
|
4
5
|
|
5
6
|
from contextlib import contextmanager
|
6
|
-
from future.utils import python_2_unicode_compatible
|
7
7
|
|
8
8
|
SENTINEL = object()
|
9
9
|
|
10
10
|
|
11
|
-
|
11
|
+
transform_type = Dict[str, Callable[[str], Any]]
|
12
|
+
pre_transform_type = Dict[str, Callable[[Any], str]]
|
13
|
+
|
14
|
+
|
15
|
+
def _transform_tuple(val: str) -> Tuple[Any, ...]:
|
16
|
+
"""
|
17
|
+
Deserialize a JSON-formatted string to a tuple.
|
18
|
+
|
19
|
+
This function takes a JSON-formatted string, deserializes it to a list, and
|
20
|
+
then converts the list to a tuple.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
val (str): A JSON-formatted string representing a list.
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
Tuple[Any, ...]: A tuple with the deserialized values from the input string.
|
27
|
+
"""
|
28
|
+
return tuple(json.loads(val))
|
29
|
+
|
30
|
+
|
31
|
+
def _pre_transform_tuple(val: Tuple[Any, ...]) -> str:
|
32
|
+
"""
|
33
|
+
Serialize a tuple to a JSON-formatted string.
|
34
|
+
|
35
|
+
This function takes a tuple, converts it to a list, and then serializes
|
36
|
+
the list to a JSON-formatted string.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
val (Tuple[Any, ...]): A tuple with values to be serialized.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
str: A JSON-formatted string representing the input tuple.
|
43
|
+
"""
|
44
|
+
return json.dumps(list(val))
|
45
|
+
|
46
|
+
|
47
|
+
def _transform_set(val: str) -> set[Any]:
|
48
|
+
"""
|
49
|
+
Deserialize a JSON-formatted string to a set.
|
50
|
+
|
51
|
+
This function takes a JSON-formatted string, deserializes it to a list, and
|
52
|
+
then converts the list to a set.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
val (str): A JSON-formatted string representing a list.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
set[Any]: A set with the deserialized values from the input string.
|
59
|
+
"""
|
60
|
+
return set(json.loads(val))
|
61
|
+
|
62
|
+
|
63
|
+
def _pre_transform_set(val: set[Any]) -> str:
|
64
|
+
"""
|
65
|
+
Serialize a set to a JSON-formatted string.
|
66
|
+
|
67
|
+
This function takes a set, converts it to a list, and then serializes the
|
68
|
+
list to a JSON-formatted string.
|
69
|
+
|
70
|
+
Args:
|
71
|
+
val (set[Any]): A set with values to be serialized.
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
str: A JSON-formatted string representing the input set.
|
75
|
+
"""
|
76
|
+
return json.dumps(list(val))
|
77
|
+
|
78
|
+
|
12
79
|
class RedisDict:
|
13
|
-
|
80
|
+
"""
|
81
|
+
A Redis-backed dictionary-like data structure with support for advanced features, such as
|
82
|
+
custom data types, pipelining, and key expiration.
|
83
|
+
|
84
|
+
This class provides a dictionary-like interface that interacts with a Redis database, allowing
|
85
|
+
for efficient storage and retrieval of key-value pairs. It supports various data types, including
|
86
|
+
strings, integers, floats, lists, dictionaries, tuples, sets, and user-defined types. The class
|
87
|
+
leverages the power of Redis pipelining to minimize network round-trip time, latency, and I/O load,
|
88
|
+
thereby optimizing performance for batch operations. Additionally, it allows for the management of
|
89
|
+
key expiration through the use of context managers.
|
90
|
+
|
91
|
+
The RedisDict class is designed to be analogous to a standard Python dictionary while providing
|
92
|
+
enhanced functionality, such as support for a wider range of data types and efficient batch operations.
|
93
|
+
It aims to offer a seamless and familiar interface for developers familiar with Python dictionaries,
|
94
|
+
enabling a smooth transition to a Redis-backed data store.
|
95
|
+
|
96
|
+
Attributes:
|
97
|
+
transform (Dict[str, Callable[[str], Any]]): A dictionary of data type transformation functions for loading data.
|
98
|
+
pre_transform (Dict[str, Callable[[Any], str]]): A dictionary of data type transformation functions for storing data.
|
99
|
+
namespace (str): A string used as a prefix for Redis keys to separate data in different namespaces.
|
100
|
+
expire (Union[int, None]): An optional expiration time for keys, in seconds.
|
101
|
+
|
102
|
+
"""
|
103
|
+
|
104
|
+
transform: transform_type = {
|
14
105
|
type('').__name__: str,
|
15
106
|
type(1).__name__: int,
|
16
107
|
type(0.1).__name__: float,
|
17
108
|
type(True).__name__: lambda x: x == "True",
|
18
109
|
type(None).__name__: lambda x: None,
|
19
|
-
type(None).__name__: lambda x: None,
|
20
110
|
|
21
111
|
"list": json.loads,
|
22
112
|
"dict": json.loads,
|
113
|
+
"tuple": _transform_tuple,
|
114
|
+
type(set()).__name__: _transform_set,
|
23
115
|
}
|
24
116
|
|
25
|
-
pre_transform = {
|
117
|
+
pre_transform: pre_transform_type = {
|
26
118
|
"list": json.dumps,
|
27
119
|
"dict": json.dumps,
|
120
|
+
"tuple": _pre_transform_tuple,
|
121
|
+
type(set()).__name__: _pre_transform_set,
|
28
122
|
}
|
29
123
|
|
30
|
-
def __init__(self, **kwargs):
|
31
|
-
|
32
|
-
|
33
|
-
self.namespace = kwargs.pop('namespace', '')
|
34
|
-
self.expire = kwargs.pop('expire', None)
|
124
|
+
def __init__(self, **kwargs: Any):
|
125
|
+
"""
|
126
|
+
Initialize a RedisDict instance.
|
35
127
|
|
36
|
-
|
37
|
-
|
38
|
-
|
128
|
+
Args:
|
129
|
+
namespace (str, optional): A prefix for keys stored in Redis.
|
130
|
+
expire (int, optional): Expiration time for keys in seconds.
|
131
|
+
**kwargs: Additional keyword arguments passed to StrictRedis.
|
132
|
+
"""
|
133
|
+
self.temp_redis: Optional[StrictRedis[Any]] = None
|
134
|
+
|
135
|
+
self.namespace: str = kwargs.pop('namespace', '')
|
136
|
+
self.expire: Union[int, None] = kwargs.pop('expire', None)
|
39
137
|
|
40
|
-
|
138
|
+
self.redis: StrictRedis[Any] = StrictRedis(decode_responses=True, **kwargs)
|
139
|
+
self.get_redis: StrictRedis[Any] = self.redis
|
140
|
+
self.iter: Iterator[str] = self.iterkeys()
|
141
|
+
|
142
|
+
def _format_key(self, key: str) -> str:
|
143
|
+
"""
|
144
|
+
Format a key with the namespace prefix.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
key (str): The key to be formatted.
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
str: The formatted key with the namespace prefix.
|
151
|
+
"""
|
41
152
|
return '{}:{}'.format(self.namespace, str(key))
|
42
153
|
|
43
|
-
def
|
154
|
+
def _valid_input(self, val: Any, val_type: str) -> bool:
|
155
|
+
"""
|
156
|
+
Check if the input value is valid based on the specified value type.
|
157
|
+
|
158
|
+
This method ensures that the input value is within the acceptable constraints for the given
|
159
|
+
value type. For example, when the value type is "str", the method checks that the string
|
160
|
+
length does not exceed the maximum allowed size (500 MB).
|
161
|
+
|
162
|
+
Args:
|
163
|
+
val (Union[str, int, float, bool]): The input value to be validated.
|
164
|
+
val_type (str): The type of the input value ("str", "int", "float", or "bool").
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
bool: True if the input value is valid, False otherwise.
|
168
|
+
"""
|
169
|
+
if val_type == "str":
|
170
|
+
return len(val) < (500 * 1024 * 1024)
|
171
|
+
return True
|
172
|
+
|
173
|
+
def _store(self, key: str, value: Any) -> None:
|
174
|
+
"""
|
175
|
+
Store a value in Redis with the given key.
|
176
|
+
|
177
|
+
Args:
|
178
|
+
key (str): The key to store the value.
|
179
|
+
value (Any): The value to be stored.
|
180
|
+
"""
|
44
181
|
store_type = type(value).__name__
|
182
|
+
if not self._valid_input(value, store_type) or not self._valid_input(key, "str"):
|
183
|
+
# TODO When needed, make valid_input, pass the reason, or throw a exception.
|
184
|
+
raise ValueError("Invalid input value or key size exceeded the maximum limit.")
|
185
|
+
value = self.pre_transform.get(store_type, lambda x: x)(value) # type: ignore
|
45
186
|
|
46
|
-
value = self.pre_transform.get(store_type, lambda x: x)(value)
|
47
187
|
store_value = '{}:{}'.format(store_type, value)
|
48
188
|
self.redis.set(self._format_key(key), store_value, ex=self.expire)
|
49
189
|
|
50
|
-
def _load(self, key):
|
190
|
+
def _load(self, key: str) -> Tuple[bool, Any]:
|
191
|
+
"""
|
192
|
+
Load a value from Redis with the given key.
|
193
|
+
|
194
|
+
Args:
|
195
|
+
key (str): The key to retrieve the value.
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
tuple: A tuple containing a boolean indicating whether the value was found and the value itself.
|
199
|
+
"""
|
51
200
|
result = self.get_redis.get(self._format_key(key))
|
52
201
|
if result is None:
|
53
202
|
return False, None
|
54
203
|
t, value = result.split(':', 1)
|
55
204
|
return True, self.transform.get(t, lambda x: x)(value)
|
56
205
|
|
57
|
-
def _transform(self, result):
|
206
|
+
def _transform(self, result: str) -> Any:
|
207
|
+
"""
|
208
|
+
Transform the result string from Redis into the appropriate Python object.
|
209
|
+
|
210
|
+
Args:
|
211
|
+
result (str): The result string from Redis.
|
212
|
+
|
213
|
+
Returns:
|
214
|
+
Any: The transformed Python object.
|
215
|
+
"""
|
58
216
|
t, value = result.split(':', 1)
|
59
217
|
return self.transform.get(t, lambda x: x)(value)
|
60
218
|
|
61
|
-
def add_type(self, k, v):
|
62
|
-
|
63
|
-
|
64
|
-
|
219
|
+
def add_type(self, k: str, v: Callable[[str], Any]) -> None:
|
220
|
+
"""
|
221
|
+
Add a custom type to the transform mapping.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
k (str): The key representing the type.
|
225
|
+
v (Callable): The transformation function for the type.
|
226
|
+
"""
|
227
|
+
self.transform[k] = v
|
228
|
+
|
229
|
+
def __cmp__(self, other: Any) -> int:
|
230
|
+
"""
|
231
|
+
Compare the current RedisDict with another object.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
other (Any): The object to compare with.
|
235
|
+
|
236
|
+
Returns:
|
237
|
+
int: 1 if equal, -1 otherwise.
|
238
|
+
Note:
|
239
|
+
TODO add the following methods
|
240
|
+
__lt__(self, other)
|
241
|
+
__le__(self, other)
|
242
|
+
__eq__(self, other)
|
243
|
+
__ne__(self, other)
|
244
|
+
__gt__(self, other)
|
245
|
+
__ge__(self, other)
|
246
|
+
"""
|
65
247
|
if len(self) != len(other):
|
66
|
-
return
|
67
|
-
for key in self:
|
68
|
-
if
|
69
|
-
return
|
70
|
-
return
|
248
|
+
return -1
|
249
|
+
for key, value in self.iteritems():
|
250
|
+
if value != other.get(key, SENTINEL):
|
251
|
+
return -1
|
252
|
+
return 1
|
253
|
+
|
254
|
+
def __getitem__(self, item: str) -> Any:
|
255
|
+
"""
|
256
|
+
Get the value associated with the given key, analogous to a dictionary.
|
257
|
+
|
258
|
+
Args:
|
259
|
+
item (str): The key to retrieve the value.
|
260
|
+
|
261
|
+
Returns:
|
262
|
+
Any: The value associated with the key.
|
71
263
|
|
72
|
-
|
264
|
+
Raises:
|
265
|
+
KeyError: If the key is not found.
|
266
|
+
"""
|
73
267
|
found, value = self._load(item)
|
74
268
|
if not found:
|
75
269
|
raise KeyError(item)
|
76
270
|
return value
|
77
271
|
|
78
|
-
def __setitem__(self, key, value):
|
272
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
273
|
+
"""
|
274
|
+
Set the value associated with the given key, analogous to a dictionary.
|
275
|
+
|
276
|
+
Args:
|
277
|
+
key (str): The key to store the value.
|
278
|
+
value (Any): The value to be stored.
|
279
|
+
"""
|
79
280
|
self._store(key, value)
|
80
281
|
|
81
|
-
def __delitem__(self, key):
|
282
|
+
def __delitem__(self, key: str) -> None:
|
283
|
+
"""
|
284
|
+
Delete the value associated with the given key, analogous to a dictionary.
|
285
|
+
|
286
|
+
Args:
|
287
|
+
key (str): The key to delete the value.
|
288
|
+
"""
|
82
289
|
self.redis.delete(self._format_key(key))
|
83
290
|
|
84
|
-
def __contains__(self, key):
|
85
|
-
|
291
|
+
def __contains__(self, key: str) -> bool:
|
292
|
+
"""
|
293
|
+
Check if the given key exists in the RedisDict, analogous to a dictionary.
|
294
|
+
|
295
|
+
Args:
|
296
|
+
key (str): The key to check for existence.
|
86
297
|
|
87
|
-
|
298
|
+
Returns:
|
299
|
+
bool: True if the key exists, False otherwise.
|
300
|
+
"""
|
301
|
+
return self._load(key)[0]
|
302
|
+
|
303
|
+
def __len__(self) -> int:
|
304
|
+
"""
|
305
|
+
Get the number of items in the RedisDict, analogous to a dictionary.
|
306
|
+
|
307
|
+
Returns:
|
308
|
+
int: The number of items in the RedisDict.
|
309
|
+
"""
|
88
310
|
return len(list(self._scan_keys()))
|
89
311
|
|
90
|
-
def __iter__(self):
|
312
|
+
def __iter__(self) -> Iterator[str]:
|
313
|
+
"""
|
314
|
+
Return an iterator over the keys of the RedisDict, analogous to a dictionary.
|
315
|
+
|
316
|
+
Returns:
|
317
|
+
Iterator[str]: An iterator over the keys of the RedisDict.
|
318
|
+
"""
|
91
319
|
self.iter = self.iterkeys()
|
92
320
|
return self
|
93
321
|
|
94
|
-
def __repr__(self):
|
322
|
+
def __repr__(self) -> str:
|
323
|
+
"""
|
324
|
+
Create a string representation of the RedisDict.
|
325
|
+
|
326
|
+
Returns:
|
327
|
+
str: A string representation of the RedisDict.
|
328
|
+
"""
|
95
329
|
return str(self)
|
96
330
|
|
97
|
-
def __str__(self):
|
331
|
+
def __str__(self) -> str:
|
332
|
+
"""
|
333
|
+
Create a string representation of the RedisDict.
|
334
|
+
|
335
|
+
Returns:
|
336
|
+
str: A string representation of the RedisDict.
|
337
|
+
"""
|
98
338
|
return str(self.to_dict())
|
99
339
|
|
100
|
-
def __next__(self):
|
340
|
+
def __next__(self) -> str:
|
341
|
+
"""
|
342
|
+
Get the next item in the iterator.
|
343
|
+
|
344
|
+
Returns:
|
345
|
+
str: The next item in the iterator.
|
346
|
+
|
347
|
+
Raises:
|
348
|
+
StopIteration: If there are no more items.
|
349
|
+
"""
|
101
350
|
return next(self.iter)
|
102
351
|
|
103
|
-
def next(self):
|
352
|
+
def next(self) -> str:
|
353
|
+
"""
|
354
|
+
Get the next item in the iterator (alias for __next__).
|
355
|
+
|
356
|
+
Returns:
|
357
|
+
str: The next item in the iterator.
|
358
|
+
|
359
|
+
Raises:
|
360
|
+
StopIteration: If there are no more items.
|
361
|
+
"""
|
104
362
|
return self.__next__()
|
105
363
|
|
106
|
-
def _scan_keys(self, search_term=''):
|
364
|
+
def _scan_keys(self, search_term: str = '') -> Iterator[str]:
|
365
|
+
"""
|
366
|
+
Scan for Redis keys matching the given search term.
|
367
|
+
|
368
|
+
Args:
|
369
|
+
search_term (str, optional): A search term to filter keys. Defaults to ''.
|
370
|
+
|
371
|
+
Returns:
|
372
|
+
Iterator[str]: An iterator of matching Redis keys.
|
373
|
+
"""
|
107
374
|
return self.get_redis.scan_iter(match='{}:{}{}'.format(self.namespace, search_term, '*'))
|
108
375
|
|
109
|
-
def get(self, key, default=None):
|
376
|
+
def get(self, key: str, default: Optional[Any] = None) -> Any:
|
377
|
+
"""
|
378
|
+
Return the value for the given key if it exists, otherwise return the default value.
|
379
|
+
Analogous to a dictionary's get method.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
key (str): The key to retrieve the value.
|
383
|
+
default (Optional[Any], optional): The value to return if the key is not found.
|
384
|
+
|
385
|
+
Returns:
|
386
|
+
Optional[Any]: The value associated with the key or the default value.
|
387
|
+
"""
|
110
388
|
found, item = self._load(key)
|
111
389
|
if not found:
|
112
390
|
return default
|
113
391
|
return item
|
114
392
|
|
115
|
-
def iterkeys(self):
|
116
|
-
"""
|
393
|
+
def iterkeys(self) -> Iterator[str]:
|
394
|
+
"""
|
395
|
+
Note: for pythone2 str is needed
|
396
|
+
"""
|
117
397
|
to_rm = len(self.namespace) + 1
|
118
398
|
return (str(item[to_rm:]) for item in self._scan_keys())
|
119
399
|
|
120
|
-
def
|
121
|
-
"""
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def key(self, search_term=''):
|
126
|
-
"""Note: for pythone2 str is needed"""
|
400
|
+
def key(self, search_term: str = '') -> Optional[str]:
|
401
|
+
"""
|
402
|
+
Note: for pythone2 str is needed
|
403
|
+
"""
|
127
404
|
to_rm = len(self.namespace) + 1
|
128
405
|
cursor, data = self.get_redis.scan(match='{}:{}{}'.format(self.namespace, search_term, '*'), count=1)
|
129
406
|
for item in data:
|
130
407
|
return str(item[to_rm:])
|
131
408
|
|
132
|
-
|
409
|
+
return None
|
410
|
+
|
411
|
+
def keys(self) -> List[str]:
|
412
|
+
"""
|
413
|
+
Return a list of keys in the RedisDict, analogous to a dictionary's keys method.
|
414
|
+
|
415
|
+
Returns:
|
416
|
+
List[str]: A list of keys in the RedisDict.
|
417
|
+
"""
|
133
418
|
return list(self.iterkeys())
|
134
419
|
|
135
|
-
def iteritems(self):
|
136
|
-
"""
|
420
|
+
def iteritems(self) -> Iterator[Tuple[str, Any]]:
|
421
|
+
"""
|
422
|
+
Note: for pythone2 str is needed
|
423
|
+
"""
|
137
424
|
to_rm = len(self.namespace) + 1
|
138
425
|
for item in self._scan_keys():
|
139
426
|
try:
|
@@ -141,13 +428,31 @@ class RedisDict:
|
|
141
428
|
except KeyError:
|
142
429
|
pass
|
143
430
|
|
144
|
-
def items(self):
|
431
|
+
def items(self) -> List[Tuple[str, Any]]:
|
432
|
+
"""
|
433
|
+
Return a list of key-value pairs (tuples) in the RedisDict, analogous to a dictionary's items method.
|
434
|
+
|
435
|
+
Returns:
|
436
|
+
List[Tuple[str, Any]]: A list of key-value pairs in the RedisDict.
|
437
|
+
"""
|
145
438
|
return list(self.iteritems())
|
146
439
|
|
147
|
-
def values(self):
|
440
|
+
def values(self) -> List[Any]:
|
441
|
+
"""
|
442
|
+
Return a list of values in the RedisDict, analogous to a dictionary's values method.
|
443
|
+
|
444
|
+
Returns:
|
445
|
+
List[Any]: A list of values in the RedisDict.
|
446
|
+
"""
|
148
447
|
return list(self.itervalues())
|
149
448
|
|
150
|
-
def itervalues(self):
|
449
|
+
def itervalues(self) -> Iterator[Any]:
|
450
|
+
"""
|
451
|
+
Iterate over the values in the RedisDict.
|
452
|
+
|
453
|
+
Returns:
|
454
|
+
Iterator[Any]: An iterator of values in the RedisDict.
|
455
|
+
"""
|
151
456
|
to_rm = len(self.namespace) + 1
|
152
457
|
for item in self._scan_keys():
|
153
458
|
try:
|
@@ -155,15 +460,44 @@ class RedisDict:
|
|
155
460
|
except KeyError:
|
156
461
|
pass
|
157
462
|
|
158
|
-
def to_dict(self):
|
463
|
+
def to_dict(self) -> Dict[str, Any]:
|
464
|
+
"""
|
465
|
+
Convert the RedisDict to a Python dictionary.
|
466
|
+
|
467
|
+
Returns:
|
468
|
+
Dict[str, Any]: A dictionary with the same key-value pairs as the RedisDict.
|
469
|
+
"""
|
159
470
|
return dict(self.items())
|
160
471
|
|
161
|
-
def clear(self):
|
472
|
+
def clear(self) -> None:
|
473
|
+
"""
|
474
|
+
Remove all key-value pairs from the RedisDict in one batch operation using pipelining.
|
475
|
+
|
476
|
+
This method mimics the behavior of the `clear` method from a standard Python dictionary.
|
477
|
+
Redis pipelining is employed to group multiple commands into a single request, minimizing
|
478
|
+
network round-trip time, latency, and I/O load, thereby enhancing the overall performance.
|
479
|
+
|
480
|
+
It is important to highlight that the clear method can be safely executed within the context of an initiated pipeline operation
|
481
|
+
"""
|
162
482
|
with self.pipeline():
|
163
483
|
for key in self:
|
164
|
-
del(self[key])
|
484
|
+
del (self[key])
|
165
485
|
|
166
|
-
def pop(self, key, default=SENTINEL):
|
486
|
+
def pop(self, key: str, default: Union[Any, object] = SENTINEL) -> Any:
|
487
|
+
"""
|
488
|
+
Remove the value associated with the given key and return it, or return the default value
|
489
|
+
if the key is not found. Analogous to a dictionary's pop method.
|
490
|
+
|
491
|
+
Args:
|
492
|
+
key (str): The key to remove the value.
|
493
|
+
default (Optional[Any], optional): The value to return if the key is not found.
|
494
|
+
|
495
|
+
Returns:
|
496
|
+
Optional[Any]: The value associated with the key or the default value.
|
497
|
+
|
498
|
+
Raises:
|
499
|
+
KeyError: If the key is not found and no default value is provided.
|
500
|
+
"""
|
167
501
|
try:
|
168
502
|
value = self[key]
|
169
503
|
except KeyError:
|
@@ -171,10 +505,20 @@ class RedisDict:
|
|
171
505
|
return default
|
172
506
|
raise
|
173
507
|
|
174
|
-
del(self[key])
|
508
|
+
del (self[key])
|
175
509
|
return value
|
176
510
|
|
177
|
-
def popitem(self):
|
511
|
+
def popitem(self) -> Tuple[str, Any]:
|
512
|
+
"""
|
513
|
+
Remove and return a random (key, value) pair from the RedisDict as a tuple.
|
514
|
+
This method is analogous to the `popitem` method of a standard Python dictionary.
|
515
|
+
|
516
|
+
Returns:
|
517
|
+
tuple: A tuple containing a randomly chosen (key, value) pair.
|
518
|
+
|
519
|
+
Raises:
|
520
|
+
KeyError: If RedisDict is empty.
|
521
|
+
"""
|
178
522
|
while True:
|
179
523
|
key = self.key()
|
180
524
|
if key is None:
|
@@ -184,44 +528,132 @@ class RedisDict:
|
|
184
528
|
except KeyError:
|
185
529
|
continue
|
186
530
|
|
187
|
-
def setdefault(self, key, default_value=None):
|
531
|
+
def setdefault(self, key: str, default_value: Optional[Any] = None) -> Any:
|
532
|
+
"""
|
533
|
+
Return the value associated with the given key if it exists, otherwise set the value to the
|
534
|
+
default value and return it. Analogous to a dictionary's setdefault method.
|
535
|
+
|
536
|
+
Args:
|
537
|
+
key (str): The key to retrieve the value.
|
538
|
+
default (Optional[Any], optional): The value to set if the key is not found.
|
539
|
+
|
540
|
+
Returns:
|
541
|
+
Any: The value associated with the key or the default value.
|
542
|
+
"""
|
188
543
|
found, value = self._load(key)
|
189
544
|
if not found:
|
190
545
|
self[key] = default_value
|
191
546
|
return default_value
|
192
547
|
return value
|
193
548
|
|
194
|
-
def copy(self):
|
549
|
+
def copy(self) -> Dict[str, Any]:
|
550
|
+
"""
|
551
|
+
Create a shallow copy of the RedisDict and return it as a standard Python dictionary.
|
552
|
+
This method is analogous to the `copy` method of a standard Python dictionary
|
553
|
+
|
554
|
+
Returns:
|
555
|
+
dict: A shallow copy of the RedisDict as a standard Python dictionary.
|
556
|
+
|
557
|
+
Note:
|
558
|
+
does not create a new RedisDict instance.
|
559
|
+
"""
|
195
560
|
return self.to_dict()
|
196
561
|
|
197
|
-
def update(self, dic):
|
562
|
+
def update(self, dic: Dict[str, Any]) -> None:
|
563
|
+
"""
|
564
|
+
Update the RedisDict with key-value pairs from the given mapping, analogous to a dictionary's update method.
|
565
|
+
|
566
|
+
Args:
|
567
|
+
other (Mapping[str, Any]): A mapping containing key-value pairs to update the RedisDict.
|
568
|
+
"""
|
198
569
|
with self.pipeline():
|
199
570
|
for key, value in dic.items():
|
200
571
|
self[key] = value
|
201
572
|
|
202
|
-
def fromkeys(self, iterable, value=None):
|
203
|
-
|
573
|
+
def fromkeys(self, iterable: List[str], value: Optional[Any] = None) -> 'RedisDict':
|
574
|
+
"""
|
575
|
+
Create a new RedisDict with keys from the provided iterable and values set to the given value.
|
576
|
+
This method is analogous to the `fromkeys` method of a standard Python dictionary, populating
|
577
|
+
the RedisDict with the keys from the iterable and setting their corresponding values to the
|
578
|
+
specified value.
|
579
|
+
|
580
|
+
|
581
|
+
Args:
|
582
|
+
iterable (List[str]): An iterable containing the keys to be added to the RedisDict.
|
583
|
+
value (Optional[Any], optional): The value to be assigned to each key in the RedisDict. Defaults to None.
|
204
584
|
|
205
|
-
|
585
|
+
Returns:
|
586
|
+
RedisDict: The current RedisDict instance, now populated with the keys from the iterable and their corresponding values.
|
587
|
+
"""
|
588
|
+
for key in iterable:
|
589
|
+
self[key] = value
|
590
|
+
return self
|
591
|
+
|
592
|
+
def __sizeof__(self) -> int:
|
593
|
+
"""
|
594
|
+
Return the approximate size of the RedisDict in memory, in bytes.
|
595
|
+
This method is analogous to the `__sizeof__` method of a standard Python dictionary, estimating
|
596
|
+
the memory consumption of the RedisDict based on the serialized in-memory representation.
|
597
|
+
|
598
|
+
Returns:
|
599
|
+
int: The approximate size of the RedisDict in memory, in bytes.
|
600
|
+
"""
|
206
601
|
return self.to_dict().__sizeof__()
|
207
602
|
|
208
|
-
def chain_set(self, iterable, v):
|
603
|
+
def chain_set(self, iterable: List[str], v: Any) -> None:
|
604
|
+
"""
|
605
|
+
Set a value in the RedisDict using a chain of keys.
|
606
|
+
|
607
|
+
Args:
|
608
|
+
iterable (List[str]): A list of keys representing the chain.
|
609
|
+
v (Any): The value to be set.
|
610
|
+
"""
|
209
611
|
self[':'.join(iterable)] = v
|
210
612
|
|
211
|
-
def chain_get(self, iterable):
|
613
|
+
def chain_get(self, iterable: List[str]) -> Any:
|
614
|
+
"""
|
615
|
+
Get a value from the RedisDict using a chain of keys.
|
616
|
+
|
617
|
+
Args:
|
618
|
+
iterable (List[str]): A list of keys representing the chain.
|
619
|
+
|
620
|
+
Returns:
|
621
|
+
Any: The value associated with the chain of keys.
|
622
|
+
"""
|
212
623
|
return self[':'.join(iterable)]
|
213
624
|
|
214
|
-
def chain_del(self, iterable):
|
625
|
+
def chain_del(self, iterable: List[str]) -> None:
|
626
|
+
"""
|
627
|
+
Delete a value from the RedisDict using a chain of keys.
|
628
|
+
|
629
|
+
Args:
|
630
|
+
iterable (List[str]): A list of keys representing the chain.
|
631
|
+
"""
|
215
632
|
return self.__delitem__(':'.join(iterable))
|
216
633
|
|
217
634
|
@contextmanager
|
218
|
-
def expire_at(self, sec_epoch):
|
635
|
+
def expire_at(self, sec_epoch: int) -> Iterator[None]:
|
636
|
+
"""
|
637
|
+
Context manager to set the expiration time for keys in the RedisDict.
|
638
|
+
|
639
|
+
Args:
|
640
|
+
sec_epoch (int): The expiration time in Unix timestamp format.
|
641
|
+
|
642
|
+
Returns:
|
643
|
+
ContextManager: A context manager during which the expiration time is the time set.
|
644
|
+
"""
|
219
645
|
self.expire, temp = sec_epoch, self.expire
|
220
646
|
yield
|
221
647
|
self.expire = temp
|
222
648
|
|
223
649
|
@contextmanager
|
224
|
-
def pipeline(self):
|
650
|
+
def pipeline(self) -> Iterator[None]:
|
651
|
+
"""
|
652
|
+
Context manager to create a Redis pipeline for batch operations.
|
653
|
+
|
654
|
+
Returns:
|
655
|
+
ContextManager: A context manager to create a Redis pipeline batching all operations within the context.
|
656
|
+
"""
|
225
657
|
top_level = False
|
226
658
|
if self.temp_redis is None:
|
227
659
|
self.redis, self.temp_redis, top_level = self.redis.pipeline(), self.redis, True
|
@@ -229,25 +661,61 @@ class RedisDict:
|
|
229
661
|
yield
|
230
662
|
finally:
|
231
663
|
if top_level:
|
232
|
-
_, self.temp_redis, self.redis = self.redis.execute(), None, self.temp_redis
|
664
|
+
_, self.temp_redis, self.redis = self.redis.execute(), None, self.temp_redis # type: ignore
|
665
|
+
|
666
|
+
def multi_get(self, key: str) -> List[Any]:
|
667
|
+
"""
|
668
|
+
Get multiple values from the RedisDict using a shared key prefix.
|
669
|
+
|
670
|
+
Args:
|
671
|
+
key (str): The shared key prefix.
|
233
672
|
|
234
|
-
|
673
|
+
Returns:
|
674
|
+
List[Any]: A list of values associated with the key prefix.
|
675
|
+
"""
|
235
676
|
found_keys = list(self._scan_keys(key))
|
236
677
|
if len(found_keys) == 0:
|
237
678
|
return []
|
238
679
|
return [self._transform(i) for i in self.redis.mget(found_keys) if i is not None]
|
239
680
|
|
240
|
-
def multi_chain_get(self, keys):
|
681
|
+
def multi_chain_get(self, keys: List[str]) -> List[Any]:
|
682
|
+
"""
|
683
|
+
Get multiple values from the RedisDict using a chain of keys.
|
684
|
+
|
685
|
+
Args:
|
686
|
+
keys (List[str]): A list of keys representing the chain.
|
687
|
+
|
688
|
+
Returns:
|
689
|
+
List[Any]: A list of values associated with the chain of keys.
|
690
|
+
"""
|
241
691
|
return self.multi_get(':'.join(keys))
|
242
692
|
|
243
|
-
def multi_dict(self, key):
|
693
|
+
def multi_dict(self, key: str) -> Dict[str, Any]:
|
694
|
+
"""
|
695
|
+
Get a dictionary of key-value pairs from the RedisDict using a shared key prefix.
|
696
|
+
|
697
|
+
Args:
|
698
|
+
key (str): The shared key prefix.
|
699
|
+
|
700
|
+
Returns:
|
701
|
+
Dict[str, Any]: A dictionary of key-value pairs associated with the key prefix.
|
702
|
+
"""
|
244
703
|
keys = list(self._scan_keys(key))
|
245
704
|
if len(keys) == 0:
|
246
705
|
return {}
|
247
706
|
to_rm = keys[0].rfind(':') + 1
|
248
707
|
return dict(zip([i[to_rm:] for i in keys], (self._transform(i) for i in self.redis.mget(keys) if i is not None)))
|
249
708
|
|
250
|
-
def multi_del(self, key):
|
709
|
+
def multi_del(self, key: str) -> int:
|
710
|
+
"""
|
711
|
+
Delete multiple values from the RedisDict using a shared key prefix.
|
712
|
+
|
713
|
+
Args:
|
714
|
+
key (str): The shared key prefix.
|
715
|
+
|
716
|
+
Returns:
|
717
|
+
int: The number of keys deleted.
|
718
|
+
"""
|
251
719
|
keys = list(self._scan_keys(key))
|
252
720
|
if len(keys) == 0:
|
253
721
|
return 0
|
@@ -1,142 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: redis-dict
|
3
|
-
Version: 1.6.0
|
4
|
-
Summary: Dictionary with Redis as storage backend
|
5
|
-
Home-page: https://github.com/Attumm/redisdict
|
6
|
-
Author: Melvin Bijman
|
7
|
-
Author-email: bijman.m.m@gmail.com
|
8
|
-
License: MIT
|
9
|
-
Platform: UNKNOWN
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
11
|
-
Classifier: Intended Audience :: Developers
|
12
|
-
Classifier: Topic :: Database
|
13
|
-
Classifier: Topic :: System :: Distributed Computing
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
15
|
-
Classifier: Programming Language :: Python :: 2
|
16
|
-
Classifier: Programming Language :: Python :: 2.7
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
18
|
-
Classifier: Programming Language :: Python :: 3.3
|
19
|
-
Classifier: Programming Language :: Python :: 3.4
|
20
|
-
Classifier: Programming Language :: Python :: 3.5
|
21
|
-
Classifier: Programming Language :: Python :: 3.6
|
22
|
-
Classifier: Programming Language :: Python :: 3.7
|
23
|
-
Description-Content-Type: text/markdown
|
24
|
-
License-File: LICENSE
|
25
|
-
Requires-Dist: redis
|
26
|
-
Requires-Dist: future
|
27
|
-
|
28
|
-
# redis-dict
|
29
|
-
[](https://travis-ci.com/Attumm/redis-dict)
|
30
|
-
[](https://pepy.tech/project/redis-dict)
|
31
|
-
|
32
|
-
A Python dictionary with Redis as the storage back-end.
|
33
|
-
Redis is a great database for all kinds of environments; from simple to complex.
|
34
|
-
redis-dict tries to make using Redis as simple as using a dictionary.
|
35
|
-
redis-dict stores data in Redis with key-values, this is according to [Redis best practices](https://redislabs.com/redis-best-practices/data-storage-patterns/).
|
36
|
-
This also allows other non-Python programs to access the data stored in Redis.
|
37
|
-
|
38
|
-
redis-dict was built out of the necessity of working with incredibly large data sets.
|
39
|
-
It had to be possible to only send or receive the required data over the wire and into memory.
|
40
|
-
With redis-dict it's as simple as a dictionary.
|
41
|
-
|
42
|
-
## Example
|
43
|
-
Redis is a really fast database if used right.
|
44
|
-
redis-dict uses Redis for key-value storage.
|
45
|
-
```python
|
46
|
-
>>> from redis_dict import RedisDict
|
47
|
-
>>> dic = RedisDict(namespace='bar')
|
48
|
-
>>> 'foo' in dic
|
49
|
-
False
|
50
|
-
>>> dic['foo'] = 42
|
51
|
-
>>> dic['foo']
|
52
|
-
42
|
53
|
-
>>> 'foo' in dic
|
54
|
-
True
|
55
|
-
>>> dic["baz"] = "a string"
|
56
|
-
>>> print(dic)
|
57
|
-
{'foo': 42, 'baz': 'a string'}
|
58
|
-
|
59
|
-
```
|
60
|
-
In Redis our example looks like this.
|
61
|
-
```
|
62
|
-
127.0.0.1:6379> KEYS "*"
|
63
|
-
1) "bar:foo"
|
64
|
-
2) "bar:baz"
|
65
|
-
```
|
66
|
-
|
67
|
-
## Features
|
68
|
-
|
69
|
-
#### Dictionary
|
70
|
-
redis-dict can be used as a drop-in replacement for a normal dictionary as long as no datastructures are used by reference.
|
71
|
-
i.e. no nested layout
|
72
|
-
e.g. values such list, instance and other dictionaries.
|
73
|
-
When used with supported types, it can be used a drop-in for a normal dictionary.
|
74
|
-
|
75
|
-
redis-dict has all the methods and behavior of a normal dictionary.
|
76
|
-
|
77
|
-
#### Types
|
78
|
-
Several Python types can be saved and retrieved as the same type.
|
79
|
-
As of writing, redis-dict supports the following types.
|
80
|
-
* String
|
81
|
-
* Integer
|
82
|
-
* Float
|
83
|
-
* Boolean
|
84
|
-
* None
|
85
|
-
|
86
|
-
#### Other Types not fully supported
|
87
|
-
Experimental support for the following types.
|
88
|
-
List, Dictionary supported provided with json serialization.
|
89
|
-
If your list or Dictionary can be serializate by json this feature will work.
|
90
|
-
|
91
|
-
Although is not the best solution, it could work for many usecases. So use at your discretion.
|
92
|
-
If there is need for other referenced types open issue on github.
|
93
|
-
* List
|
94
|
-
* Dictionary
|
95
|
-
|
96
|
-
#### Expire
|
97
|
-
Redis has the great feature of expiring keys. This feature is supported.
|
98
|
-
1. You can set the default expiration when creating a redis-dict instance.
|
99
|
-
```python
|
100
|
-
r_dic = RedisDict(namespace='app_name', expire=10)
|
101
|
-
```
|
102
|
-
2. With a context manager you can temporarily set the default expiration time.
|
103
|
-
Defaults to None (does not expire)
|
104
|
-
```python
|
105
|
-
seconds = 60
|
106
|
-
with r_dic.expire_at(seconds):
|
107
|
-
r_dic['gone_in_sixty_seconds'] = 'foo'
|
108
|
-
```
|
109
|
-
|
110
|
-
#### Batching
|
111
|
-
Batch your requests by using Pipeline, as easy as using a context manager
|
112
|
-
|
113
|
-
Example storing the first ten items of Fibonacci, with one round trip to Redis.
|
114
|
-
```python
|
115
|
-
def fib(n):
|
116
|
-
a, b = 0, 1
|
117
|
-
for _ in range(n):
|
118
|
-
yield a
|
119
|
-
a, b = (a+b), a
|
120
|
-
|
121
|
-
with r_dic.pipeline():
|
122
|
-
for index, item in enumerate(fib(10)):
|
123
|
-
r_dic[str(index)] = item
|
124
|
-
```
|
125
|
-
|
126
|
-
#### Namespaces
|
127
|
-
redis-dict uses namespaces by default. This allows you to have an instance of redis-dict per project.
|
128
|
-
When looking directly at the data in Redis, this gives you the advantage of directly seeing which data belongs to which app.
|
129
|
-
This also has the advantage that it is less likely for apps to collide with keys, which is a difficult problem to debug.
|
130
|
-
|
131
|
-
### More Examples
|
132
|
-
More complex examples of redis-dict can be found in the tests. All functionality is tested in either[ `assert_test.py` (here)](https://github.com/Attumm/redis-dict/blob/master/assert_test.py#L1) or in the [unit tests (here)](https://github.com/Attumm/redis-dict/blob/master/tests.py#L1).
|
133
|
-
|
134
|
-
## Installation
|
135
|
-
```sh
|
136
|
-
pip install redis-dict
|
137
|
-
```
|
138
|
-
|
139
|
-
### Note
|
140
|
-
This project is used by different companies in production.
|
141
|
-
|
142
|
-
|
@@ -1,6 +0,0 @@
|
|
1
|
-
redis_dict.py,sha256=6l6ZnFUBDKxYgNpyZXww1RiEB-uDgMV1JwocxqSFfjs,7084
|
2
|
-
redis_dict-1.6.0.dist-info/LICENSE,sha256=-QiLwYznh_vNUSz337k0faP9Jl0dgtCIHVZ39Uyl6cA,1070
|
3
|
-
redis_dict-1.6.0.dist-info/METADATA,sha256=CmY5CmO2B5sq4t7hS4NSnMdX9DxvEFBjVzyiK-R3arM,4950
|
4
|
-
redis_dict-1.6.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
5
|
-
redis_dict-1.6.0.dist-info/top_level.txt,sha256=Wyp5Xvq_imoxvu-c-Le1rbTZ3pYM5BF440H9YAcgBZ8,11
|
6
|
-
redis_dict-1.6.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|