imaplib.IMAP4 now supports the context manager protocol.

Original patch by Tarek Ziadé.
This commit is contained in:
Serhiy Storchaka 2014-09-09 19:07:49 +03:00
parent bb1e3f1ebe
commit 38684c3663
5 changed files with 67 additions and 0 deletions

View File

@ -37,6 +37,19 @@ base class:
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 :class:`IMAP4` class supports the :keyword:`with` statement. When used
like this, the IMAP4 ``LOGOUT`` command is issued automatically when the
:keyword:`with` statement exits. E.g.::
>>> from imaplib import IMAP4
>>> with IMAP4("domain.org") as M:
... M.noop()
...
('OK', [b'Nothing Accomplished. d25if65hy903weo.87'])
.. versionchanged:: 3.5
Support for the :keyword:`with` statement was added.
Three exceptions are defined as attributes of the :class:`IMAP4` class: Three exceptions are defined as attributes of the :class:`IMAP4` class:

View File

@ -141,6 +141,14 @@ doctest
*module* contains no docstrings instead of raising :exc:`ValueError` *module* contains no docstrings instead of raising :exc:`ValueError`
(contributed by Glenn Jones in :issue:`15916`). (contributed by Glenn Jones in :issue:`15916`).
imaplib
-------
* :class:`IMAP4` now supports the context management protocol. When used in a
:keyword:`with` statement, the IMAP4 ``LOGOUT`` command will be called
automatically at the end of the block. (Contributed by Tarek Ziadé and
Serhiy Storchaka in :issue:`4972`).
imghdr imghdr
------ ------

View File

@ -238,6 +238,14 @@ class IMAP4:
return getattr(self, attr.lower()) return getattr(self, attr.lower())
raise AttributeError("Unknown IMAP4 command: '%s'" % attr) raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
def __enter__(self):
return self
def __exit__(self, *args):
try:
self.logout()
except OSError:
pass
# Overridable methods # Overridable methods

View File

@ -98,6 +98,10 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler):
continuation = None continuation = None
capabilities = '' capabilities = ''
def setup(self):
super().setup()
self.server.logged = None
def _send(self, message): def _send(self, message):
if verbose: if verbose:
print("SENT: %r" % message.strip()) print("SENT: %r" % message.strip())
@ -162,9 +166,14 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler):
self._send_tagged(tag, 'OK', 'CAPABILITY completed') self._send_tagged(tag, 'OK', 'CAPABILITY completed')
def cmd_LOGOUT(self, tag, args): def cmd_LOGOUT(self, tag, args):
self.server.logged = None
self._send_textline('* BYE IMAP4ref1 Server logging out') self._send_textline('* BYE IMAP4ref1 Server logging out')
self._send_tagged(tag, 'OK', 'LOGOUT completed') self._send_tagged(tag, 'OK', 'LOGOUT completed')
def cmd_LOGIN(self, tag, args):
self.server.logged = args[0]
self._send_tagged(tag, 'OK', 'LOGIN completed')
class ThreadedNetworkedTests(unittest.TestCase): class ThreadedNetworkedTests(unittest.TestCase):
server_class = socketserver.TCPServer server_class = socketserver.TCPServer
@ -345,6 +354,32 @@ class ThreadedNetworkedTests(unittest.TestCase):
self.assertRaises(imaplib.IMAP4.error, self.assertRaises(imaplib.IMAP4.error,
self.imap_class, *server.server_address) self.imap_class, *server.server_address)
@reap_threads
def test_simple_with_statement(self):
# simplest call
with self.reaped_server(SimpleIMAPHandler) as server:
with self.imap_class(*server.server_address):
pass
@reap_threads
def test_with_statement(self):
with self.reaped_server(SimpleIMAPHandler) as server:
with self.imap_class(*server.server_address) as imap:
imap.login('user', 'pass')
self.assertEqual(server.logged, 'user')
self.assertIsNone(server.logged)
@reap_threads
def test_with_statement_logout(self):
# what happens if already logout in the block?
with self.reaped_server(SimpleIMAPHandler) as server:
with self.imap_class(*server.server_address) as imap:
imap.login('user', 'pass')
self.assertEqual(server.logged, 'user')
imap.logout()
self.assertIsNone(server.logged)
self.assertIsNone(server.logged)
@unittest.skipUnless(ssl, "SSL not available") @unittest.skipUnless(ssl, "SSL not available")
class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests): class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests):

View File

@ -132,6 +132,9 @@ Core and Builtins
Library Library
------- -------
- Issue #12410: imaplib.IMAP4 now supports the context manager protocol.
Original patch by Tarek Ziadé.
- Issue #16662: load_tests() is now unconditionally run when it is present in - Issue #16662: load_tests() is now unconditionally run when it is present in
a package's __init__.py. TestLoader.loadTestsFromModule() still accepts a package's __init__.py. TestLoader.loadTestsFromModule() still accepts
use_load_tests, but it is deprecated and ignored. A new keyword-only use_load_tests, but it is deprecated and ignored. A new keyword-only