Skip to content

Commit 1b2f408

Browse files
Fix behaviour of async PythonParser to match RedisParser as for issue #2349 (#2582)
* Allow data to drain from PythonParser after connection close. * Add Changes
1 parent 7d474f9 commit 1b2f408

File tree

3 files changed

+12
-15
lines changed

3 files changed

+12
-15
lines changed

CHANGES

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Allow data to drain from async PythonParser when reading during a disconnect()
12
* Use asyncio.timeout() instead of async_timeout.timeout() for python >= 3.11 (#2602)
23
* Add test and fix async HiredisParser when reading during a disconnect() (#2349)
34
* Use hiredis-py pack_command if available.

redis/asyncio/connection.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def decode(self, value: EncodableT, force=False) -> EncodableT:
146146
class BaseParser:
147147
"""Plain Python parsing class"""
148148

149-
__slots__ = "_stream", "_read_size"
149+
__slots__ = "_stream", "_read_size", "_connected"
150150

151151
EXCEPTION_CLASSES: ExceptionMappingT = {
152152
"ERR": {
@@ -177,6 +177,7 @@ class BaseParser:
177177
def __init__(self, socket_read_size: int):
178178
self._stream: Optional[asyncio.StreamReader] = None
179179
self._read_size = socket_read_size
180+
self._connected = False
180181

181182
def __del__(self):
182183
try:
@@ -213,7 +214,7 @@ async def read_response(
213214
class PythonParser(BaseParser):
214215
"""Plain Python parsing class"""
215216

216-
__slots__ = BaseParser.__slots__ + ("encoder", "_buffer", "_pos", "_chunks")
217+
__slots__ = ("encoder", "_buffer", "_pos", "_chunks")
217218

218219
def __init__(self, socket_read_size: int):
219220
super().__init__(socket_read_size)
@@ -231,28 +232,28 @@ def on_connect(self, connection: "Connection"):
231232
self._stream = connection._reader
232233
if self._stream is None:
233234
raise RedisError("Buffer is closed.")
234-
235235
self.encoder = connection.encoder
236+
self._clear()
237+
self._connected = True
236238

237239
def on_disconnect(self):
238240
"""Called when the stream disconnects"""
239-
if self._stream is not None:
240-
self._stream = None
241-
self.encoder = None
242-
self._clear()
241+
self._connected = False
243242

244243
async def can_read_destructive(self) -> bool:
244+
if not self._connected:
245+
raise RedisError("Buffer is closed.")
245246
if self._buffer:
246247
return True
247-
if self._stream is None:
248-
raise RedisError("Buffer is closed.")
249248
try:
250249
async with async_timeout(0):
251250
return await self._stream.read(1)
252251
except asyncio.TimeoutError:
253252
return False
254253

255254
async def read_response(self, disable_decoding: bool = False):
255+
if not self._connected:
256+
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
256257
if self._chunks:
257258
# augment parsing buffer with previously read data
258259
self._buffer += b"".join(self._chunks)
@@ -266,8 +267,6 @@ async def read_response(self, disable_decoding: bool = False):
266267
async def _read_response(
267268
self, disable_decoding: bool = False
268269
) -> Union[EncodableT, ResponseError, None]:
269-
if not self._stream or not self.encoder:
270-
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
271270
raw = await self._readline()
272271
response: Any
273272
byte, response = raw[:1], raw[1:]
@@ -354,14 +353,13 @@ async def _readline(self) -> bytes:
354353
class HiredisParser(BaseParser):
355354
"""Parser class for connections using Hiredis"""
356355

357-
__slots__ = BaseParser.__slots__ + ("_reader", "_connected")
356+
__slots__ = ("_reader",)
358357

359358
def __init__(self, socket_read_size: int):
360359
if not HIREDIS_AVAILABLE:
361360
raise RedisError("Hiredis is not available.")
362361
super().__init__(socket_read_size=socket_read_size)
363362
self._reader: Optional[hiredis.Reader] = None
364-
self._connected: bool = False
365363

366364
def on_connect(self, connection: "Connection"):
367365
self._stream = connection._reader

tests/test_asyncio/test_connection.py

-2
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,6 @@ async def test_connection_disconect_race(parser_class):
211211
This test verifies that a read in progress can finish even
212212
if the `disconnect()` method is called.
213213
"""
214-
if parser_class == PythonParser:
215-
pytest.xfail("doesn't work yet with PythonParser")
216214
if parser_class == HiredisParser and not HIREDIS_AVAILABLE:
217215
pytest.skip("Hiredis not available")
218216

0 commit comments

Comments
 (0)