22import functools
33import sys
44from typing import Optional
5+ from unittest .mock import patch
56
67import async_timeout
78import pytest
@@ -773,20 +774,52 @@ def callback(message):
773774 }
774775
775776
776- @pytest .mark .skipif (sys .version_info < (3 , 8 ), reason = "requires python 3.8 or higher" )
777777@pytest .mark .onlynoncluster
778- async def test_outer_timeout (r : redis .Redis ):
779- """
780- Using asyncio_timeout manually outside the inner method timeouts works.
781- This works on Python versions 3.8 and greater, at which time asyncio.CancelledError became a BaseException
782- instead of an Exception before.
783- """
784- pubsub = r .pubsub ()
785- await pubsub .subscribe ("foo" )
786- assert pubsub .connection .is_connected
787-
788- async def get_msg_or_timeout (timeout = 0.1 ):
789- async with async_timeout .timeout (timeout ):
778+ class TestBaseException :
779+ @pytest .mark .skipif (
780+ sys .version_info < (3 , 8 ), reason = "requires python 3.8 or higher"
781+ )
782+ async def test_outer_timeout (self , r : redis .Redis ):
783+ """
784+ Using asyncio_timeout manually outside the inner method timeouts works.
785+ This works on Python versions 3.8 and greater, at which time asyncio.CancelledError
786+ became a BaseException instead of an Exception before.
787+ """
788+ pubsub = r .pubsub ()
789+ await pubsub .subscribe ("foo" )
790+ assert pubsub .connection .is_connected
791+
792+ async def get_msg_or_timeout (timeout = 0.1 ):
793+ async with async_timeout .timeout (timeout ):
794+ # blocking method to return messages
795+ while True :
796+ response = await pubsub .parse_response (block = True )
797+ message = await pubsub .handle_message (
798+ response , ignore_subscribe_messages = False
799+ )
800+ if message is not None :
801+ return message
802+
803+ # get subscribe message
804+ msg = await get_msg_or_timeout (10 )
805+ assert msg is not None
806+ # timeout waiting for another message which never arrives
807+ assert pubsub .connection .is_connected
808+ with pytest .raises (asyncio .TimeoutError ):
809+ await get_msg_or_timeout ()
810+ # the timeout on the read should not cause disconnect
811+ assert pubsub .connection .is_connected
812+
813+ async def test_base_exception (self , r : redis .Redis ):
814+ """
815+ Manually trigger a BaseException inside the parser's .read_response method
816+ and verify that it isn't caught
817+ """
818+ pubsub = r .pubsub ()
819+ await pubsub .subscribe ("foo" )
820+ assert pubsub .connection .is_connected
821+
822+ async def get_msg ():
790823 # blocking method to return messages
791824 while True :
792825 response = await pubsub .parse_response (block = True )
@@ -796,12 +829,18 @@ async def get_msg_or_timeout(timeout=0.1):
796829 if message is not None :
797830 return message
798831
799- # get subscribe message
800- msg = await get_msg_or_timeout (10 )
801- assert msg is not None
802- # timeout waiting for another message which never arrives
803- assert pubsub .connection .is_connected
804- with pytest .raises (asyncio .TimeoutError ):
805- await get_msg_or_timeout ()
806- # the timeout on the read should not cause disconnect
807- assert pubsub .connection .is_connected
832+ # get subscribe message
833+ msg = await get_msg ()
834+ assert msg is not None
835+ # timeout waiting for another message which never arrives
836+ assert pubsub .connection .is_connected
837+ with patch ("redis.asyncio.connection.PythonParser.read_response" ) as mock1 :
838+ mock1 .side_effect = BaseException ("boom" )
839+ with patch ("redis.asyncio.connection.HiredisParser.read_response" ) as mock2 :
840+ mock2 .side_effect = BaseException ("boom" )
841+
842+ with pytest .raises (BaseException ):
843+ await get_msg ()
844+
845+ # the timeout on the read should not cause disconnect
846+ assert pubsub .connection .is_connected
0 commit comments