@@ -186,6 +186,7 @@ class IMAP4:
186186 class error (Exception ): pass # Logical errors - debug required
187187 class abort (error ): pass # Service errors - close and retry
188188 class readonly (abort ): pass # Mailbox status changed to READ-ONLY
189+ class _responsetimeout (TimeoutError ): pass # No response during IDLE
189190
190191 def __init__ (self , host = '' , port = IMAP4_PORT , timeout = None ):
191192 self .debug = Debug
@@ -1154,14 +1155,28 @@ def _get_capabilities(self):
11541155 self .capabilities = tuple (dat .split ())
11551156
11561157
1157- def _get_response (self ):
1158+ def _get_response (self , start_timeout = False ):
11581159
11591160 # Read response and store.
11601161 #
11611162 # Returns None for continuation responses,
11621163 # otherwise first response line received.
1163-
1164- resp = self ._get_line ()
1164+ #
1165+ # If start_timeout is given, temporarily uses it as a socket
1166+ # timeout while waiting for the start of a response, raising
1167+ # _responsetimeout if one doesn't arrive. (Used by Idler.)
1168+
1169+ if start_timeout is not False and self .sock :
1170+ assert start_timeout is None or start_timeout > 0
1171+ saved_timeout = self .sock .gettimeout ()
1172+ self .sock .settimeout (start_timeout )
1173+ try :
1174+ resp = self ._get_line ()
1175+ except TimeoutError as err :
1176+ raise self ._responsetimeout from err
1177+ finally :
1178+ if start_timeout is not False and self .sock :
1179+ self .sock .settimeout (saved_timeout )
11651180
11661181 # Command completion response?
11671182
@@ -1386,7 +1401,6 @@ def __init__(self, imap, duration=None):
13861401 self ._deadline = None
13871402 self ._imap = imap
13881403 self ._tag = None
1389- self ._saved_timeout = None
13901404 self ._saved_state = None
13911405
13921406 def __enter__ (self ):
@@ -1424,10 +1438,6 @@ def __enter__(self):
14241438 imap ._idle_capture = False
14251439 raise
14261440
1427- self ._saved_timeout = imap .sock .gettimeout () if imap .sock else None
1428- if self ._saved_timeout is not None :
1429- imap .sock .settimeout (None ) # Socket timeout would break IDLE
1430-
14311441 if self ._duration is not None :
14321442 self ._deadline = time .monotonic () + self ._duration
14331443
@@ -1443,10 +1453,6 @@ def __exit__(self, exc_type, exc_val, exc_tb):
14431453 imap ._mesg ('idle done' )
14441454 imap .state = self ._saved_state
14451455
1446- if self ._saved_timeout is not None :
1447- imap .sock .settimeout (self ._saved_timeout )
1448- self ._saved_timeout = None
1449-
14501456 # Stop intercepting untagged responses before sending DONE,
14511457 # since we can no longer deliver them via iteration.
14521458 imap ._idle_capture = False
@@ -1514,19 +1520,16 @@ def _pop(self, timeout, default=('', None)):
15141520 imap ._mesg (f'idle _pop({ timeout } ) reading' )
15151521
15161522 if timeout is not None :
1517- assert isinstance (imap .sock , socket .socket )
15181523 if timeout <= 0 :
15191524 return default
1520- imap .sock .settimeout (float (timeout ))
1525+ timeout = float (timeout ) # Required by socket.settimeout()
1526+
15211527 try :
1522- imap ._get_response () # Reads line, calls _append_untagged()
1523- except TimeoutError :
1528+ imap ._get_response (timeout ) # Reads line, calls _append_untagged()
1529+ except IMAP4 . _responsetimeout :
15241530 if __debug__ and imap .debug >= 4 :
15251531 imap ._mesg (f'idle _pop({ timeout } ) done' )
15261532 return default
1527- finally :
1528- if timeout is not None :
1529- imap .sock .settimeout (None )
15301533
15311534 resp = imap ._idle_responses .pop (0 )
15321535
0 commit comments