@@ -68,49 +68,101 @@ def _test_redis_getter():
6868 client = _get_test_redis_client ()
6969 if client is None :
7070 # Create a mock Redis client for testing
71- class MockRedis :
72- def __init__ (self ):
73- self .data = {}
74-
75- def hgetall (self , key ):
76- return self .data .get (key , {})
77-
78- def hset (self , key , mapping = None , ** kwargs ):
79- if key not in self .data :
80- self .data [key ] = {}
81- if mapping :
82- self .data [key ].update (mapping )
83- if kwargs :
84- self .data [key ].update (kwargs )
85-
86- def keys (self , pattern ):
87- import re
88-
89- pattern = pattern .replace ("*" , ".*" )
90- return [k for k in self .data if re .match (pattern , k .decode ())]
91-
92- def delete (self , * keys ):
93- for key in keys :
94- self .data .pop (key , None )
95-
96- def pipeline (self ):
97- return MockPipeline (self )
98-
99- class MockPipeline :
100- def __init__ (self , redis_client ):
101- self .redis_client = redis_client
102- self .commands = []
103-
104- def hset (self , key , field , value ):
105- self .commands .append (("hset" , key , field , value ))
106- return self
107-
108- def execute (self ):
109- for cmd , key , field , value in self .commands :
110- if cmd == "hset" :
111- self .redis_client .hset (key , field , value )
112-
113- return MockRedis ()
71+ # Use a singleton pattern to ensure the same instance is returned
72+ if not hasattr (_test_redis_getter , "_mock_client" ):
73+
74+ class MockRedis :
75+ def __init__ (self ):
76+ self .data = {}
77+ print ("DEBUG: MockRedis initialized" )
78+
79+ def hgetall (self , key ):
80+ result = self .data .get (key , {})
81+ # Convert string values to bytes to match Redis behavior
82+ bytes_result = {}
83+ for k , v in result .items ():
84+ if isinstance (v , str ):
85+ bytes_result [k .encode ("utf-8" )] = v .encode ("utf-8" )
86+ else :
87+ bytes_result [k .encode ("utf-8" )] = v
88+ print (
89+ f"DEBUG: hgetall({ key } ) = { result } -> { bytes_result } "
90+ )
91+ return bytes_result
92+
93+ def hset (
94+ self , key , field = None , value = None , mapping = None , ** kwargs
95+ ):
96+ if key not in self .data :
97+ self .data [key ] = {}
98+
99+ # Handle different calling patterns
100+ if mapping is not None :
101+ # Called with mapping dict
102+ self .data [key ].update (mapping )
103+ elif field is not None and value is not None :
104+ # Called with field, value arguments
105+ self .data [key ][field ] = value
106+ elif kwargs :
107+ # Called with keyword arguments
108+ self .data [key ].update (kwargs )
109+
110+ print (
111+ f"DEBUG: hset({ key } , field={ field } , value={ value } , "
112+ "mapping={mapping}, kwargs={kwargs}) -> "
113+ "{self.data[key]}"
114+ )
115+
116+ def keys (self , pattern ):
117+ import re
118+
119+ pattern = pattern .replace ("*" , ".*" )
120+ # Fix: keys are strings, not bytes, so no need to decode
121+ result = [k for k in self .data if re .match (pattern , k )]
122+ print (f"DEBUG: keys({ pattern } ) = { result } " )
123+ return result
124+
125+ def delete (self , * keys ):
126+ for key in keys :
127+ self .data .pop (key , None )
128+ print (f"DEBUG: delete({ keys } )" )
129+
130+ def pipeline (self ):
131+ return MockPipeline (self )
132+
133+ def ping (self ):
134+ return True
135+
136+ def set (self , key , value ):
137+ self .data [key ] = value
138+ print (f"DEBUG: set({ key } , { value } )" )
139+
140+ def get (self , key ):
141+ result = self .data .get (key )
142+ if isinstance (result , str ):
143+ result = result .encode ("utf-8" )
144+ print (f"DEBUG: get({ key } ) = { result } " )
145+ return result
146+
147+ class MockPipeline :
148+ def __init__ (self , redis_client ):
149+ self .redis_client = redis_client
150+ self .commands = []
151+
152+ def hset (self , key , field , value ):
153+ self .commands .append (("hset" , key , field , value ))
154+ return self
155+
156+ def execute (self ):
157+ for cmd , key , field , value in self .commands :
158+ if cmd == "hset" :
159+ self .redis_client .hset (
160+ key , field = field , value = value
161+ )
162+
163+ _test_redis_getter ._mock_client = MockRedis ()
164+
165+ return _test_redis_getter ._mock_client
114166 return client
115167
116168
@@ -213,46 +265,75 @@ def _stale_after_redis(arg_1, arg_2):
213265
214266
215267def _calls_takes_time_redis (res_queue ):
268+ print ("DEBUG: _calls_takes_time_redis started" )
269+
216270 @cachier (backend = "redis" , redis_client = _test_redis_getter )
217271 def _takes_time (arg_1 , arg_2 ):
218272 """Some function."""
273+ print (
274+ f"DEBUG: _calls_takes_time_redis._takes_time({ arg_1 } , { arg_2 } )"
275+ " called"
276+ )
219277 sleep (3 )
220- return random () + arg_1 + arg_2
278+ result = random () + arg_1 + arg_2
279+ print (
280+ f"DEBUG: _calls_takes_time_redis._takes_time({ arg_1 } , { arg_2 } ) "
281+ f"returning { result } "
282+ )
283+ return result
221284
285+ print ("DEBUG: _calls_takes_time_redis calling _takes_time(34, 82.3)" )
222286 res = _takes_time (34 , 82.3 )
287+ print (f"DEBUG: _calls_takes_time_redis got result { res } , putting in queue" )
223288 res_queue .put (res )
289+ print ("DEBUG: _calls_takes_time_redis completed" )
224290
225291
226292@pytest .mark .redis
227293def test_redis_being_calculated ():
228294 """Testing Redis core handling of being calculated scenarios."""
295+ print ("DEBUG: test_redis_being_calculated started" )
229296
230297 @cachier (backend = "redis" , redis_client = _test_redis_getter )
231298 def _takes_time (arg_1 , arg_2 ):
232299 """Some function."""
300+ print (f"DEBUG: _takes_time({ arg_1 } , { arg_2 } ) called" )
233301 sleep (3 )
234- return random () + arg_1 + arg_2
302+ result = random () + arg_1 + arg_2
303+ print (f"DEBUG: _takes_time({ arg_1 } , { arg_2 } ) returning { result } " )
304+ return result
235305
306+ print ("DEBUG: Clearing cache" )
236307 _takes_time .clear_cache ()
237308 res_queue = queue .Queue ()
309+ print ("DEBUG: Starting thread1" )
238310 thread1 = threading .Thread (
239311 target = _calls_takes_time_redis ,
240312 kwargs = {"res_queue" : res_queue },
241313 daemon = True ,
242314 )
315+ print ("DEBUG: Starting thread2" )
243316 thread2 = threading .Thread (
244317 target = _calls_takes_time_redis ,
245318 kwargs = {"res_queue" : res_queue },
246319 daemon = True ,
247320 )
321+ print ("DEBUG: Starting thread1" )
248322 thread1 .start ()
323+ print ("DEBUG: Sleeping 1 second" )
249324 sleep (1 )
325+ print ("DEBUG: Starting thread2" )
250326 thread2 .start ()
327+ print ("DEBUG: Waiting for thread1 to join" )
251328 thread1 .join ()
329+ print ("DEBUG: Waiting for thread2 to join" )
252330 thread2 .join ()
331+ print ("DEBUG: Getting results from queue" )
253332 res1 = res_queue .get ()
254333 res2 = res_queue .get ()
334+ print (f"DEBUG: Results: res1={ res1 } , res2={ res2 } " )
255335 assert res1 == res2
336+ print ("DEBUG: test_redis_being_calculated completed successfully" )
256337
257338
258339@pytest .mark .redis
0 commit comments