]> granicus.if.org Git - python/commitdiff
imaplib.IMAP4 now supports the context manager protocol.
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 9 Sep 2014 16:07:49 +0000 (19:07 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Tue, 9 Sep 2014 16:07:49 +0000 (19:07 +0300)
Original patch by Tarek Ziadé.

Doc/library/imaplib.rst
Doc/whatsnew/3.5.rst
Lib/imaplib.py
Lib/test/test_imaplib.py
Misc/NEWS

index fa736fe3af44924fa6fb641bfc881a735a7260b7..f263bab9f02b103704510d2b9cb49a402dfc4a7c 100644 (file)
@@ -37,6 +37,19 @@ base class:
    initialized. If *host* is not specified, ``''`` (the local host) is used. If
    *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:
 
 
index 43972b0f36a61b68dcd0daf4208f9bb45f531939..38ea2f203a6124bfb30d6f170d028d9f9a186ae2 100644 (file)
@@ -141,6 +141,14 @@ doctest
   *module* contains no docstrings instead of raising :exc:`ValueError`
   (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
 ------
 
index ad104fe76a71d8ee3259ddd76f6be0c7d3779979..27445dd6c51da7724e0ae93460e2ce4b1912a6de 100644 (file)
@@ -238,6 +238,14 @@ class IMAP4:
             return getattr(self, attr.lower())
         raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
 
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *args):
+        try:
+            self.logout()
+        except OSError:
+            pass
 
 
     #       Overridable methods
index 910aa0e60a15cb606d1e8b2e9d38407a8a2456d0..b58707359c677164f5a03ba77158e911d8eb463e 100644 (file)
@@ -98,6 +98,10 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler):
     continuation = None
     capabilities = ''
 
+    def setup(self):
+        super().setup()
+        self.server.logged = None
+
     def _send(self, message):
         if verbose:
             print("SENT: %r" % message.strip())
@@ -162,9 +166,14 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler):
         self._send_tagged(tag, 'OK', 'CAPABILITY completed')
 
     def cmd_LOGOUT(self, tag, args):
+        self.server.logged = None
         self._send_textline('* BYE IMAP4ref1 Server logging out')
         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):
     server_class = socketserver.TCPServer
@@ -345,6 +354,32 @@ class ThreadedNetworkedTests(unittest.TestCase):
             self.assertRaises(imaplib.IMAP4.error,
                               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")
 class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests):
index 157e72c52d6b0bf92b2e1120846e679a07df0eed..ec19e7b2836bb39be6933dab132fd457dadc15f1 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -132,6 +132,9 @@ Core and Builtins
 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
   a package's __init__.py.  TestLoader.loadTestsFromModule() still accepts
   use_load_tests, but it is deprecated and ignored.  A new keyword-only