mirror of https://github.com/python/cpython
bpo-38615: Add timeout parameter for IMAP4 and IMAP4_SSL constructor (GH-17203)
imaplib.IMAP4 and imaplib.IMAP4_SSL now have an optional *timeout* parameter for their constructors. Also, the imaplib.IMAP4.open() method now has an optional *timeout* parameter with this change. The overridden methods of imaplib.IMAP4_SSL and imaplib.IMAP4_stream were applied to this change.
This commit is contained in:
parent
950c6795aa
commit
13a7ee8d62
|
@ -30,12 +30,14 @@ Three classes are provided by the :mod:`imaplib` module, :class:`IMAP4` is the
|
||||||
base class:
|
base class:
|
||||||
|
|
||||||
|
|
||||||
.. class:: IMAP4(host='', port=IMAP4_PORT)
|
.. class:: IMAP4(host='', port=IMAP4_PORT, timeout=None)
|
||||||
|
|
||||||
This class implements the actual IMAP4 protocol. The connection is created and
|
This class implements the actual IMAP4 protocol. The connection is created and
|
||||||
protocol version (IMAP4 or IMAP4rev1) is determined when the instance is
|
protocol version (IMAP4 or IMAP4rev1) is determined when the instance is
|
||||||
initialized. If *host* is not specified, ``''`` (the local host) is used. If
|
initialized. If *host* is not specified, ``''`` (the local host) is used. If
|
||||||
*port* is omitted, the standard IMAP4 port (143) is used.
|
*port* is omitted, the standard IMAP4 port (143) is used. The optional *timeout*
|
||||||
|
parameter specifies a timeout in seconds for the connection attempt.
|
||||||
|
If timeout is not given or is None, the global default socket timeout is used.
|
||||||
|
|
||||||
The :class:`IMAP4` class supports the :keyword:`with` statement. When used
|
The :class:`IMAP4` class supports the :keyword:`with` statement. When used
|
||||||
like this, the IMAP4 ``LOGOUT`` command is issued automatically when the
|
like this, the IMAP4 ``LOGOUT`` command is issued automatically when the
|
||||||
|
@ -50,6 +52,9 @@ base class:
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
Support for the :keyword:`with` statement was added.
|
Support for the :keyword:`with` statement was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The optional *timeout* parameter was added.
|
||||||
|
|
||||||
Three exceptions are defined as attributes of the :class:`IMAP4` class:
|
Three exceptions are defined as attributes of the :class:`IMAP4` class:
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,7 +83,7 @@ There's also a subclass for secure connections:
|
||||||
|
|
||||||
|
|
||||||
.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, \
|
.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, \
|
||||||
certfile=None, ssl_context=None)
|
certfile=None, ssl_context=None, timeout=None)
|
||||||
|
|
||||||
This is a subclass derived from :class:`IMAP4` that connects over an SSL
|
This is a subclass derived from :class:`IMAP4` that connects over an SSL
|
||||||
encrypted socket (to use this class you need a socket module that was compiled
|
encrypted socket (to use this class you need a socket module that was compiled
|
||||||
|
@ -95,8 +100,12 @@ There's also a subclass for secure connections:
|
||||||
mutually exclusive with *ssl_context*, a :class:`ValueError` is raised
|
mutually exclusive with *ssl_context*, a :class:`ValueError` is raised
|
||||||
if *keyfile*/*certfile* is provided along with *ssl_context*.
|
if *keyfile*/*certfile* is provided along with *ssl_context*.
|
||||||
|
|
||||||
|
The optional *timeout* parameter specifies a timeout in seconds for the
|
||||||
|
connection attempt. If timeout is not given or is None, the global default
|
||||||
|
socket timeout is used.
|
||||||
|
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
*ssl_context* parameter added.
|
*ssl_context* parameter was added.
|
||||||
|
|
||||||
.. versionchanged:: 3.4
|
.. versionchanged:: 3.4
|
||||||
The class now supports hostname check with
|
The class now supports hostname check with
|
||||||
|
@ -110,6 +119,8 @@ There's also a subclass for secure connections:
|
||||||
:func:`ssl.create_default_context` select the system's trusted CA
|
:func:`ssl.create_default_context` select the system's trusted CA
|
||||||
certificates for you.
|
certificates for you.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The optional *timeout* parameter was added.
|
||||||
|
|
||||||
The second subclass allows for connections created by a child process:
|
The second subclass allows for connections created by a child process:
|
||||||
|
|
||||||
|
@ -353,16 +364,22 @@ An :class:`IMAP4` instance has the following methods:
|
||||||
Send ``NOOP`` to server.
|
Send ``NOOP`` to server.
|
||||||
|
|
||||||
|
|
||||||
.. method:: IMAP4.open(host, port)
|
.. method:: IMAP4.open(host, port, timeout=None)
|
||||||
|
|
||||||
Opens socket to *port* at *host*. This method is implicitly called by
|
Opens socket to *port* at *host*. The optional *timeout* parameter
|
||||||
the :class:`IMAP4` constructor. The connection objects established by this
|
specifies a timeout in seconds for the connection attempt.
|
||||||
method will be used in the :meth:`IMAP4.read`, :meth:`IMAP4.readline`,
|
If timeout is not given or is None, the global default socket timeout
|
||||||
:meth:`IMAP4.send`, and :meth:`IMAP4.shutdown` methods. You may override
|
is used. Also note that if the *timeout* parameter is set to be zero,
|
||||||
this method.
|
it will raise a :class:`ValueError` to reject creating a non-blocking socket.
|
||||||
|
This method is implicitly called by the :class:`IMAP4` constructor.
|
||||||
|
The connection objects established by this method will be used in
|
||||||
|
the :meth:`IMAP4.read`, :meth:`IMAP4.readline`, :meth:`IMAP4.send`,
|
||||||
|
and :meth:`IMAP4.shutdown` methods. You may override this method.
|
||||||
|
|
||||||
.. audit-event:: imaplib.open self,host,port imaplib.IMAP4.open
|
.. audit-event:: imaplib.open self,host,port imaplib.IMAP4.open
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *timeout* parameter was added.
|
||||||
|
|
||||||
.. method:: IMAP4.partial(message_num, message_part, start, length)
|
.. method:: IMAP4.partial(message_num, message_part, start, length)
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,16 @@ When the garbage collector makes a collection in which some objects resurrect
|
||||||
been executed), do not block the collection of all objects that are still
|
been executed), do not block the collection of all objects that are still
|
||||||
unreachable. (Contributed by Pablo Galindo and Tim Peters in :issue:`38379`.)
|
unreachable. (Contributed by Pablo Galindo and Tim Peters in :issue:`38379`.)
|
||||||
|
|
||||||
|
imaplib
|
||||||
|
-------
|
||||||
|
|
||||||
|
:class:`~imaplib.IMAP4` and :class:`~imaplib.IMAP4_SSL` now have
|
||||||
|
an optional *timeout* parameter for their constructors.
|
||||||
|
Also, the :meth:`~imaplib.IMAP4.open` method now has an optional *timeout* parameter
|
||||||
|
with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and
|
||||||
|
:class:`~imaplib.IMAP4_stream` were applied to this change.
|
||||||
|
(Contributed by Dong-hee Na in :issue:`38615`.)
|
||||||
|
|
||||||
os
|
os
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
|
@ -135,10 +135,13 @@ class IMAP4:
|
||||||
|
|
||||||
r"""IMAP4 client class.
|
r"""IMAP4 client class.
|
||||||
|
|
||||||
Instantiate with: IMAP4([host[, port]])
|
Instantiate with: IMAP4([host[, port[, timeout=None]]])
|
||||||
|
|
||||||
host - host's name (default: localhost);
|
host - host's name (default: localhost);
|
||||||
port - port number (default: standard IMAP4 port).
|
port - port number (default: standard IMAP4 port).
|
||||||
|
timeout - socket timeout (default: None)
|
||||||
|
If timeout is not given or is None,
|
||||||
|
the global default socket timeout is used
|
||||||
|
|
||||||
All IMAP4rev1 commands are supported by methods of the same
|
All IMAP4rev1 commands are supported by methods of the same
|
||||||
name (in lower-case).
|
name (in lower-case).
|
||||||
|
@ -181,7 +184,7 @@ class IMAP4:
|
||||||
class abort(error): pass # Service errors - close and retry
|
class abort(error): pass # Service errors - close and retry
|
||||||
class readonly(abort): pass # Mailbox status changed to READ-ONLY
|
class readonly(abort): pass # Mailbox status changed to READ-ONLY
|
||||||
|
|
||||||
def __init__(self, host='', port=IMAP4_PORT):
|
def __init__(self, host='', port=IMAP4_PORT, timeout=None):
|
||||||
self.debug = Debug
|
self.debug = Debug
|
||||||
self.state = 'LOGOUT'
|
self.state = 'LOGOUT'
|
||||||
self.literal = None # A literal argument to a command
|
self.literal = None # A literal argument to a command
|
||||||
|
@ -195,7 +198,7 @@ class IMAP4:
|
||||||
|
|
||||||
# Open socket to server.
|
# Open socket to server.
|
||||||
|
|
||||||
self.open(host, port)
|
self.open(host, port, timeout)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._connect()
|
self._connect()
|
||||||
|
@ -284,15 +287,20 @@ class IMAP4:
|
||||||
# Overridable methods
|
# Overridable methods
|
||||||
|
|
||||||
|
|
||||||
def _create_socket(self):
|
def _create_socket(self, timeout):
|
||||||
# Default value of IMAP4.host is '', but socket.getaddrinfo()
|
# Default value of IMAP4.host is '', but socket.getaddrinfo()
|
||||||
# (which is used by socket.create_connection()) expects None
|
# (which is used by socket.create_connection()) expects None
|
||||||
# as a default value for host.
|
# as a default value for host.
|
||||||
|
if timeout is not None and not timeout:
|
||||||
|
raise ValueError('Non-blocking socket (timeout=0) is not supported')
|
||||||
host = None if not self.host else self.host
|
host = None if not self.host else self.host
|
||||||
sys.audit("imaplib.open", self, self.host, self.port)
|
sys.audit("imaplib.open", self, self.host, self.port)
|
||||||
return socket.create_connection((host, self.port))
|
address = (host, self.port)
|
||||||
|
if timeout is not None:
|
||||||
|
return socket.create_connection(address, timeout)
|
||||||
|
return socket.create_connection(address)
|
||||||
|
|
||||||
def open(self, host = '', port = IMAP4_PORT):
|
def open(self, host='', port=IMAP4_PORT, timeout=None):
|
||||||
"""Setup connection to remote server on "host:port"
|
"""Setup connection to remote server on "host:port"
|
||||||
(default: localhost:standard IMAP4 port).
|
(default: localhost:standard IMAP4 port).
|
||||||
This connection will be used by the routines:
|
This connection will be used by the routines:
|
||||||
|
@ -300,7 +308,7 @@ class IMAP4:
|
||||||
"""
|
"""
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.sock = self._create_socket()
|
self.sock = self._create_socket(timeout)
|
||||||
self.file = self.sock.makefile('rb')
|
self.file = self.sock.makefile('rb')
|
||||||
|
|
||||||
|
|
||||||
|
@ -1261,7 +1269,7 @@ if HAVE_SSL:
|
||||||
|
|
||||||
"""IMAP4 client class over SSL connection
|
"""IMAP4 client class over SSL connection
|
||||||
|
|
||||||
Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context]]]]])
|
Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context[, timeout=None]]]]]])
|
||||||
|
|
||||||
host - host's name (default: localhost);
|
host - host's name (default: localhost);
|
||||||
port - port number (default: standard IMAP4 SSL port);
|
port - port number (default: standard IMAP4 SSL port);
|
||||||
|
@ -1271,13 +1279,15 @@ if HAVE_SSL:
|
||||||
and private key (default: None)
|
and private key (default: None)
|
||||||
Note: if ssl_context is provided, then parameters keyfile or
|
Note: if ssl_context is provided, then parameters keyfile or
|
||||||
certfile should not be set otherwise ValueError is raised.
|
certfile should not be set otherwise ValueError is raised.
|
||||||
|
timeout - socket timeout (default: None) If timeout is not given or is None,
|
||||||
|
the global default socket timeout is used
|
||||||
|
|
||||||
for more documentation see the docstring of the parent class IMAP4.
|
for more documentation see the docstring of the parent class IMAP4.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None,
|
def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None,
|
||||||
certfile=None, ssl_context=None):
|
certfile=None, ssl_context=None, timeout=None):
|
||||||
if ssl_context is not None and keyfile is not None:
|
if ssl_context is not None and keyfile is not None:
|
||||||
raise ValueError("ssl_context and keyfile arguments are mutually "
|
raise ValueError("ssl_context and keyfile arguments are mutually "
|
||||||
"exclusive")
|
"exclusive")
|
||||||
|
@ -1294,20 +1304,20 @@ if HAVE_SSL:
|
||||||
ssl_context = ssl._create_stdlib_context(certfile=certfile,
|
ssl_context = ssl._create_stdlib_context(certfile=certfile,
|
||||||
keyfile=keyfile)
|
keyfile=keyfile)
|
||||||
self.ssl_context = ssl_context
|
self.ssl_context = ssl_context
|
||||||
IMAP4.__init__(self, host, port)
|
IMAP4.__init__(self, host, port, timeout)
|
||||||
|
|
||||||
def _create_socket(self):
|
def _create_socket(self, timeout):
|
||||||
sock = IMAP4._create_socket(self)
|
sock = IMAP4._create_socket(self, timeout)
|
||||||
return self.ssl_context.wrap_socket(sock,
|
return self.ssl_context.wrap_socket(sock,
|
||||||
server_hostname=self.host)
|
server_hostname=self.host)
|
||||||
|
|
||||||
def open(self, host='', port=IMAP4_SSL_PORT):
|
def open(self, host='', port=IMAP4_SSL_PORT, timeout=None):
|
||||||
"""Setup connection to remote server on "host:port".
|
"""Setup connection to remote server on "host:port".
|
||||||
(default: localhost:standard IMAP4 SSL port).
|
(default: localhost:standard IMAP4 SSL port).
|
||||||
This connection will be used by the routines:
|
This connection will be used by the routines:
|
||||||
read, readline, send, shutdown.
|
read, readline, send, shutdown.
|
||||||
"""
|
"""
|
||||||
IMAP4.open(self, host, port)
|
IMAP4.open(self, host, port, timeout)
|
||||||
|
|
||||||
__all__.append("IMAP4_SSL")
|
__all__.append("IMAP4_SSL")
|
||||||
|
|
||||||
|
@ -1329,7 +1339,7 @@ class IMAP4_stream(IMAP4):
|
||||||
IMAP4.__init__(self)
|
IMAP4.__init__(self)
|
||||||
|
|
||||||
|
|
||||||
def open(self, host = None, port = None):
|
def open(self, host=None, port=None, timeout=None):
|
||||||
"""Setup a stream connection.
|
"""Setup a stream connection.
|
||||||
This connection will be used by the routines:
|
This connection will be used by the routines:
|
||||||
read, readline, send, shutdown.
|
read, readline, send, shutdown.
|
||||||
|
|
|
@ -440,6 +440,29 @@ class NewIMAPTestsMixin():
|
||||||
with self.imap_class(*server.server_address):
|
with self.imap_class(*server.server_address):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_imaplib_timeout_test(self):
|
||||||
|
_, server = self._setup(SimpleIMAPHandler)
|
||||||
|
addr = server.server_address[1]
|
||||||
|
client = self.imap_class("localhost", addr, timeout=None)
|
||||||
|
self.assertEqual(client.sock.timeout, None)
|
||||||
|
client.shutdown()
|
||||||
|
client = self.imap_class("localhost", addr, timeout=support.LOOPBACK_TIMEOUT)
|
||||||
|
self.assertEqual(client.sock.timeout, support.LOOPBACK_TIMEOUT)
|
||||||
|
client.shutdown()
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
client = self.imap_class("localhost", addr, timeout=0)
|
||||||
|
|
||||||
|
def test_imaplib_timeout_functionality_test(self):
|
||||||
|
class TimeoutHandler(SimpleIMAPHandler):
|
||||||
|
def handle(self):
|
||||||
|
time.sleep(1)
|
||||||
|
SimpleIMAPHandler.handle(self)
|
||||||
|
|
||||||
|
_, server = self._setup(TimeoutHandler)
|
||||||
|
addr = server.server_address[1]
|
||||||
|
with self.assertRaises(socket.timeout):
|
||||||
|
client = self.imap_class("localhost", addr, timeout=0.001)
|
||||||
|
|
||||||
def test_with_statement(self):
|
def test_with_statement(self):
|
||||||
_, server = self._setup(SimpleIMAPHandler, connect=False)
|
_, server = self._setup(SimpleIMAPHandler, connect=False)
|
||||||
with self.imap_class(*server.server_address) as imap:
|
with self.imap_class(*server.server_address) as imap:
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
:class:`~imaplib.IMAP4` and :class:`~imaplib.IMAP4_SSL` now have an
|
||||||
|
optional *timeout* parameter for their constructors.
|
||||||
|
Also, the :meth:`~imaplib.IMAP4.open` method now has an optional *timeout* parameter
|
||||||
|
with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and
|
||||||
|
:class:`~imaplib.IMAP4_stream` were applied to this change. Patch by Dong-hee Na.
|
Loading…
Reference in New Issue