[Python-modules-commits] [python-subprocess32] 01/08: Import python-subprocess32_3.2.7.orig.tar.gz

Ondřej Nový onovy-guest at moszumanska.debian.org
Sat Jun 4 12:46:53 UTC 2016


This is an automated email from the git hooks/post-receive script.

onovy-guest pushed a commit to branch master
in repository python-subprocess32.

commit 244bdf570970dd319a91a5e6d1eaa95b9cfd7a34
Author: Ondřej Nový <novy at ondrej.org>
Date:   Sat Jun 4 14:16:28 2016 +0200

    Import python-subprocess32_3.2.7.orig.tar.gz
---
 ChangeLog             | 29 ++++++++++++++++
 MANIFEST.in           |  2 +-
 PKG-INFO              | 29 ++++++++++++----
 README.md             | 45 ++++++++++++++++++++++++
 README.txt            | 16 ---------
 _posixsubprocess.c    | 94 ++++++++++++++++++++++++++-------------------------
 setup.py              | 37 ++++++++++++++++----
 subprocess32.py       | 21 ++++++++----
 test_subprocess32.py  | 90 ++++++++++++++++++++++++++++++++++++++++++++++++
 testdata/fd_status.py | 46 +++++++++++++++----------
 10 files changed, 308 insertions(+), 101 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index ff6af02..6ad9d8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,33 @@
 -----------------
+2015-11-15 3.2.7
+-----------------
+
+* Issue #6973: When we know a subprocess.Popen process has died, do
+  not allow the send_signal(), terminate(), or kill() methods to do
+  anything as they could potentially signal a different process.
+* Issue #23564: Fixed a partially broken sanity check in the _posixsubprocess
+  internals regarding how fds_to_pass were passed to the child.  The bug had
+  no actual impact as subprocess32.py already avoided it.
+
+-----------------
+2015-11-14 3.2.7rc2
+-----------------
+
+* Moved the repository from code.google.com to github.
+* Added a _WAIT_TIMEOUT to satisfy the unsupported people entirely on
+  their own trying to use this on Windows.
+* Similarly: Updated setup.py to not build the extension on non-posix.
+
+-----------------
+2014-06-01 3.2.7rc1
+-----------------
+
+* Issue #21618: The subprocess module could fail to close open fds that were
+  inherited by the calling process and already higher than POSIX resource
+  limits would otherwise allow.  On systems with a functioning /proc/self/fd
+  or /dev/fd interface the max is now ignored and all fds are closed.
+
+-----------------
 2014-04-23 3.2.6
 -----------------
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 8b7b39a..e5b5bbb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include *.txt
+include *.md
 include LICENSE*
 include setup.py
 include setup.cfg
diff --git a/PKG-INFO b/PKG-INFO
index 48cfc1d..6425b16 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,15 +1,30 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: subprocess32
-Version: 3.2.6
-Summary: Backport of the subprocess module from Python 3.2/3.3 for use on 2.x.
-Home-page: http://code.google.com/p/python-subprocess32/
+Version: 3.2.7
+Summary: A backport of the subprocess module from Python 3.2/3.3 for use on 2.x.
+Home-page: https://github.com/google/python-subprocess32
 Author: Gregory P. Smith
 Author-email: greg at krypto.org
 License: PSF license
 Description: 
         This is a backport of the subprocess standard library module from
-        Python 3.2 & 3.3 for use on Python 2.4, 2.5, 2.6 and 2.7.
-        It includes bugfixes and new features.  On POSIX systems it is
+        Python 3.2 & 3.3 for use on Python 2.
+        It includes bugfixes and some new features.  On POSIX systems it is
         guaranteed to be reliable when used in threaded applications.
-        Bonus: It includes timeout support from Python 3.3.
+        It includes timeout support from Python 3.3 but otherwise matches
+        3.2's API.  It has not been tested on Windows.
 Platform: UNKNOWN
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: SunOS/Solaris
+Classifier: Programming Language :: Python :: 2.4
+Classifier: Programming Language :: Python :: 2.5
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 2 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..76eb5ea
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+subprocess32
+------------
+
+This is a backport of the Python 3 subprocess module for use on Python 2.
+This code has not been tested on Windows or other non-POSIX platforms.
+
+subprocess32 includes many important reliability bug fixes relevant on
+POSIX platforms.  The most important of which is a C extension module
+used internally to handle the code path between fork() and exec().
+This module is reliable when an application is using threads.
+
+Refer to the
+[Python 3.3 subprocess documentation](https://docs.python.org/3.3/library/subprocess.html)
+for usage information.
+
+* Timeout support backported from Python 3.3 is included.
+* Otherwise features are frozen at the 3.2 level.
+
+Usage
+-----
+
+The recommend pattern for cross platform code is to use the following:
+
+```python
+if os.name == 'posix' and sys.version_info[0] < 3:
+    import subprocess32 as subprocess
+else:
+    import subprocess
+```
+
+Or if you fully control your POSIX Python 2.7 installation, just drop
+this in as a replacement for its subprocess module.  Users will thank
+you by not filing concurrency bugs.
+
+Got Bugs?
+---------
+
+Try to reproduce them on the latest Python 3.x itself and file bug
+reports on [bugs.python.org](https://bugs.python.org/).
+Add gregory.p.smith to the Nosy list.
+
+If you have reason to believe the issue is specifically with this backport
+and not a problem in Python 3 itself, use the github issue tracker.
+
+-- Gregory P. Smith  _greg at krypto.org_
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 8608e7d..0000000
--- a/README.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a backport of the Python 3.2 subprocess module for use on
-Python versions 2.4 through 2.7.
-
-It includes many important bug fixes including a C extension module used
-internally to handle the code path between fork() and exec().  This version
-is reliable when an application is using threads.
-
-Refer to the Python 3.2 documentation for usage information:
- http://docs.python.org/3.2/library/subprocess.html
-
-Timeout support backported from Python 3.3 is also included.
-
-Bugs?  Try to reproduce them on the latest Python 3.x itself and file bug
-reports on http://bugs.python.org/.  Add gregory.p.smith to the Nosy list.
-
--- Gregory P. Smith  greg at krypto.org
diff --git a/_posixsubprocess.c b/_posixsubprocess.c
index cd63054..012715e 100644
--- a/_posixsubprocess.c
+++ b/_posixsubprocess.c
@@ -61,10 +61,6 @@ static int _PyImport_ReleaseLock(void);
 #define POSIX_CALL(call)   if ((call) == -1) goto error
 
 
-/* Maximum file descriptor, initialized on module load. */
-static long max_fd;
-
-
 /* Given the gc module call gc.enable() and return 0 on success. */
 static int
 _enable_gc(PyObject *gc_module)
@@ -125,10 +121,11 @@ _sanity_check_python_fd_sequence(PyObject *fd_sequence)
     for (seq_idx = 0; seq_idx < seq_len; ++seq_idx) {
         PyObject* py_fd = PySequence_Fast_GET_ITEM(fd_sequence, seq_idx);
         long iter_fd = PyLong_AsLong(py_fd);
-        if (iter_fd < 0 || iter_fd < prev_fd || iter_fd > INT_MAX) {
+        if (iter_fd < 0 || iter_fd <= prev_fd || iter_fd > INT_MAX) {
             /* Negative, overflow, not a Long, unsorted, too big for a fd. */
             return 1;
         }
+        prev_fd = iter_fd;
     }
     return 0;
 }
@@ -158,14 +155,39 @@ _is_fd_in_sorted_fd_sequence(int fd, PyObject *fd_sequence)
 }
 
 
-/* Close all file descriptors in the range start_fd inclusive to
- * end_fd exclusive except for those in py_fds_to_keep.  If the
- * range defined by [start_fd, end_fd) is large this will take a
- * long time as it calls close() on EVERY possible fd.
+/* Get the maximum file descriptor that could be opened by this process.
+ * This function is async signal safe for use between fork() and exec().
+ */
+static long
+safe_get_max_fd(void)
+{
+    long local_max_fd;
+#if defined(__NetBSD__)
+    local_max_fd = fcntl(0, F_MAXFD);
+    if (local_max_fd >= 0)
+        return local_max_fd;
+#endif
+#ifdef _SC_OPEN_MAX
+    local_max_fd = sysconf(_SC_OPEN_MAX);
+    if (local_max_fd == -1)
+#endif
+        local_max_fd = 256;  /* Matches legacy Lib/subprocess.py behavior. */
+    return local_max_fd;
+}
+
+
+/* Close all file descriptors in the range from start_fd and higher
+ * except for those in py_fds_to_keep.  If the range defined by
+ * [start_fd, safe_get_max_fd()) is large this will take a long
+ * time as it calls close() on EVERY possible fd.
+ *
+ * It isn't possible to know for sure what the max fd to go up to
+ * is for processes with the capability of raising their maximum.
  */
 static void
-_close_fds_by_brute_force(int start_fd, int end_fd, PyObject *py_fds_to_keep)
+_close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep)
 {
+    long end_fd = safe_get_max_fd();
     Py_ssize_t num_fds_to_keep = PySequence_Length(py_fds_to_keep);
     Py_ssize_t keep_seq_idx;
     int fd_num;
@@ -205,8 +227,8 @@ struct linux_dirent64 {
    char           d_name[256];  /* Filename (null-terminated) */
 };
 
-/* Close all open file descriptors in the range start_fd inclusive to end_fd
- * exclusive. Do not close any in the sorted py_fds_to_keep list.
+/* Close all open file descriptors in the range from start_fd and higher
+ * Do not close any in the sorted py_fds_to_keep list.
  *
  * This version is async signal safe as it does not make any unsafe C library
  * calls, malloc calls or handle any locks.  It is _unfortunate_ to be forced
@@ -221,11 +243,9 @@ struct linux_dirent64 {
  * it with some cpp #define magic to work on other OSes as well if you want.
  */
 static void
-_close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep)
+_close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
 {
     int fd_dir_fd;
-    if (start_fd >= end_fd)
-        return;
 #ifdef O_CLOEXEC
     fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0);
 #else
@@ -240,7 +260,7 @@ _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep)
 #endif
     if (fd_dir_fd == -1) {
         /* No way to get a list of open fds. */
-        _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep);
+        _close_fds_by_brute_force(start_fd, py_fds_to_keep);
         return;
     } else {
         char buffer[sizeof(struct linux_dirent64)];
@@ -255,23 +275,23 @@ _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep)
                 entry = (struct linux_dirent64 *)(buffer + offset);
                 if ((fd = _pos_int_from_ascii(entry->d_name)) < 0)
                     continue;  /* Not a number. */
-                if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd &&
+                if (fd != fd_dir_fd && fd >= start_fd &&
                     !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) {
                     while (close(fd) < 0 && errno == EINTR);
                 }
             }
         }
-        close(fd_dir_fd);
+        while (close(fd_dir_fd) < 0 && errno == EINTR);
     }
 }
 
-#define _close_open_fd_range _close_open_fd_range_safe
+#define _close_open_fds _close_open_fds_safe
 
 #else  /* NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */
 
 
-/* Close all open file descriptors in the range start_fd inclusive to end_fd
- * exclusive. Do not close any in the sorted py_fds_to_keep list.
+/* Close all open file descriptors from start_fd and higher.
+ * Do not close any in the sorted py_fds_to_keep list.
  *
  * This function violates the strict use of async signal safe functions. :(
  * It calls opendir(), readdir() and closedir().  Of these, the one most
@@ -284,17 +304,13 @@ _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep)
  *   http://womble.decadent.org.uk/readdir_r-advisory.html
  */
 static void
-_close_open_fd_range_maybe_unsafe(int start_fd, int end_fd,
-                                  PyObject* py_fds_to_keep)
+_close_open_fds_maybe_unsafe(long start_fd, PyObject* py_fds_to_keep)
 {
     DIR *proc_fd_dir;
 #ifndef HAVE_DIRFD
-    while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep) &&
-           (start_fd < end_fd)) {
+    while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep)) {
         ++start_fd;
     }
-    if (start_fd >= end_fd)
-        return;
     /* Close our lowest fd before we call opendir so that it is likely to
      * reuse that fd otherwise we might close opendir's file descriptor in
      * our loop.  This trick assumes that fd's are allocated on a lowest
@@ -302,8 +318,6 @@ _close_open_fd_range_maybe_unsafe(int start_fd, int end_fd,
     while (close(start_fd) < 0 && errno == EINTR);
     ++start_fd;
 #endif
-    if (start_fd >= end_fd)
-        return;
 
 #if defined(__FreeBSD__)
     if (!_is_fdescfs_mounted_on_dev_fd())
@@ -313,7 +327,7 @@ _close_open_fd_range_maybe_unsafe(int start_fd, int end_fd,
         proc_fd_dir = opendir(FD_DIR);
     if (!proc_fd_dir) {
         /* No way to get a list of open fds. */
-        _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep);
+        _close_fds_by_brute_force(start_fd, py_fds_to_keep);
     } else {
         struct dirent *dir_entry;
 #ifdef HAVE_DIRFD
@@ -326,7 +340,7 @@ _close_open_fd_range_maybe_unsafe(int start_fd, int end_fd,
             int fd;
             if ((fd = _pos_int_from_ascii(dir_entry->d_name)) < 0)
                 continue;  /* Not a number. */
-            if (fd != fd_used_by_opendir && fd >= start_fd && fd < end_fd &&
+            if (fd != fd_used_by_opendir && fd >= start_fd &&
                 !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep)) {
                 while (close(fd) < 0 && errno == EINTR);
             }
@@ -334,13 +348,13 @@ _close_open_fd_range_maybe_unsafe(int start_fd, int end_fd,
         }
         if (errno) {
             /* readdir error, revert behavior. Highly Unlikely. */
-            _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep);
+            _close_fds_by_brute_force(start_fd, py_fds_to_keep);
         }
         closedir(proc_fd_dir);
     }
 }
 
-#define _close_open_fd_range _close_open_fd_range_maybe_unsafe
+#define _close_open_fds _close_open_fds_maybe_unsafe
 
 #endif  /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */
 
@@ -462,14 +476,8 @@ child_exec(char *const exec_array[],
     }
 
     if (close_fds) {
-        int local_max_fd = max_fd;
-#if defined(__NetBSD__)
-        local_max_fd = fcntl(0, F_MAXFD);
-        if (local_max_fd < 0)
-            local_max_fd = max_fd;
-#endif
         /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
-        _close_open_fd_range(3, local_max_fd, py_fds_to_keep);
+        _close_open_fds(3, py_fds_to_keep);
     }
 
     /* This loop matches the Lib/os.py _execvpe()'s PATH search when */
@@ -872,12 +880,6 @@ init_posixsubprocess(void)
 {
     PyObject *m;
 
-#ifdef _SC_OPEN_MAX
-    max_fd = sysconf(_SC_OPEN_MAX);
-    if (max_fd == -1)
-#endif
-        max_fd = 256;  /* Matches Lib/subprocess.py */
-
 #if (PY_VERSION_HEX < MIN_PY_VERSION_WITH_PYIMPORT_ACQUIRELOCK)
     imp_module = PyImport_ImportModule("imp");
     if (imp_module == NULL)
diff --git a/setup.py b/setup.py
index 07d9c37..da410c1 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 
+import os
 import sys
 from distutils.core import setup, Extension
 
@@ -11,25 +12,47 @@ def main():
 
     ext = Extension('_posixsubprocess', ['_posixsubprocess.c'],
                     depends=['_posixsubprocess_helpers.c'])
+    if os.name == 'posix':
+        ext_modules = [ext]
+    else:
+        ext_modules = []
 
     setup(
       name='subprocess32',
-      version='3.2.6',
-      description='Backport of the subprocess module from Python 3.2/3.3 for use on 2.x.',
+      version='3.2.7',
+      description='A backport of the subprocess module from Python 3.2/3.3 for use on 2.x.',
       long_description="""
 This is a backport of the subprocess standard library module from
-Python 3.2 & 3.3 for use on Python 2.4, 2.5, 2.6 and 2.7.
-It includes bugfixes and new features.  On POSIX systems it is
+Python 3.2 & 3.3 for use on Python 2.
+It includes bugfixes and some new features.  On POSIX systems it is
 guaranteed to be reliable when used in threaded applications.
-Bonus: It includes timeout support from Python 3.3.""",
+It includes timeout support from Python 3.3 but otherwise matches
+3.2's API.  It has not been tested on Windows.""",
       license='PSF license',
 
       maintainer='Gregory P. Smith',
       maintainer_email='greg at krypto.org',
-      url='http://code.google.com/p/python-subprocess32/',
+      url='https://github.com/google/python-subprocess32',
 
-      ext_modules=[ext],
+      ext_modules=ext_modules,
       py_modules=['subprocess32'],
+
+      classifiers=[
+          'Intended Audience :: Developers',
+          'Topic :: Software Development :: Libraries',
+          'Development Status :: 5 - Production/Stable',
+          'License :: OSI Approved :: Python Software Foundation License',
+          'Operating System :: POSIX',
+          'Operating System :: POSIX :: BSD',
+          'Operating System :: POSIX :: Linux',
+          'Operating System :: POSIX :: SunOS/Solaris',
+          'Programming Language :: Python :: 2.4',
+          'Programming Language :: Python :: 2.5',
+          'Programming Language :: Python :: 2.6',
+          'Programming Language :: Python :: 2.7',
+          'Programming Language :: Python :: 2 :: Only',
+          'Programming Language :: Python :: Implementation :: CPython',
+      ],
     )
 
 
diff --git a/subprocess32.py b/subprocess32.py
index 567faf6..f1522c0 100644
--- a/subprocess32.py
+++ b/subprocess32.py
@@ -506,6 +506,9 @@ if mswindows:
                              STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
                              STD_ERROR_HANDLE, SW_HIDE,
                              STARTF_USESTDHANDLES, STARTF_USESHOWWINDOW)
+    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
+    # Note: In Python 3.3 this constant is found in the _winapi module.
+    _WAIT_TIMEOUT = 0x102
 
     __all__.extend(["CREATE_NEW_CONSOLE", "CREATE_NEW_PROCESS_GROUP",
                     "STD_INPUT_HANDLE", "STD_OUTPUT_HANDLE",
@@ -1144,7 +1147,7 @@ class Popen(object):
                 timeout = int(timeout * 1000)
             if self.returncode is None:
                 result = _subprocess.WaitForSingleObject(self._handle, timeout)
-                if result == _subprocess.WAIT_TIMEOUT:
+                if result == _WAIT_TIMEOUT:
                     raise TimeoutExpired(self.args, timeout)
                 self.returncode = _subprocess.GetExitCodeProcess(self._handle)
             return self.returncode
@@ -1220,8 +1223,10 @@ class Popen(object):
             return (stdout, stderr)
 
         def send_signal(self, sig):
-            """Send a signal to the process
-            """
+            """Send a signal to the process."""
+            # Don't signal a process that we know has already died.
+            if self.returncode is not None:
+                return
             if sig == signal.SIGTERM:
                 self.terminate()
             elif sig == signal.CTRL_C_EVENT:
@@ -1232,8 +1237,10 @@ class Popen(object):
                 raise ValueError("Unsupported signal: %s" % sig)
 
         def terminate(self):
-            """Terminates the process
-            """
+            """Terminates the process."""
+            # Don't terminate a process that we know has already died.
+            if self.returncode is not None:
+                return
             _subprocess.TerminateProcess(self._handle, 1)
 
         kill = terminate
@@ -1878,7 +1885,9 @@ class Popen(object):
         def send_signal(self, sig):
             """Send a signal to the process
             """
-            os.kill(self.pid, sig)
+            # Skip signalling a process that we know has already died.
+            if self.returncode is None:
+                os.kill(self.pid, sig)
 
         def terminate(self):
             """Terminate the process with SIGTERM
diff --git a/test_subprocess32.py b/test_subprocess32.py
index 818f1cb..c312949 100644
--- a/test_subprocess32.py
+++ b/test_subprocess32.py
@@ -7,6 +7,7 @@ import signal
 import os
 import errno
 import tempfile
+import textwrap
 import time
 try:
     import threading
@@ -1627,6 +1628,60 @@ class POSIXProcessTestCase(BaseTestCase):
                          "Some fds not in pass_fds were left open")
         self.assertIn(1, remaining_fds, "Subprocess failed")
 
+
+    def test_close_fds_when_max_fd_is_lowered(self):
+        """Confirm that issue21618 is fixed (may fail under valgrind)."""
+        fd_status = test_support.findfile("testdata/fd_status.py")
+
+        open_fds = set()
+        # Add a bunch more fds to pass down.
+        for _ in range(40):
+            fd = os.open("/dev/null", os.O_RDONLY)
+            open_fds.add(fd)
+
+        # Leave a two pairs of low ones available for use by the
+        # internal child error pipe and the stdout pipe.
+        # We also leave 10 more open for use by the Python 2 startup
+        # import machinery which tends to consume several at once.
+        for fd in sorted(open_fds)[:14]:
+            os.close(fd)
+            open_fds.remove(fd)
+
+        for fd in open_fds:
+            self.addCleanup(os.close, fd)
+
+        max_fd_open = max(open_fds)
+
+        import resource
+        rlim_cur, rlim_max = resource.getrlimit(resource.RLIMIT_NOFILE)
+        try:
+            # 29 is lower than the highest fds we are leaving open.
+            resource.setrlimit(resource.RLIMIT_NOFILE, (29, rlim_max))
+            # Launch a new Python interpreter with our low fd rlim_cur that
+            # inherits open fds above that limit.  It then uses subprocess
+            # with close_fds=True to get a report of open fds in the child.
+            # An explicit list of fds to check is passed to fd_status.py as
+            # letting fd_status rely on its default logic would miss the
+            # fds above rlim_cur as it normally only checks up to that limit.
+            p = subprocess.Popen(
+                [sys.executable, '-c',
+                 textwrap.dedent("""
+                     import subprocess32, sys
+                     subprocess32.Popen([sys.executable, %(fd_status)r] +
+                                        [str(x) for x in range(%(max_fd)d)],
+                                        close_fds=True).wait()
+                     """ % dict(fd_status=fd_status, max_fd=max_fd_open+1))],
+                stdout=subprocess.PIPE, close_fds=False)
+        finally:
+            resource.setrlimit(resource.RLIMIT_NOFILE, (rlim_cur, rlim_max))
+
+        output, unused_stderr = p.communicate()
+        remaining_fds = set(map(int, output.strip().split(',')))
+
+        self.assertFalse(remaining_fds & open_fds,
+                         msg="Some fds were left open.")
+
+
     def test_pass_fds(self):
         fd_status = test_support.findfile("testdata/fd_status.py")
 
@@ -1925,9 +1980,43 @@ class ProcessTestCasePOSIXPurePython(ProcessTestCase, POSIXProcessTestCase):
         ProcessTestCase.tearDown(self)
 
 
+class POSIXSubprocessModuleTestCase(unittest.TestCase):
+    def test_fork_exec_sorted_fd_sanity_check(self):
+        # Issue #23564: sanity check the fork_exec() fds_to_keep sanity check.
+        _posixsubprocess = subprocess._posixsubprocess
+        gc_enabled = gc.isenabled()
+        try:
+            gc.enable()
+
+            for fds_to_keep in (
+                (-1, 2, 3, 4, 5),  # Negative number.
+                ('str', 4),  # Not an int.
+                (18, 23, 42, 2**63),  # Out of range.
+                (5, 4),  # Not sorted.
+                (6, 7, 7, 8),  # Duplicate.
+            ):
+                try:
+                    _posixsubprocess.fork_exec(
+                        ["false"], ["false"],
+                        True, fds_to_keep, None, ["env"],
+                        -1, -1, -1, -1,
+                        1, 2, 3, 4,
+                        True, True, None)
+                except ValueError, exception:
+                    self.assertTrue('fds_to_keep' in str(exception),
+                                    msg=str(exception))
+                else:
+                    self.fail("ValueError not raised, fds_to_keep=%s" %
+                              (fds_to_keep,))
+        finally:
+            if not gc_enabled:
+                gc.disable()
+
+
 if not getattr(subprocess, '_posixsubprocess', False):
     print >>sys.stderr, "_posixsubprocess extension module not found."
     class ProcessTestCasePOSIXPurePython(unittest.TestCase): pass
+    class POSIXSubprocessModuleTestCase(unittest.TestCase): pass
 
 
 class HelperFunctionTests(unittest.TestCase):
@@ -2059,6 +2148,7 @@ if sys.version_info[:2] <= (2,4):
 def test_main():
     unit_tests = (ProcessTestCase,
                   POSIXProcessTestCase,
+                  POSIXSubprocessModuleTestCase,
                   Win32ProcessTestCase,
                   ProcessTestCasePOSIXPurePython,
                   ProcessTestCaseNoPoll,
diff --git a/testdata/fd_status.py b/testdata/fd_status.py
index 1a0b7ed..67fb41c 100644
--- a/testdata/fd_status.py
+++ b/testdata/fd_status.py
@@ -1,24 +1,34 @@
 """When called as a script, print a comma-separated list of the open
-file descriptors on stdout."""
+file descriptors on stdout.
+
+Usage:
+fd_stats.py: check all file descriptors
+fd_status.py fd1 fd2 ...: check only specified file descriptors
+"""
 
 import errno
 import os
-import fcntl
-
-try:
-    _MAXFD = os.sysconf("SC_OPEN_MAX")
-except:
-    _MAXFD = 256
-
-def isopen(fd):
-    """Return True if the fd is open, and False otherwise"""
-    try:
-        fcntl.fcntl(fd, fcntl.F_GETFD, 0)
-    except IOError, e:
-        if e.errno == errno.EBADF:
-            return False
-        raise
-    return True
+import stat
+import sys
 
 if __name__ == "__main__":
-    print(','.join(str(fd) for fd in range(0, _MAXFD) if isopen(fd)))
+    fds = []
+    if len(sys.argv) == 1:
+        try:
+            _MAXFD = os.sysconf("SC_OPEN_MAX")
+        except:
+            _MAXFD = 256
+        test_fds = range(0, _MAXFD)
+    else:
+        test_fds = map(int, sys.argv[1:])
+    for fd in test_fds:
+        try:
+            st = os.fstat(fd)
+        except OSError, e:
+            if e.errno == errno.EBADF:
+                continue
+            raise
+        # Ignore Solaris door files
+        if not hasattr(stat, 'S_ISDOOR') or not stat.S_ISDOOR(st.st_mode):
+            fds.append(fd)
+    print ','.join(map(str, fds))

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-subprocess32.git



More information about the Python-modules-commits mailing list