3
3
import time
4
4
from urllib .parse import urlparse
5
5
6
- try :
6
+ try : # pragma: no cover
7
7
import redis
8
+ from redis .exceptions import RedisError
8
9
except ImportError :
9
10
redis = None
11
+ RedisError = None
12
+
13
+ try : # pragma: no cover
14
+ import valkey
15
+ from valkey .exceptions import ValkeyError
16
+ except ImportError :
17
+ valkey = None
18
+ ValkeyError = None
10
19
11
20
from .pubsub_manager import PubSubManager
12
21
@@ -18,7 +27,7 @@ def parse_redis_sentinel_url(url):
18
27
redis+sentinel://[:password]@host1:port1,host2:port2,.../db/service_name
19
28
"""
20
29
parsed_url = urlparse (url )
21
- if parsed_url .scheme != 'redis+sentinel' :
30
+ if parsed_url .scheme not in { 'redis+sentinel' , 'valkey+sentinel' } :
22
31
raise ValueError ('Invalid Redis Sentinel URL' )
23
32
sentinels = []
24
33
for host_port in parsed_url .netloc .split ('@' )[- 1 ].split (',' ):
@@ -71,10 +80,11 @@ class RedisManager(PubSubManager): # pragma: no cover
71
80
72
81
def __init__ (self , url = 'redis://localhost:6379/0' , channel = 'socketio' ,
73
82
write_only = False , logger = None , redis_options = None ):
74
- if redis is None :
83
+ if redis is None and valkey is None :
75
84
raise RuntimeError ('Redis package is not installed '
76
- '(Run "pip install redis" in your '
77
- 'virtualenv).' )
85
+ '(Run "pip install redis" '
86
+ 'or "pip install valkey" '
87
+ 'in your virtualenv).' )
78
88
super ().__init__ (channel = channel , write_only = write_only , logger = logger )
79
89
self .redis_url = url
80
90
self .redis_options = redis_options or {}
@@ -95,27 +105,48 @@ def initialize(self):
95
105
'Redis requires a monkey patched socket library to work '
96
106
'with ' + self .server .async_mode )
97
107
108
+ def _get_redis_module_and_error (self ):
109
+ parsed_url = urlparse (self .redis_url )
110
+ schema = parsed_url .scheme .split ('+' , 1 )[0 ].lower ()
111
+ if schema == 'redis' :
112
+ if redis is None or RedisError is None :
113
+ raise RuntimeError ('Redis package is not installed '
114
+ '(Run "pip install redis" '
115
+ 'in your virtualenv).' )
116
+ return redis , RedisError
117
+ if schema == 'valkey' :
118
+ if valkey is None or ValkeyError is None :
119
+ raise RuntimeError ('Valkey package is not installed '
120
+ '(Run "pip install valkey" '
121
+ 'in your virtualenv).' )
122
+ return valkey , ValkeyError
123
+ error_msg = f'Unsupported Redis URL schema: { schema } '
124
+ raise ValueError (error_msg )
125
+
98
126
def _redis_connect (self ):
99
- if not self .redis_url .startswith ('redis+sentinel://' ):
100
- self .redis = redis .Redis .from_url (self .redis_url ,
101
- ** self .redis_options )
102
- else :
127
+ module , _ = self ._get_redis_module_and_error ()
128
+ parsed_url = urlparse (self .redis_url )
129
+ if parsed_url .scheme in {"redis+sentinel" , "valkey+sentinel" }:
103
130
sentinels , service_name , connection_kwargs = \
104
131
parse_redis_sentinel_url (self .redis_url )
105
132
kwargs = self .redis_options
106
133
kwargs .update (connection_kwargs )
107
- sentinel = redis .sentinel .Sentinel (sentinels , ** kwargs )
134
+ sentinel = module .sentinel .Sentinel (sentinels , ** kwargs )
108
135
self .redis = sentinel .master_for (service_name or self .channel )
136
+ else :
137
+ self .redis = module .Redis .from_url (self .redis_url ,
138
+ ** self .redis_options )
109
139
self .pubsub = self .redis .pubsub (ignore_subscribe_messages = True )
110
140
111
141
def _publish (self , data ):
112
142
retry = True
143
+ _ , error = self ._get_redis_module_and_error ()
113
144
while True :
114
145
try :
115
146
if not retry :
116
147
self ._redis_connect ()
117
148
return self .redis .publish (self .channel , pickle .dumps (data ))
118
- except redis . exceptions . RedisError as exc :
149
+ except error as exc :
119
150
if retry :
120
151
logger .error (
121
152
'Cannot publish to redis... retrying' ,
@@ -132,16 +163,17 @@ def _publish(self, data):
132
163
def _redis_listen_with_retries (self ):
133
164
retry_sleep = 1
134
165
connect = False
166
+ _ , error = self ._get_redis_module_and_error ()
135
167
while True :
136
168
try :
137
169
if connect :
138
170
self ._redis_connect ()
139
171
self .pubsub .subscribe (self .channel )
140
172
retry_sleep = 1
141
173
yield from self .pubsub .listen ()
142
- except redis . exceptions . RedisError as exc :
174
+ except error as exc :
143
175
logger .error ('Cannot receive from redis... '
144
- 'retrying in {} secs' . format ( retry_sleep ) ,
176
+ f 'retrying in { retry_sleep } secs' ,
145
177
extra = {"redis_exception" : str (exc )})
146
178
connect = True
147
179
time .sleep (retry_sleep )
0 commit comments