Index: Lib/smtplib.py =================================================================== --- Lib/smtplib.py (révision 68762) +++ Lib/smtplib.py (copie de travail) @@ -257,6 +257,21 @@ pass self.local_hostname = '[%s]' % addr + def __enter__(self): + """Context management protocol. Returns self.""" + return self + + def __exit__(self, *args): + """Context management protocol. + + Will try to quit(), and will close() in case of an exception.""" + try: + errcode, errmsg = self.quit() + if errcode != 221: + raise SMTPResponseException(errmsg) + except SMTPServerDisconnected: + self.close() + def set_debuglevel(self, debuglevel): """Set the debug output level. Index: Lib/test/test_smtplib.py =================================================================== --- Lib/test/test_smtplib.py (révision 68762) +++ Lib/test/test_smtplib.py (copie de travail) @@ -92,7 +92,11 @@ self.assertEqual(smtp.sock.gettimeout(), 30) smtp.close() + def testBasicWithStatement(self): + with smtplib.SMTP(HOST, self.port) as smtp: + pass + # Test server thread using the specified SMTP server class def debugging_server(serv, serv_evt, client_evt): serv_evt.set() @@ -224,7 +228,19 @@ mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END) self.assertEqual(self.output.getvalue(), mexpect) + def testSendWithStatement(self): + m = 'A test message' + with smtplib.SMTP(HOST, self.port, local_hostname='localhost', + timeout=3) as smtp: + smtp.sendmail('John', 'Sally', m) + time.sleep(0.01) + self.client_evt.set() + self.serv_evt.wait() + self.output.flush() + mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END) + self.assertEqual(self.output.getvalue(), mexpect) + class NonConnectingTests(TestCase): def testNotConnected(self): Index: Doc/whatsnew/2.7.rst =================================================================== --- Doc/whatsnew/2.7.rst (révision 68762) +++ Doc/whatsnew/2.7.rst (copie de travail) @@ -131,6 +131,9 @@ prompt for the password if not present. (Added by tarek, with the initial contribution of Nathan Van Gheem; :issue:`4394`.) +* Now :class:`SMTP` can be used in a :keyword:`with` block. + (Contributed by Tarek) + .. ====================================================================== .. whole new modules get described in subsections here Index: Doc/library/smtplib.rst =================================================================== --- Doc/library/smtplib.rst (révision 68762) +++ Doc/library/smtplib.rst (copie de travail) @@ -335,6 +335,21 @@ Return a value. +.. method:: SMTP.__enter__() + SMTP.__exit__(\*exc_info) + + The SMTP class implements :meth:`__enter__` and :meth:`__exit__` and thus + supports the context protocol for the :keyword:`with` statement:: + + with SMTP('some.host') as smtp: + # ... work with smtp server ... + + will automatically try to terminate the SMTP session by calling + :meth:`quit`, then will call :meth:`close`, when control leaves the + :keyword:`with` block. + + .. versionadded:: 2.7 + Low-level methods corresponding to the standard SMTP/ESMTP commands ``HELP``, ``RSET``, ``NOOP``, ``MAIL``, ``RCPT``, and ``DATA`` are also supported. Normally these do not need to be called directly, so they are not documented @@ -380,3 +395,16 @@ server.sendmail(fromaddr, toaddrs, msg) server.quit() +Another example, using a :keyword:`with` block. :meth:`quit` is automatically +called at the end of the block:: + + import smtplib + + fromaddr = 'sender@example.com' + toaddrs = ['receiver@example.com'] + msg = "From: %s\r\nTo: %s\r\n\r\nA message." % (fromaddr, + ",".join(toaddrs)) + + with smtplib.SMTP('localhost') as server: + server.sendmail(fromaddr, toaddrs, msg) +