[python/f20] Fix test failures with SQLite 3.8.4

Bohuslav Kabrda bkabrda at fedoraproject.org
Thu Jun 19 12:08:09 UTC 2014


commit bc6f71354c70a045b143d1ee8ee69a8508fb512a
Author: Slavek Kabrda <bkabrda at redhat.com>
Date:   Thu Jun 19 14:07:48 2014 +0200

    Fix test failures with SQLite 3.8.4
    
    - Fix double close of subprocess pipes when child process fails
    Resolves: rhbz#1103450

 00194-fix-tests-with-sqlite-3.8.4.patch            |   19 ++
 ...uble-close-of-pipes-on-child-process-fail.patch |  288 ++++++++++++++++++++
 python.spec                                        |   22 ++-
 3 files changed, 328 insertions(+), 1 deletions(-)
---
diff --git a/00194-fix-tests-with-sqlite-3.8.4.patch b/00194-fix-tests-with-sqlite-3.8.4.patch
new file mode 100644
index 0000000..4037c26
--- /dev/null
+++ b/00194-fix-tests-with-sqlite-3.8.4.patch
@@ -0,0 +1,19 @@
+# HG changeset patch
+# User Benjamin Peterson <benjamin at python.org>
+# Date 1394679112 18000
+# Node ID 1763e27a182d571cc3a428c71085bb86b3d895b5
+# Parent  1d31060f8a5c9695f0b79a738d355d8530e09cc7
+weaken callback count inequality (closes #20901)
+
+diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py
+--- a/Lib/sqlite3/test/hooks.py
++++ b/Lib/sqlite3/test/hooks.py
+@@ -162,7 +162,7 @@ class ProgressTests(unittest.TestCase):
+             create table bar (a, b)
+             """)
+         second_count = len(progress_calls)
+-        self.assertTrue(first_count > second_count)
++        self.assertGreaterEqual(first_count, second_count)
+ 
+     def CheckCancelOperation(self):
+         """
diff --git a/00195-avoid-double-close-of-pipes-on-child-process-fail.patch b/00195-avoid-double-close-of-pipes-on-child-process-fail.patch
new file mode 100644
index 0000000..bd53bbc
--- /dev/null
+++ b/00195-avoid-double-close-of-pipes-on-child-process-fail.patch
@@ -0,0 +1,288 @@
+
+# HG changeset patch
+# User Antoine Pitrou <solipsis at pitrou.net>
+# Date 1377898693 -7200
+# Node ID 43749cb6bdbd0fdab70f76cd171c3c02a3f600dd
+# Parent  ba54011aa295004ad87438211fe3bb1568dd69ab
+Issue #18851: Avoid a double close of subprocess pipes when the child process fails starting.
+
+diff --git a/Lib/subprocess.py b/Lib/subprocess.py
+--- a/Lib/subprocess.py
++++ b/Lib/subprocess.py
+@@ -698,12 +698,12 @@ class Popen(object):
+ 
+         (p2cread, p2cwrite,
+          c2pread, c2pwrite,
+-         errread, errwrite) = self._get_handles(stdin, stdout, stderr)
++         errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
+ 
+         try:
+             self._execute_child(args, executable, preexec_fn, close_fds,
+                                 cwd, env, universal_newlines,
+-                                startupinfo, creationflags, shell,
++                                startupinfo, creationflags, shell, to_close,
+                                 p2cread, p2cwrite,
+                                 c2pread, c2pwrite,
+                                 errread, errwrite)
+@@ -711,18 +711,12 @@ class Popen(object):
+             # Preserve original exception in case os.close raises.
+             exc_type, exc_value, exc_trace = sys.exc_info()
+ 
+-            to_close = []
+-            # Only close the pipes we created.
+-            if stdin == PIPE:
+-                to_close.extend((p2cread, p2cwrite))
+-            if stdout == PIPE:
+-                to_close.extend((c2pread, c2pwrite))
+-            if stderr == PIPE:
+-                to_close.extend((errread, errwrite))
+-
+             for fd in to_close:
+                 try:
+-                    os.close(fd)
++                    if mswindows:
++                        fd.Close()
++                    else:
++                        os.close(fd)
+                 except EnvironmentError:
+                     pass
+ 
+@@ -816,8 +810,9 @@ class Popen(object):
+             """Construct and return tuple with IO objects:
+             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+             """
++            to_close = set()
+             if stdin is None and stdout is None and stderr is None:
+-                return (None, None, None, None, None, None)
++                return (None, None, None, None, None, None), to_close
+ 
+             p2cread, p2cwrite = None, None
+             c2pread, c2pwrite = None, None
+@@ -835,6 +830,10 @@ class Popen(object):
+                 # Assuming file-like object
+                 p2cread = msvcrt.get_osfhandle(stdin.fileno())
+             p2cread = self._make_inheritable(p2cread)
++            # We just duplicated the handle, it has to be closed at the end
++            to_close.add(p2cread)
++            if stdin == PIPE:
++                to_close.add(p2cwrite)
+ 
+             if stdout is None:
+                 c2pwrite = _subprocess.GetStdHandle(_subprocess.STD_OUTPUT_HANDLE)
+@@ -848,6 +847,10 @@ class Popen(object):
+                 # Assuming file-like object
+                 c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
+             c2pwrite = self._make_inheritable(c2pwrite)
++            # We just duplicated the handle, it has to be closed at the end
++            to_close.add(c2pwrite)
++            if stdout == PIPE:
++                to_close.add(c2pread)
+ 
+             if stderr is None:
+                 errwrite = _subprocess.GetStdHandle(_subprocess.STD_ERROR_HANDLE)
+@@ -863,10 +866,14 @@ class Popen(object):
+                 # Assuming file-like object
+                 errwrite = msvcrt.get_osfhandle(stderr.fileno())
+             errwrite = self._make_inheritable(errwrite)
++            # We just duplicated the handle, it has to be closed at the end
++            to_close.add(errwrite)
++            if stderr == PIPE:
++                to_close.add(errread)
+ 
+             return (p2cread, p2cwrite,
+                     c2pread, c2pwrite,
+-                    errread, errwrite)
++                    errread, errwrite), to_close
+ 
+ 
+         def _make_inheritable(self, handle):
+@@ -895,7 +902,7 @@ class Popen(object):
+ 
+         def _execute_child(self, args, executable, preexec_fn, close_fds,
+                            cwd, env, universal_newlines,
+-                           startupinfo, creationflags, shell,
++                           startupinfo, creationflags, shell, to_close,
+                            p2cread, p2cwrite,
+                            c2pread, c2pwrite,
+                            errread, errwrite):
+@@ -934,6 +941,10 @@ class Popen(object):
+                     # kill children.
+                     creationflags |= _subprocess.CREATE_NEW_CONSOLE
+ 
++            def _close_in_parent(fd):
++                fd.Close()
++                to_close.remove(fd)
++
+             # Start the process
+             try:
+                 hp, ht, pid, tid = _subprocess.CreateProcess(executable, args,
+@@ -958,11 +969,11 @@ class Popen(object):
+                 # pipe will not close when the child process exits and the
+                 # ReadFile will hang.
+                 if p2cread is not None:
+-                    p2cread.Close()
++                    _close_in_parent(p2cread)
+                 if c2pwrite is not None:
+-                    c2pwrite.Close()
++                    _close_in_parent(c2pwrite)
+                 if errwrite is not None:
+-                    errwrite.Close()
++                    _close_in_parent(errwrite)
+ 
+             # Retain the process handle, but close the thread handle
+             self._child_created = True
+@@ -1088,6 +1099,7 @@ class Popen(object):
+             """Construct and return tuple with IO objects:
+             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
+             """
++            to_close = set()
+             p2cread, p2cwrite = None, None
+             c2pread, c2pwrite = None, None
+             errread, errwrite = None, None
+@@ -1096,6 +1108,7 @@ class Popen(object):
+                 pass
+             elif stdin == PIPE:
+                 p2cread, p2cwrite = self.pipe_cloexec()
++                to_close.update((p2cread, p2cwrite))
+             elif isinstance(stdin, int):
+                 p2cread = stdin
+             else:
+@@ -1106,6 +1119,7 @@ class Popen(object):
+                 pass
+             elif stdout == PIPE:
+                 c2pread, c2pwrite = self.pipe_cloexec()
++                to_close.update((c2pread, c2pwrite))
+             elif isinstance(stdout, int):
+                 c2pwrite = stdout
+             else:
+@@ -1116,6 +1130,7 @@ class Popen(object):
+                 pass
+             elif stderr == PIPE:
+                 errread, errwrite = self.pipe_cloexec()
++                to_close.update((errread, errwrite))
+             elif stderr == STDOUT:
+                 errwrite = c2pwrite
+             elif isinstance(stderr, int):
+@@ -1126,7 +1141,7 @@ class Popen(object):
+ 
+             return (p2cread, p2cwrite,
+                     c2pread, c2pwrite,
+-                    errread, errwrite)
++                    errread, errwrite), to_close
+ 
+ 
+         def _set_cloexec_flag(self, fd, cloexec=True):
+@@ -1170,7 +1185,7 @@ class Popen(object):
+ 
+         def _execute_child(self, args, executable, preexec_fn, close_fds,
+                            cwd, env, universal_newlines,
+-                           startupinfo, creationflags, shell,
++                           startupinfo, creationflags, shell, to_close,
+                            p2cread, p2cwrite,
+                            c2pread, c2pwrite,
+                            errread, errwrite):
+@@ -1189,6 +1204,10 @@ class Popen(object):
+             if executable is None:
+                 executable = args[0]
+ 
++            def _close_in_parent(fd):
++                os.close(fd)
++                to_close.remove(fd)
++
+             # For transferring possible exec failure from child to parent
+             # The first char specifies the exception type: 0 means
+             # OSError, 1 means some other error.
+@@ -1283,17 +1302,17 @@ class Popen(object):
+                     # be sure the FD is closed no matter what
+                     os.close(errpipe_write)
+ 
+-                if p2cread is not None and p2cwrite is not None:
+-                    os.close(p2cread)
+-                if c2pwrite is not None and c2pread is not None:
+-                    os.close(c2pwrite)
+-                if errwrite is not None and errread is not None:
+-                    os.close(errwrite)
+-
+                 # Wait for exec to fail or succeed; possibly raising exception
+                 # Exception limited to 1M
+                 data = _eintr_retry_call(os.read, errpipe_read, 1048576)
+             finally:
++                if p2cread is not None and p2cwrite is not None:
++                    _close_in_parent(p2cread)
++                if c2pwrite is not None and c2pread is not None:
++                    _close_in_parent(c2pwrite)
++                if errwrite is not None and errread is not None:
++                    _close_in_parent(errwrite)
++
+                 # be sure the FD is closed no matter what
+                 os.close(errpipe_read)
+ 
+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
+@@ -14,6 +14,10 @@ try:
+     import resource
+ except ImportError:
+     resource = None
++try:
++    import threading
++except ImportError:
++    threading = None
+ 
+ mswindows = (sys.platform == "win32")
+ 
+@@ -629,6 +633,36 @@ class ProcessTestCase(BaseTestCase):
+             if c.exception.errno not in (errno.ENOENT, errno.EACCES):
+                 raise c.exception
+ 
++    @unittest.skipIf(threading is None, "threading required")
++    def test_double_close_on_error(self):
++        # Issue #18851
++        fds = []
++        def open_fds():
++            for i in range(20):
++                fds.extend(os.pipe())
++                time.sleep(0.001)
++        t = threading.Thread(target=open_fds)
++        t.start()
++        try:
++            with self.assertRaises(EnvironmentError):
++                subprocess.Popen(['nonexisting_i_hope'],
++                                 stdin=subprocess.PIPE,
++                                 stdout=subprocess.PIPE,
++                                 stderr=subprocess.PIPE)
++        finally:
++            t.join()
++            exc = None
++            for fd in fds:
++                # If a double close occurred, some of those fds will
++                # already have been closed by mistake, and os.close()
++                # here will raise.
++                try:
++                    os.close(fd)
++                except OSError as e:
++                    exc = e
++            if exc is not None:
++                raise exc
++
+     def test_handles_closed_on_exception(self):
+         # If CreateProcess exits with an error, ensure the
+         # duplicate output handles are released
+@@ -783,7 +817,7 @@ class POSIXProcessTestCase(BaseTestCase)
+ 
+         def _execute_child(
+                 self, args, executable, preexec_fn, close_fds, cwd, env,
+-                universal_newlines, startupinfo, creationflags, shell,
++                universal_newlines, startupinfo, creationflags, shell, to_close,
+                 p2cread, p2cwrite,
+                 c2pread, c2pwrite,
+                 errread, errwrite):
+@@ -791,7 +825,7 @@ class POSIXProcessTestCase(BaseTestCase)
+                 subprocess.Popen._execute_child(
+                         self, args, executable, preexec_fn, close_fds,
+                         cwd, env, universal_newlines,
+-                        startupinfo, creationflags, shell,
++                        startupinfo, creationflags, shell, to_close,
+                         p2cread, p2cwrite,
+                         c2pread, c2pwrite,
+                         errread, errwrite)
diff --git a/python.spec b/python.spec
index b43ae73..f3539b4 100644
--- a/python.spec
+++ b/python.spec
@@ -106,7 +106,7 @@ Summary: An interpreted, interactive, object-oriented programming language
 Name: %{python}
 # Remember to also rebase python-docs when changing this:
 Version: 2.7.5
-Release: 11%{?dist}
+Release: 12%{?dist}
 License: Python
 Group: Development/Languages
 Requires: %{python}-libs%{?_isa} = %{version}-%{release}
@@ -873,6 +873,19 @@ Patch192: 00192-buffer-overflow.patch
 # Patch provided by John C. Peterson
 Patch193: 00193-enable-loading-sqlite-extensions.patch
 
+# 00194 #
+#
+# Fix tests with SQLite >= 3.8.4
+# http://bugs.python.org/issue20901
+# http://hg.python.org/cpython/raw-rev/1763e27a182d
+Patch194: 00194-fix-tests-with-sqlite-3.8.4.patch
+
+# 00195 #
+#
+# Fix double close of subprocess pipes when child process starts failing
+# http://bugs.python.org/issue18851
+Patch195: 00195-avoid-double-close-of-pipes-on-child-process-fail.patch
+
 
 # (New patches go here ^^^)
 #
@@ -1227,6 +1240,8 @@ mv Modules/cryptmodule.c Modules/_cryptmodule.c
 %patch190 -p1
 %patch192 -p1
 %patch193 -p1
+%patch194 -p1
+%patch195 -p1
 
 
 # This shouldn't be necesarry, but is right now (2.2a3)
@@ -2056,6 +2071,11 @@ rm -fr %{buildroot}
 # ======================================================
 
 %changelog
+* Thu Jun 19 2014 Bohuslav Kabrda <bkabrda at redhat.com> - 2.7.5-12
+- Fix test failures with SQLite 3.8.4
+- Fix double close of subprocess pipes when child process fails
+Resolves: rhbz#1103450
+
 * Wed Feb 19 2014 Bohuslav Kabrda <bkabrda at redhat.com> - 2.7.5-11
 - Enable loading sqlite extensions.
 Resolves: rhbz#1066708


More information about the scm-commits mailing list