[Python-checkins] r87026 - in python/branches/py3k: Lib/subprocess.py Modules/_posixsubprocess.c

gregory.p.smith python-checkins at python.org
Sat Dec 4 12:22:11 CET 2010


Author: gregory.p.smith
Date: Sat Dec  4 12:22:11 2010
New Revision: 87026

Log:
issue6559: Adds a pass_fds parameter to subprocess.Popen that allows the caller
to list exactly which file descriptors should be kept open.


Modified:
   python/branches/py3k/Lib/subprocess.py
   python/branches/py3k/Modules/_posixsubprocess.c

Modified: python/branches/py3k/Lib/subprocess.py
==============================================================================
--- python/branches/py3k/Lib/subprocess.py	(original)
+++ python/branches/py3k/Lib/subprocess.py	Sat Dec  4 12:22:11 2010
@@ -608,7 +608,8 @@
                  preexec_fn=None, close_fds=None, shell=False,
                  cwd=None, env=None, universal_newlines=False,
                  startupinfo=None, creationflags=0,
-                 restore_signals=True, start_new_session=False):
+                 restore_signals=True, start_new_session=False,
+                 pass_fds=()):
         """Create new Popen instance."""
         _cleanup()
 
@@ -644,6 +645,9 @@
                 raise ValueError("creationflags is only supported on Windows "
                                  "platforms")
 
+        if pass_fds and not close_fds:
+            raise ValueError("pass_fds requires close_fds=True.")
+
         self.stdin = None
         self.stdout = None
         self.stderr = None
@@ -671,7 +675,7 @@
          errread, errwrite) = self._get_handles(stdin, stdout, stderr)
 
         self._execute_child(args, executable, preexec_fn, close_fds,
-                            cwd, env, universal_newlines,
+                            pass_fds, cwd, env, universal_newlines,
                             startupinfo, creationflags, shell,
                             p2cread, p2cwrite,
                             c2pread, c2pwrite,
@@ -848,7 +852,7 @@
 
 
         def _execute_child(self, args, executable, preexec_fn, close_fds,
-                           cwd, env, universal_newlines,
+                           pass_fds, cwd, env, universal_newlines,
                            startupinfo, creationflags, shell,
                            p2cread, p2cwrite,
                            c2pread, c2pwrite,
@@ -856,6 +860,8 @@
                            unused_restore_signals, unused_start_new_session):
             """Execute program (MS Windows version)"""
 
+            assert not pass_fds, "pass_fds not yet supported on Windows"
+
             if not isinstance(args, str):
                 args = list2cmdline(args)
 
@@ -1075,8 +1081,19 @@
             os.closerange(but + 1, MAXFD)
 
 
+        def _close_all_but_a_sorted_few_fds(self, fds_to_keep):
+            # precondition: fds_to_keep must be sorted and unique
+            start_fd = 3
+            for fd in fds_to_keep:
+                if fd > start_fd:
+                    os.closerange(start_fd, fd)
+                    start_fd = fd + 1
+            if start_fd <= MAXFD:
+                os.closerange(start_fd, MAXFD)
+
+
         def _execute_child(self, args, executable, preexec_fn, close_fds,
-                           cwd, env, universal_newlines,
+                           pass_fds, cwd, env, universal_newlines,
                            startupinfo, creationflags, shell,
                            p2cread, p2cwrite,
                            c2pread, c2pwrite,
@@ -1124,9 +1141,11 @@
                             executable_list = tuple(
                                 os.path.join(os.fsencode(dir), executable)
                                 for dir in os.get_exec_path(env))
+                        fds_to_keep = set(pass_fds)
+                        fds_to_keep.add(errpipe_write)
                         self.pid = _posixsubprocess.fork_exec(
                                 args, executable_list,
-                                close_fds, cwd, env_list,
+                                close_fds, sorted(fds_to_keep), cwd, env_list,
                                 p2cread, p2cwrite, c2pread, c2pwrite,
                                 errread, errwrite,
                                 errpipe_read, errpipe_write,
@@ -1180,7 +1199,14 @@
 
                                 # Close all other fds, if asked for
                                 if close_fds:
-                                    self._close_fds(but=errpipe_write)
+                                    if pass_fds:
+                                        fds_to_keep = set(pass_fds)
+                                        fds_to_keep.add(errpipe_write)
+                                        self._close_all_but_a_sorted_few_fds(
+                                                sorted(fds_to_keep))
+                                    else:
+                                        self._close_fds(but=errpipe_write)
+
 
                                 if cwd is not None:
                                     os.chdir(cwd)

Modified: python/branches/py3k/Modules/_posixsubprocess.c
==============================================================================
--- python/branches/py3k/Modules/_posixsubprocess.c	(original)
+++ python/branches/py3k/Modules/_posixsubprocess.c	Sat Dec  4 12:22:11 2010
@@ -42,7 +42,8 @@
                        int errread, int errwrite,
                        int errpipe_read, int errpipe_write,
                        int close_fds, int restore_signals,
-                       int call_setsid,
+                       int call_setsid, Py_ssize_t num_fds_to_keep,
+                       PyObject *py_fds_to_keep,
                        PyObject *preexec_fn,
                        PyObject *preexec_fn_args_tuple)
 {
@@ -91,11 +92,28 @@
     /* close() is intentionally not checked for errors here as we are closing */
     /* a large range of fds, some of which may be invalid. */
     if (close_fds) {
-        for (fd_num = 3; fd_num < errpipe_write; ++fd_num) {
-            close(fd_num);
-        }
-        for (fd_num = errpipe_write+1; fd_num < max_fd; ++fd_num) {
-            close(fd_num);
+        Py_ssize_t keep_seq_idx;
+        int start_fd = 3;
+        for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) {
+            PyObject* py_keep_fd = PySequence_Fast_GET_ITEM(py_fds_to_keep,
+                                                            keep_seq_idx);
+            int keep_fd = PyLong_AsLong(py_keep_fd);
+            if (keep_fd < 0) {  /* Negative number, overflow or not a Long. */
+                err_msg = "bad value in fds_to_keep.";
+                errno = 0;  /* We don't want to report an OSError. */
+                goto error;
+            }
+            if (keep_fd <= start_fd)
+                continue;
+            for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) {
+                close(fd_num);
+            }
+            start_fd = keep_fd + 1;
+        }
+        if (start_fd <= max_fd) {
+            for (fd_num = start_fd; fd_num < max_fd; ++fd_num) {
+                close(fd_num);
+            }
         }
     }
 
@@ -170,7 +188,7 @@
 subprocess_fork_exec(PyObject* self, PyObject *args)
 {
     PyObject *gc_module = NULL;
-    PyObject *executable_list, *py_close_fds;
+    PyObject *executable_list, *py_close_fds, *py_fds_to_keep;
     PyObject *env_list, *preexec_fn;
     PyObject *process_args, *converted_args = NULL, *fast_args = NULL;
     PyObject *preexec_fn_args_tuple = NULL;
@@ -182,11 +200,11 @@
     pid_t pid;
     int need_to_reenable_gc = 0;
     char *const *exec_array, *const *argv = NULL, *const *envp = NULL;
-    Py_ssize_t arg_num;
+    Py_ssize_t arg_num, num_fds_to_keep;
 
     if (!PyArg_ParseTuple(
-            args, "OOOOOiiiiiiiiiiO:fork_exec",
-            &process_args, &executable_list, &py_close_fds,
+            args, "OOOOOOiiiiiiiiiiO:fork_exec",
+            &process_args, &executable_list, &py_close_fds, &py_fds_to_keep,
             &cwd_obj, &env_list,
             &p2cread, &p2cwrite, &c2pread, &c2pwrite,
             &errread, &errwrite, &errpipe_read, &errpipe_write,
@@ -198,6 +216,11 @@
         PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
         return NULL;
     }
+    num_fds_to_keep = PySequence_Length(py_fds_to_keep);
+    if (num_fds_to_keep < 0) {
+        PyErr_SetString(PyExc_ValueError, "bad fds_to_keep");
+        return NULL;
+    }
 
     /* We need to call gc.disable() when we'll be calling preexec_fn */
     if (preexec_fn != Py_None) {
@@ -298,6 +321,7 @@
                    p2cread, p2cwrite, c2pread, c2pwrite,
                    errread, errwrite, errpipe_read, errpipe_write,
                    close_fds, restore_signals, call_setsid,
+                   num_fds_to_keep, py_fds_to_keep,
                    preexec_fn, preexec_fn_args_tuple);
         _exit(255);
         return NULL;  /* Dead code to avoid a potential compiler warning. */


More information about the Python-checkins mailing list