[Python-checkins] cpython (merge default -> default): merge

brett.cannon python-checkins at python.org
Tue Mar 15 21:03:43 CET 2011


http://hg.python.org/cpython/rev/e04253f3ad4a
changeset:   68532:e04253f3ad4a
parent:      68531:07f5ec8fc014
parent:      68528:fbdcc7437b34
user:        Brett Cannon <brett at python.org>
date:        Tue Mar 15 16:03:32 2011 -0400
summary:
  merge

files:
  

diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -537,9 +537,10 @@
    operations such as ``PyEval_ReleaseThread(tstate)``. It is not needed before
    calling :c:func:`PyEval_SaveThread` or :c:func:`PyEval_RestoreThread`.
 
-   .. index:: single: Py_Initialize()
+   This is a no-op when called for a second time.
 
-   This is a no-op when called for a second time.
+   .. versionchanged:: 3.2
+      This function cannot be called before :c:func:`Py_Initialize()` anymore.
 
    .. index:: module: _thread
 
diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst
--- a/Doc/library/smtplib.rst
+++ b/Doc/library/smtplib.rst
@@ -34,6 +34,20 @@
    For normal use, you should only require the initialization/connect,
    :meth:`sendmail`, and :meth:`quit` methods.  An example is included below.
 
+   The :class:`SMTP` class supports the :keyword:`with` statement.  When used
+   like this, the SMTP ``QUIT`` command is issued automatically when the
+   :keyword:`with` statement exits.  E.g.::
+
+    >>> from smtplib import SMTP
+    >>> with SMTP("domain.org") as smtp:
+    ...     smtp.noop()
+    ...
+    (250, b'Ok')
+    >>>
+
+   .. versionadded:: 3.3
+      Support for the :keyword:`with` statement was added.
+
 
 .. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout])
 
diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst
--- a/Doc/whatsnew/3.2.rst
+++ b/Doc/whatsnew/3.2.rst
@@ -2698,3 +2698,7 @@
   a new function, :func:`asyncore.handle_accepted`, was added to replace it.
 
   (Contributed by Giampaolo Rodola in :issue:`6706`.)
+
+* Due to the new :term:`GIL` implementation, :c:func:`PyEval_InitThreads()`
+  cannot be called before :c:func:`Py_Initialize()` anymore.
+
diff --git a/Lib/smtplib.py b/Lib/smtplib.py
--- a/Lib/smtplib.py
+++ b/Lib/smtplib.py
@@ -269,6 +269,19 @@
                     pass
                 self.local_hostname = '[%s]' % addr
 
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *args):
+        try:
+            code, message = self.docmd("QUIT")
+            if code != 221:
+                raise SMTPResponseException(code, message)
+        except SMTPServerDisconnected:
+            pass
+        finally:
+            self.close()
+
     def set_debuglevel(self, debuglevel):
         """Set the debug output level.
 
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1,11 +1,35 @@
 # Minimal tests for dis module
 
 from test.support import run_unittest, captured_stdout
+import difflib
 import unittest
 import sys
 import dis
 import io
 
+class _C:
+    def __init__(self, x):
+        self.x = x == 1
+
+dis_c_instance_method = """\
+ %-4d         0 LOAD_FAST                1 (x)
+              3 LOAD_CONST               1 (1)
+              6 COMPARE_OP               2 (==)
+              9 LOAD_FAST                0 (self)
+             12 STORE_ATTR               0 (x)
+             15 LOAD_CONST               0 (None)
+             18 RETURN_VALUE
+""" % (_C.__init__.__code__.co_firstlineno + 1,)
+
+dis_c_instance_method_bytes = """\
+          0 LOAD_FAST           1 (1)
+          3 LOAD_CONST          1 (1)
+          6 COMPARE_OP          2 (==)
+          9 LOAD_FAST           0 (0)
+         12 STORE_ATTR          0 (0)
+         15 LOAD_CONST          0 (0)
+         18 RETURN_VALUE
+"""
 
 def _f(a):
     print(a)
@@ -23,6 +47,16 @@
        _f.__code__.co_firstlineno + 2)
 
 
+dis_f_co_code = """\
+          0 LOAD_GLOBAL         0 (0)
+          3 LOAD_FAST           0 (0)
+          6 CALL_FUNCTION       1
+          9 POP_TOP
+         10 LOAD_CONST          1 (1)
+         13 RETURN_VALUE
+"""
+
+
 def bug708901():
     for res in range(1,
                      10):
@@ -138,18 +172,27 @@
 """
 
 class DisTests(unittest.TestCase):
-    def do_disassembly_test(self, func, expected):
+
+    def get_disassembly(self, func, lasti=-1, wrapper=True):
         s = io.StringIO()
         save_stdout = sys.stdout
         sys.stdout = s
-        dis.dis(func)
-        sys.stdout = save_stdout
-        got = s.getvalue()
+        try:
+            if wrapper:
+                dis.dis(func)
+            else:
+                dis.disassemble(func, lasti)
+        finally:
+            sys.stdout = save_stdout
         # Trim trailing blanks (if any).
-        lines = got.split('\n')
-        lines = [line.rstrip() for line in lines]
-        expected = expected.split("\n")
-        import difflib
+        return [line.rstrip() for line in s.getvalue().splitlines()]
+
+    def get_disassemble_as_string(self, func, lasti=-1):
+        return '\n'.join(self.get_disassembly(func, lasti, False))
+
+    def do_disassembly_test(self, func, expected):
+        lines = self.get_disassembly(func)
+        expected = expected.splitlines()
         if expected != lines:
             self.fail(
                 "events did not match expectation:\n" +
@@ -211,6 +254,46 @@
         self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str)
         self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str)
 
+    def test_disassemble_bytes(self):
+        self.do_disassembly_test(_f.__code__.co_code, dis_f_co_code)
+
+    def test_disassemble_method(self):
+        self.do_disassembly_test(_C(1).__init__, dis_c_instance_method)
+
+    def test_disassemble_method_bytes(self):
+        method_bytecode = _C(1).__init__.__code__.co_code
+        self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes)
+
+    def test_dis_none(self):
+        self.assertRaises(RuntimeError, dis.dis, None)
+
+    def test_dis_object(self):
+        self.assertRaises(TypeError, dis.dis, object())
+
+    def test_dis_traceback(self):
+        not_defined = object()
+        tb = None
+        old = getattr(sys, 'last_traceback', not_defined)
+
+        def cleanup():
+            if old != not_defined:
+                sys.last_traceback = old
+            else:
+                del sys.last_traceback
+
+        try:
+            1/0
+        except Exception as e:
+            tb = e.__traceback__
+            sys.last_traceback = tb
+            self.addCleanup(cleanup)
+
+        tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti)
+        self.do_disassembly_test(None, tb_dis)
+
+    def test_dis_object(self):
+        self.assertRaises(TypeError, dis.dis, object())
+
 code_info_code_info = """\
 Name:              code_info
 Filename:          (.*)
@@ -363,6 +446,13 @@
                 dis.show_code(x)
             self.assertRegex(output.getvalue(), expected+"\n")
 
+    def test_code_info_object(self):
+        self.assertRaises(TypeError, dis.code_info, object())
+
+    def test_pretty_flags_no_flags(self):
+        self.assertEqual(dis.pretty_flags(0), '0x0')
+
+
 def test_main():
     run_unittest(DisTests, CodeInfoTests)
 
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
--- a/Lib/test/test_smtplib.py
+++ b/Lib/test/test_smtplib.py
@@ -424,6 +424,9 @@
 # Simulated SMTP channel & server
 class SimSMTPChannel(smtpd.SMTPChannel):
 
+    # For testing failures in QUIT when using the context manager API.
+    quit_response = None
+
     def __init__(self, extra_features, *args, **kw):
         self._extrafeatures = ''.join(
             [ "250-{0}\r\n".format(x) for x in extra_features ])
@@ -475,19 +478,31 @@
         else:
             self.push('550 No access for you!')
 
+    def smtp_QUIT(self, arg):
+        # args is ignored
+        if self.quit_response is None:
+            super(SimSMTPChannel, self).smtp_QUIT(arg)
+        else:
+            self.push(self.quit_response)
+            self.close_when_done()
+
     def handle_error(self):
         raise
 
 
 class SimSMTPServer(smtpd.SMTPServer):
 
+    # For testing failures in QUIT when using the context manager API.
+    quit_response = None
+
     def __init__(self, *args, **kw):
         self._extra_features = []
         smtpd.SMTPServer.__init__(self, *args, **kw)
 
     def handle_accepted(self, conn, addr):
-        self._SMTPchannel = SimSMTPChannel(self._extra_features,
-                                           self, conn, addr)
+        self._SMTPchannel = SimSMTPChannel(
+            self._extra_features, self, conn, addr)
+        self._SMTPchannel.quit_response = self.quit_response
 
     def process_message(self, peer, mailfrom, rcpttos, data):
         pass
@@ -620,6 +635,25 @@
             self.assertIn(sim_auth_credentials['cram-md5'], str(err))
         smtp.close()
 
+    def test_with_statement(self):
+        with smtplib.SMTP(HOST, self.port) as smtp:
+            code, message = smtp.noop()
+            self.assertEqual(code, 250)
+        self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo')
+        with smtplib.SMTP(HOST, self.port) as smtp:
+            smtp.close()
+        self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo')
+
+    def test_with_statement_QUIT_failure(self):
+        self.serv.quit_response = '421 QUIT FAILED'
+        with self.assertRaises(smtplib.SMTPResponseException) as error:
+            with smtplib.SMTP(HOST, self.port) as smtp:
+                smtp.noop()
+        self.assertEqual(error.exception.smtp_code, 421)
+        self.assertEqual(error.exception.smtp_error, b'QUIT FAILED')
+        # We don't need to clean up self.serv.quit_response because a new
+        # server is always instantiated in the setUp().
+
     #TODO: add tests for correct AUTH method fallback now that the
     #test infrastructure can support it.
 
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -3,6 +3,7 @@
 import subprocess
 import sys
 import signal
+import io
 import os
 import errno
 import tempfile
@@ -1256,6 +1257,24 @@
                         close_fds=False, pass_fds=(fd, )))
             self.assertIn('overriding close_fds', str(context.warning))
 
+    def test_stdout_stdin_are_single_inout_fd(self):
+        with io.open(os.devnull, "r+") as inout:
+            p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
+                                 stdout=inout, stdin=inout)
+            p.wait()
+
+    def test_stdout_stderr_are_single_inout_fd(self):
+        with io.open(os.devnull, "r+") as inout:
+            p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
+                                 stdout=inout, stderr=inout)
+            p.wait()
+
+    def test_stderr_stdin_are_single_inout_fd(self):
+        with io.open(os.devnull, "r+") as inout:
+            p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"],
+                                 stderr=inout, stdin=inout)
+            p.wait()
+
     def test_wait_when_sigchild_ignored(self):
         # NOTE: sigchild_ignore.py may not be an effective test on all OSes.
         sigchild_ignore = support.findfile("sigchild_ignore.py",
@@ -1543,4 +1562,4 @@
     support.reap_children()
 
 if __name__ == "__main__":
-    test_main()
+    unittest.main()
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -228,15 +228,9 @@
         else:
             # for TTY browsers, we need stdin/out
             inout = None
-        # if possible, put browser in separate process group, so
-        # keyboard interrupts don't affect browser as well as Python
-        setsid = getattr(os, 'setsid', None)
-        if not setsid:
-            setsid = getattr(os, 'setpgrp', None)
-
         p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
                              stdout=(self.redirect_stdout and inout or None),
-                             stderr=inout, preexec_fn=setsid)
+                             stderr=inout, start_new_session=True)
         if remote:
             # wait five secons. If the subprocess is not finished, the
             # remote invocation has (hopefully) started a new instance.
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -100,6 +100,7 @@
 Forest Bond
 Gawain Bolton
 Gregory Bond
+Matias Bordese
 Jurjen Bos
 Peter Bosch
 Eric Bouck
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -68,6 +68,9 @@
 Library
 -------
 
+- Issue #11289: `smtp.SMTP` class becomes a context manager so it can be used
+  in a `with` statement.  Contributed by Giampaolo Rodola.
+
 - Issue #11554: Fixed support for Japanese codecs; previously the body output
   encoding was not done if euc-jp or shift-jis was specified as the charset.
 
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c
--- a/Modules/_posixsubprocess.c
+++ b/Modules/_posixsubprocess.c
@@ -99,10 +99,10 @@
     if (p2cread > 2) {
         POSIX_CALL(close(p2cread));
     }
-    if (c2pwrite > 2) {
+    if (c2pwrite > 2 && c2pwrite != p2cread) {
         POSIX_CALL(close(c2pwrite));
     }
-    if (errwrite != c2pwrite && errwrite > 2) {
+    if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) {
         POSIX_CALL(close(errwrite));
     }
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list