funboost 19.5__py3-none-any.whl → 19.6__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.
Potentially problematic release.
This version of funboost might be problematic. Click here for more details.
- funboost/__init__.py +0 -1
- funboost/consumers/base_consumer.py +4 -6
- funboost/utils/dependency_packages_in_pythonpath/__pycache__/add_to_pythonpath.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/__pycache__/add_to_pythonpath.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/add_to_pythonpath.py +1 -1
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__init__.py +59 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/__init__.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/__init__.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/client.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/client.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/compat.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/compat.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/connection.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/connection.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/exceptions.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/exceptions.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/lock.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/lock.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/utils.cpython-311.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/utils.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/client.py +4804 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/compat.py +8 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/connection.py +1668 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/exceptions.py +96 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/lock.py +306 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/log.py +15 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/py.typed +0 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/readme.md +20 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/sentinel.py +329 -0
- funboost/utils/dependency_packages_in_pythonpath/aioredis/utils.py +61 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/exceptions.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/py3_raise.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/readme.md +14 -0
- funboost/utils/redis_manager.py +1 -1
- {funboost-19.5.dist-info → funboost-19.6.dist-info}/METADATA +424 -423
- {funboost-19.5.dist-info → funboost-19.6.dist-info}/RECORD +42 -11
- {funboost-19.5.dist-info → funboost-19.6.dist-info}/WHEEL +5 -5
- {funboost-19.5.dist-info → funboost-19.6.dist-info}/LICENSE +0 -0
- {funboost-19.5.dist-info → funboost-19.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import weakref
|
|
3
|
+
from typing import AsyncIterator, Iterable, Mapping, Sequence, Tuple, Type
|
|
4
|
+
|
|
5
|
+
from aioredis.client import Redis
|
|
6
|
+
from aioredis.connection import Connection, ConnectionPool, EncodableT, SSLConnection
|
|
7
|
+
from aioredis.exceptions import (
|
|
8
|
+
ConnectionError,
|
|
9
|
+
ReadOnlyError,
|
|
10
|
+
ResponseError,
|
|
11
|
+
TimeoutError,
|
|
12
|
+
)
|
|
13
|
+
from aioredis.utils import str_if_bytes
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MasterNotFoundError(ConnectionError):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SlaveNotFoundError(ConnectionError):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SentinelManagedConnection(SSLConnection):
|
|
25
|
+
def __init__(self, **kwargs):
|
|
26
|
+
self.connection_pool = kwargs.pop("connection_pool")
|
|
27
|
+
if not kwargs.pop("ssl", False):
|
|
28
|
+
# use constructor from Connection class
|
|
29
|
+
super(SSLConnection, self).__init__(**kwargs)
|
|
30
|
+
else:
|
|
31
|
+
# use constructor from SSLConnection class
|
|
32
|
+
super().__init__(**kwargs)
|
|
33
|
+
|
|
34
|
+
def __repr__(self):
|
|
35
|
+
pool = self.connection_pool
|
|
36
|
+
s = f"{self.__class__.__name__}<service={pool.service_name}"
|
|
37
|
+
if self.host:
|
|
38
|
+
host_info = f",host={self.host},port={self.port}"
|
|
39
|
+
s += host_info
|
|
40
|
+
return s + ">"
|
|
41
|
+
|
|
42
|
+
async def connect_to(self, address):
|
|
43
|
+
self.host, self.port = address
|
|
44
|
+
await super().connect()
|
|
45
|
+
if self.connection_pool.check_connection:
|
|
46
|
+
await self.send_command("PING")
|
|
47
|
+
if str_if_bytes(await self.read_response()) != "PONG":
|
|
48
|
+
raise ConnectionError("PING failed")
|
|
49
|
+
|
|
50
|
+
async def connect(self):
|
|
51
|
+
if self._reader:
|
|
52
|
+
return # already connected
|
|
53
|
+
if self.connection_pool.is_master:
|
|
54
|
+
await self.connect_to(await self.connection_pool.get_master_address())
|
|
55
|
+
else:
|
|
56
|
+
async for slave in self.connection_pool.rotate_slaves():
|
|
57
|
+
try:
|
|
58
|
+
return await self.connect_to(slave)
|
|
59
|
+
except ConnectionError:
|
|
60
|
+
continue
|
|
61
|
+
raise SlaveNotFoundError # Never be here
|
|
62
|
+
|
|
63
|
+
async def read_response(self):
|
|
64
|
+
try:
|
|
65
|
+
return await super().read_response()
|
|
66
|
+
except ReadOnlyError:
|
|
67
|
+
if self.connection_pool.is_master:
|
|
68
|
+
# When talking to a master, a ReadOnlyError when likely
|
|
69
|
+
# indicates that the previous master that we're still connected
|
|
70
|
+
# to has been demoted to a slave and there's a new master.
|
|
71
|
+
# calling disconnect will force the connection to re-query
|
|
72
|
+
# sentinel during the next connect() attempt.
|
|
73
|
+
await self.disconnect()
|
|
74
|
+
raise ConnectionError("The previous master is now a slave")
|
|
75
|
+
raise
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class SentinelConnectionPool(ConnectionPool):
|
|
79
|
+
"""
|
|
80
|
+
Sentinel backed connection pool.
|
|
81
|
+
|
|
82
|
+
If ``check_connection`` flag is set to True, SentinelManagedConnection
|
|
83
|
+
sends a PING command right after establishing the connection.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, service_name, sentinel_manager, **kwargs):
|
|
87
|
+
kwargs["connection_class"] = kwargs.get(
|
|
88
|
+
"connection_class", SentinelManagedConnection
|
|
89
|
+
)
|
|
90
|
+
self.is_master = kwargs.pop("is_master", True)
|
|
91
|
+
self.check_connection = kwargs.pop("check_connection", False)
|
|
92
|
+
super().__init__(**kwargs)
|
|
93
|
+
self.connection_kwargs["connection_pool"] = weakref.proxy(self)
|
|
94
|
+
self.service_name = service_name
|
|
95
|
+
self.sentinel_manager = sentinel_manager
|
|
96
|
+
self.master_address = None
|
|
97
|
+
self.slave_rr_counter = None
|
|
98
|
+
|
|
99
|
+
def __repr__(self):
|
|
100
|
+
return (
|
|
101
|
+
f"{self.__class__.__name__}"
|
|
102
|
+
f"<service={self.service_name}({self.is_master and 'master' or 'slave'})>"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def reset(self):
|
|
106
|
+
super().reset()
|
|
107
|
+
self.master_address = None
|
|
108
|
+
self.slave_rr_counter = None
|
|
109
|
+
|
|
110
|
+
def owns_connection(self, connection: Connection):
|
|
111
|
+
check = not self.is_master or (
|
|
112
|
+
self.is_master and self.master_address == (connection.host, connection.port)
|
|
113
|
+
)
|
|
114
|
+
return check and super().owns_connection(connection)
|
|
115
|
+
|
|
116
|
+
async def get_master_address(self):
|
|
117
|
+
master_address = await self.sentinel_manager.discover_master(self.service_name)
|
|
118
|
+
if self.is_master:
|
|
119
|
+
if self.master_address != master_address:
|
|
120
|
+
self.master_address = master_address
|
|
121
|
+
# disconnect any idle connections so that they reconnect
|
|
122
|
+
# to the new master the next time that they are used.
|
|
123
|
+
await self.disconnect(inuse_connections=False)
|
|
124
|
+
return master_address
|
|
125
|
+
|
|
126
|
+
async def rotate_slaves(self) -> AsyncIterator:
|
|
127
|
+
"""Round-robin slave balancer"""
|
|
128
|
+
slaves = await self.sentinel_manager.discover_slaves(self.service_name)
|
|
129
|
+
if slaves:
|
|
130
|
+
if self.slave_rr_counter is None:
|
|
131
|
+
self.slave_rr_counter = random.randint(0, len(slaves) - 1)
|
|
132
|
+
for _ in range(len(slaves)):
|
|
133
|
+
self.slave_rr_counter = (self.slave_rr_counter + 1) % len(slaves)
|
|
134
|
+
slave = slaves[self.slave_rr_counter]
|
|
135
|
+
yield slave
|
|
136
|
+
# Fallback to the master connection
|
|
137
|
+
try:
|
|
138
|
+
yield await self.get_master_address()
|
|
139
|
+
except MasterNotFoundError:
|
|
140
|
+
pass
|
|
141
|
+
raise SlaveNotFoundError(f"No slave found for {self.service_name!r}")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class Sentinel:
|
|
145
|
+
"""
|
|
146
|
+
Redis Sentinel cluster client
|
|
147
|
+
|
|
148
|
+
>>> from aioredis.sentinel import Sentinel
|
|
149
|
+
>>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
|
|
150
|
+
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
|
|
151
|
+
>>> await master.set('foo', 'bar')
|
|
152
|
+
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
|
|
153
|
+
>>> await slave.get('foo')
|
|
154
|
+
b'bar'
|
|
155
|
+
|
|
156
|
+
``sentinels`` is a list of sentinel nodes. Each node is represented by
|
|
157
|
+
a pair (hostname, port).
|
|
158
|
+
|
|
159
|
+
``min_other_sentinels`` defined a minimum number of peers for a sentinel.
|
|
160
|
+
When querying a sentinel, if it doesn't meet this threshold, responses
|
|
161
|
+
from that sentinel won't be considered valid.
|
|
162
|
+
|
|
163
|
+
``sentinel_kwargs`` is a dictionary of connection arguments used when
|
|
164
|
+
connecting to sentinel instances. Any argument that can be passed to
|
|
165
|
+
a normal Redis connection can be specified here. If ``sentinel_kwargs`` is
|
|
166
|
+
not specified, any socket_timeout and socket_keepalive options specified
|
|
167
|
+
in ``connection_kwargs`` will be used.
|
|
168
|
+
|
|
169
|
+
``connection_kwargs`` are keyword arguments that will be used when
|
|
170
|
+
establishing a connection to a Redis server.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
sentinels,
|
|
176
|
+
min_other_sentinels=0,
|
|
177
|
+
sentinel_kwargs=None,
|
|
178
|
+
**connection_kwargs,
|
|
179
|
+
):
|
|
180
|
+
# if sentinel_kwargs isn't defined, use the socket_* options from
|
|
181
|
+
# connection_kwargs
|
|
182
|
+
if sentinel_kwargs is None:
|
|
183
|
+
sentinel_kwargs = {
|
|
184
|
+
k: v for k, v in connection_kwargs.items() if k.startswith("socket_")
|
|
185
|
+
}
|
|
186
|
+
self.sentinel_kwargs = sentinel_kwargs
|
|
187
|
+
|
|
188
|
+
self.sentinels = [
|
|
189
|
+
Redis(host=hostname, port=port, **self.sentinel_kwargs)
|
|
190
|
+
for hostname, port in sentinels
|
|
191
|
+
]
|
|
192
|
+
self.min_other_sentinels = min_other_sentinels
|
|
193
|
+
self.connection_kwargs = connection_kwargs
|
|
194
|
+
|
|
195
|
+
def __repr__(self):
|
|
196
|
+
sentinel_addresses = []
|
|
197
|
+
for sentinel in self.sentinels:
|
|
198
|
+
sentinel_addresses.append(
|
|
199
|
+
f"{sentinel.connection_pool.connection_kwargs['host']}:"
|
|
200
|
+
f"{sentinel.connection_pool.connection_kwargs['port']}"
|
|
201
|
+
)
|
|
202
|
+
return f"{self.__class__.__name__}<sentinels=[{','.join(sentinel_addresses)}]>"
|
|
203
|
+
|
|
204
|
+
def check_master_state(self, state: dict, service_name: str) -> bool:
|
|
205
|
+
if not state["is_master"] or state["is_sdown"] or state["is_odown"]:
|
|
206
|
+
return False
|
|
207
|
+
# Check if our sentinel doesn't see other nodes
|
|
208
|
+
if state["num-other-sentinels"] < self.min_other_sentinels:
|
|
209
|
+
return False
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
async def discover_master(self, service_name: str):
|
|
213
|
+
"""
|
|
214
|
+
Asks sentinel servers for the Redis master's address corresponding
|
|
215
|
+
to the service labeled ``service_name``.
|
|
216
|
+
|
|
217
|
+
Returns a pair (address, port) or raises MasterNotFoundError if no
|
|
218
|
+
master is found.
|
|
219
|
+
"""
|
|
220
|
+
for sentinel_no, sentinel in enumerate(self.sentinels):
|
|
221
|
+
try:
|
|
222
|
+
masters = await sentinel.sentinel_masters()
|
|
223
|
+
except (ConnectionError, TimeoutError):
|
|
224
|
+
continue
|
|
225
|
+
state = masters.get(service_name)
|
|
226
|
+
if state and self.check_master_state(state, service_name):
|
|
227
|
+
# Put this sentinel at the top of the list
|
|
228
|
+
self.sentinels[0], self.sentinels[sentinel_no] = (
|
|
229
|
+
sentinel,
|
|
230
|
+
self.sentinels[0],
|
|
231
|
+
)
|
|
232
|
+
return state["ip"], state["port"]
|
|
233
|
+
raise MasterNotFoundError(f"No master found for {service_name!r}")
|
|
234
|
+
|
|
235
|
+
def filter_slaves(
|
|
236
|
+
self, slaves: Iterable[Mapping]
|
|
237
|
+
) -> Sequence[Tuple[EncodableT, EncodableT]]:
|
|
238
|
+
"""Remove slaves that are in an ODOWN or SDOWN state"""
|
|
239
|
+
slaves_alive = []
|
|
240
|
+
for slave in slaves:
|
|
241
|
+
if slave["is_odown"] or slave["is_sdown"]:
|
|
242
|
+
continue
|
|
243
|
+
slaves_alive.append((slave["ip"], slave["port"]))
|
|
244
|
+
return slaves_alive
|
|
245
|
+
|
|
246
|
+
async def discover_slaves(
|
|
247
|
+
self, service_name: str
|
|
248
|
+
) -> Sequence[Tuple[EncodableT, EncodableT]]:
|
|
249
|
+
"""Returns a list of alive slaves for service ``service_name``"""
|
|
250
|
+
for sentinel in self.sentinels:
|
|
251
|
+
try:
|
|
252
|
+
slaves = await sentinel.sentinel_slaves(service_name)
|
|
253
|
+
except (ConnectionError, ResponseError, TimeoutError):
|
|
254
|
+
continue
|
|
255
|
+
slaves = self.filter_slaves(slaves)
|
|
256
|
+
if slaves:
|
|
257
|
+
return slaves
|
|
258
|
+
return []
|
|
259
|
+
|
|
260
|
+
def master_for(
|
|
261
|
+
self,
|
|
262
|
+
service_name: str,
|
|
263
|
+
redis_class: Type[Redis] = Redis,
|
|
264
|
+
connection_pool_class: Type[SentinelConnectionPool] = SentinelConnectionPool,
|
|
265
|
+
**kwargs,
|
|
266
|
+
):
|
|
267
|
+
"""
|
|
268
|
+
Returns a redis client instance for the ``service_name`` master.
|
|
269
|
+
|
|
270
|
+
A :py:class:`~redis.sentinel.SentinelConnectionPool` class is
|
|
271
|
+
used to retrive the master's address before establishing a new
|
|
272
|
+
connection.
|
|
273
|
+
|
|
274
|
+
NOTE: If the master's address has changed, any cached connections to
|
|
275
|
+
the old master are closed.
|
|
276
|
+
|
|
277
|
+
By default clients will be a :py:class:`~redis.Redis` instance.
|
|
278
|
+
Specify a different class to the ``redis_class`` argument if you
|
|
279
|
+
desire something different.
|
|
280
|
+
|
|
281
|
+
The ``connection_pool_class`` specifies the connection pool to
|
|
282
|
+
use. The :py:class:`~redis.sentinel.SentinelConnectionPool`
|
|
283
|
+
will be used by default.
|
|
284
|
+
|
|
285
|
+
All other keyword arguments are merged with any connection_kwargs
|
|
286
|
+
passed to this class and passed to the connection pool as keyword
|
|
287
|
+
arguments to be used to initialize Redis connections.
|
|
288
|
+
"""
|
|
289
|
+
kwargs["is_master"] = True
|
|
290
|
+
connection_kwargs = dict(self.connection_kwargs)
|
|
291
|
+
connection_kwargs.update(kwargs)
|
|
292
|
+
return redis_class(
|
|
293
|
+
connection_pool=connection_pool_class(
|
|
294
|
+
service_name, self, **connection_kwargs
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def slave_for(
|
|
299
|
+
self,
|
|
300
|
+
service_name: str,
|
|
301
|
+
redis_class: Type[Redis] = Redis,
|
|
302
|
+
connection_pool_class: Type[SentinelConnectionPool] = SentinelConnectionPool,
|
|
303
|
+
**kwargs,
|
|
304
|
+
):
|
|
305
|
+
"""
|
|
306
|
+
Returns redis client instance for the ``service_name`` slave(s).
|
|
307
|
+
|
|
308
|
+
A SentinelConnectionPool class is used to retrive the slave's
|
|
309
|
+
address before establishing a new connection.
|
|
310
|
+
|
|
311
|
+
By default clients will be a :py:class:`~redis.Redis` instance.
|
|
312
|
+
Specify a different class to the ``redis_class`` argument if you
|
|
313
|
+
desire something different.
|
|
314
|
+
|
|
315
|
+
The ``connection_pool_class`` specifies the connection pool to use.
|
|
316
|
+
The SentinelConnectionPool will be used by default.
|
|
317
|
+
|
|
318
|
+
All other keyword arguments are merged with any connection_kwargs
|
|
319
|
+
passed to this class and passed to the connection pool as keyword
|
|
320
|
+
arguments to be used to initialize Redis connections.
|
|
321
|
+
"""
|
|
322
|
+
kwargs["is_master"] = False
|
|
323
|
+
connection_kwargs = dict(self.connection_kwargs)
|
|
324
|
+
connection_kwargs.update(kwargs)
|
|
325
|
+
return redis_class(
|
|
326
|
+
connection_pool=connection_pool_class(
|
|
327
|
+
service_name, self, **connection_kwargs
|
|
328
|
+
)
|
|
329
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, TypeVar, overload
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from aioredis import Redis
|
|
5
|
+
from aioredis.client import Pipeline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import hiredis # noqa
|
|
10
|
+
|
|
11
|
+
HIREDIS_AVAILABLE = True
|
|
12
|
+
except ImportError:
|
|
13
|
+
HIREDIS_AVAILABLE = False
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_T = TypeVar("_T")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def from_url(url, **kwargs):
|
|
20
|
+
"""
|
|
21
|
+
Returns an active Redis client generated from the given database URL.
|
|
22
|
+
|
|
23
|
+
Will attempt to extract the database id from the path url fragment, if
|
|
24
|
+
none is provided.
|
|
25
|
+
"""
|
|
26
|
+
from aioredis.client import Redis
|
|
27
|
+
|
|
28
|
+
return Redis.from_url(url, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class pipeline:
|
|
32
|
+
def __init__(self, redis_obj: "Redis"):
|
|
33
|
+
self.p: "Pipeline" = redis_obj.pipeline()
|
|
34
|
+
|
|
35
|
+
async def __aenter__(self) -> "Pipeline":
|
|
36
|
+
return self.p
|
|
37
|
+
|
|
38
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
39
|
+
await self.p.execute()
|
|
40
|
+
del self.p
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Mypy bug: https://github.com/python/mypy/issues/11005
|
|
44
|
+
@overload
|
|
45
|
+
def str_if_bytes(value: bytes) -> str: # type: ignore[misc]
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@overload
|
|
50
|
+
def str_if_bytes(value: _T) -> _T:
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def str_if_bytes(value: object) -> object:
|
|
55
|
+
return (
|
|
56
|
+
value.decode("utf-8", errors="replace") if isinstance(value, bytes) else value
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def safe_str(value: object) -> str:
|
|
61
|
+
return str(str_if_bytes(value))
|
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-37.pyc
ADDED
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc
ADDED
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/exceptions.cpython-37.pyc
ADDED
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/py3_raise.cpython-37.pyc
ADDED
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## 这个文件夹被添加到 sys.path中去了。
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
这个文件夹存放的是三方包或三方包的修改版。
|
|
5
|
+
|
|
6
|
+
当 import funboost时候会自动 把这个文件夹添加到 sys.path (PYTHONPATH)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 如果是开发者为了方便pycharm不显示波浪线提示没安装的错误和更好的自动补全提示
|
|
10
|
+
|
|
11
|
+
在pycahrm中对 funboost/utils/dependency_packages_in_pythonpath 文件夹点击鼠标右键 -> Mark Dictionary as -> mark as source root
|
|
12
|
+
|
|
13
|
+
这样导入时候就能自动补全提示和跳转到这里的包。
|
|
14
|
+
|
funboost/utils/redis_manager.py
CHANGED
|
@@ -4,7 +4,7 @@ import redis3
|
|
|
4
4
|
from funboost import funboost_config_deafult
|
|
5
5
|
from funboost.utils import decorators
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from aioredis.client import Redis as AioRedis
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class RedisManager(object):
|