mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #6559: fix the subprocess.Popen pass_fds implementation. Add a unittest.
Issue #7213: Change the close_fds default on Windows to better match the new default on POSIX. True when possible (False if stdin/stdout/stderr are supplied). Update the documentation to reflect all of the above.
This commit is contained in:
parent
39f34aa1f3
commit
8edd99d085
5 changed files with 79 additions and 27 deletions
|
@ -28,7 +28,7 @@ Using the subprocess Module
|
||||||
This module defines one class called :class:`Popen`:
|
This module defines one class called :class:`Popen`:
|
||||||
|
|
||||||
|
|
||||||
.. class:: Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=_PLATFORM_DEFAULT, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False)
|
.. class:: Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=())
|
||||||
|
|
||||||
Arguments are:
|
Arguments are:
|
||||||
|
|
||||||
|
@ -153,14 +153,22 @@ This module defines one class called :class:`Popen`:
|
||||||
|
|
||||||
If *close_fds* is true, all file descriptors except :const:`0`, :const:`1` and
|
If *close_fds* is true, all file descriptors except :const:`0`, :const:`1` and
|
||||||
:const:`2` will be closed before the child process is executed. (Unix only).
|
:const:`2` will be closed before the child process is executed. (Unix only).
|
||||||
The default varies by platform: :const:`False` on Windows and :const:`True`
|
The default varies by platform: Always true on Unix. On Windows it is
|
||||||
on POSIX and other platforms.
|
true when *stdin*/*stdout*/*stderr* are :const:`None`, false otherwise.
|
||||||
On Windows, if *close_fds* is true then no handles will be inherited by the
|
On Windows, if *close_fds* is true then no handles will be inherited by the
|
||||||
child process. Note that on Windows, you cannot set *close_fds* to true and
|
child process. Note that on Windows, you cannot set *close_fds* to true and
|
||||||
also redirect the standard handles by setting *stdin*, *stdout* or *stderr*.
|
also redirect the standard handles by setting *stdin*, *stdout* or *stderr*.
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
The default was changed to True on non Windows platforms.
|
The default for *close_fds* was changed from :const:`False` to
|
||||||
|
what is described above.
|
||||||
|
|
||||||
|
*pass_fds* is an optional sequence of file descriptors to keep open
|
||||||
|
between the parent and child. Providing any *pass_fds* forces
|
||||||
|
*close_fds* to be :const:`True`. (Unix only)
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
The *pass_fds* parameter was added.
|
||||||
|
|
||||||
If *cwd* is not ``None``, the child's current directory will be changed to *cwd*
|
If *cwd* is not ``None``, the child's current directory will be changed to *cwd*
|
||||||
before it is executed. Note that this directory is not considered when
|
before it is executed. Note that this directory is not considered when
|
||||||
|
|
|
@ -27,10 +27,10 @@ This module defines one class called Popen:
|
||||||
|
|
||||||
class Popen(args, bufsize=0, executable=None,
|
class Popen(args, bufsize=0, executable=None,
|
||||||
stdin=None, stdout=None, stderr=None,
|
stdin=None, stdout=None, stderr=None,
|
||||||
preexec_fn=None, close_fds=_PLATFORM_DEFAULT, shell=False,
|
preexec_fn=None, close_fds=True, shell=False,
|
||||||
cwd=None, env=None, universal_newlines=False,
|
cwd=None, env=None, universal_newlines=False,
|
||||||
startupinfo=None, creationflags=0,
|
startupinfo=None, creationflags=0,
|
||||||
restore_signals=True, start_new_session=False):
|
restore_signals=True, start_new_session=False, pass_fds=()):
|
||||||
|
|
||||||
|
|
||||||
Arguments are:
|
Arguments are:
|
||||||
|
@ -81,8 +81,11 @@ is executed.
|
||||||
|
|
||||||
If close_fds is true, all file descriptors except 0, 1 and 2 will be
|
If close_fds is true, all file descriptors except 0, 1 and 2 will be
|
||||||
closed before the child process is executed. The default for close_fds
|
closed before the child process is executed. The default for close_fds
|
||||||
varies by platform: False on Windows and True on all other platforms
|
varies by platform: Always true on POSIX. True when stdin/stdout/stderr
|
||||||
such as POSIX.
|
are None on Windows, false otherwise.
|
||||||
|
|
||||||
|
pass_fds is an optional sequence of file descriptors to keep open between the
|
||||||
|
parent and child. Providing any pass_fds implicitly sets close_fds to true.
|
||||||
|
|
||||||
if shell is true, the specified command will be executed through the
|
if shell is true, the specified command will be executed through the
|
||||||
shell.
|
shell.
|
||||||
|
@ -621,17 +624,14 @@ def getoutput(cmd):
|
||||||
return getstatusoutput(cmd)[1]
|
return getstatusoutput(cmd)[1]
|
||||||
|
|
||||||
|
|
||||||
if mswindows:
|
_PLATFORM_DEFAULT_CLOSE_FDS = object()
|
||||||
_PLATFORM_DEFAULT = False
|
|
||||||
else:
|
|
||||||
_PLATFORM_DEFAULT = True
|
|
||||||
|
|
||||||
|
|
||||||
class Popen(object):
|
class Popen(object):
|
||||||
def __init__(self, args, bufsize=0, executable=None,
|
def __init__(self, args, bufsize=0, executable=None,
|
||||||
stdin=None, stdout=None, stderr=None,
|
stdin=None, stdout=None, stderr=None,
|
||||||
preexec_fn=None, close_fds=_PLATFORM_DEFAULT, shell=False,
|
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
|
||||||
cwd=None, env=None, universal_newlines=False,
|
shell=False, cwd=None, env=None, universal_newlines=False,
|
||||||
startupinfo=None, creationflags=0,
|
startupinfo=None, creationflags=0,
|
||||||
restore_signals=True, start_new_session=False,
|
restore_signals=True, start_new_session=False,
|
||||||
pass_fds=()):
|
pass_fds=()):
|
||||||
|
@ -648,12 +648,24 @@ class Popen(object):
|
||||||
if preexec_fn is not None:
|
if preexec_fn is not None:
|
||||||
raise ValueError("preexec_fn is not supported on Windows "
|
raise ValueError("preexec_fn is not supported on Windows "
|
||||||
"platforms")
|
"platforms")
|
||||||
if close_fds and (stdin is not None or stdout is not None or
|
any_stdio_set = (stdin is not None or stdout is not None or
|
||||||
stderr is not None):
|
stderr is not None)
|
||||||
raise ValueError("close_fds is not supported on Windows "
|
if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS:
|
||||||
"platforms if you redirect stdin/stdout/stderr")
|
if any_stdio_set:
|
||||||
|
close_fds = False
|
||||||
|
else:
|
||||||
|
close_fds = True
|
||||||
|
elif close_fds and any_stdio_set:
|
||||||
|
raise ValueError(
|
||||||
|
"close_fds is not supported on Windows platforms"
|
||||||
|
" if you redirect stdin/stdout/stderr")
|
||||||
else:
|
else:
|
||||||
# POSIX
|
# POSIX
|
||||||
|
if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS:
|
||||||
|
close_fds = True
|
||||||
|
if pass_fds and not close_fds:
|
||||||
|
warnings.warn("pass_fds overriding close_fds.", RuntimeWarning)
|
||||||
|
close_fds = True
|
||||||
if startupinfo is not None:
|
if startupinfo is not None:
|
||||||
raise ValueError("startupinfo is only supported on Windows "
|
raise ValueError("startupinfo is only supported on Windows "
|
||||||
"platforms")
|
"platforms")
|
||||||
|
@ -661,9 +673,6 @@ class Popen(object):
|
||||||
raise ValueError("creationflags is only supported on Windows "
|
raise ValueError("creationflags is only supported on Windows "
|
||||||
"platforms")
|
"platforms")
|
||||||
|
|
||||||
if pass_fds and not close_fds:
|
|
||||||
raise ValueError("pass_fds requires close_fds=True.")
|
|
||||||
|
|
||||||
self.stdin = None
|
self.stdin = None
|
||||||
self.stdout = None
|
self.stdout = None
|
||||||
self.stderr = None
|
self.stderr = None
|
||||||
|
@ -876,7 +885,7 @@ class Popen(object):
|
||||||
unused_restore_signals, unused_start_new_session):
|
unused_restore_signals, unused_start_new_session):
|
||||||
"""Execute program (MS Windows version)"""
|
"""Execute program (MS Windows version)"""
|
||||||
|
|
||||||
assert not pass_fds, "pass_fds not yet supported on Windows"
|
assert not pass_fds, "pass_fds not supported on Windows."
|
||||||
|
|
||||||
if not isinstance(args, str):
|
if not isinstance(args, str):
|
||||||
args = list2cmdline(args)
|
args = list2cmdline(args)
|
||||||
|
@ -1091,7 +1100,7 @@ class Popen(object):
|
||||||
# precondition: fds_to_keep must be sorted and unique
|
# precondition: fds_to_keep must be sorted and unique
|
||||||
start_fd = 3
|
start_fd = 3
|
||||||
for fd in fds_to_keep:
|
for fd in fds_to_keep:
|
||||||
if fd > start_fd:
|
if fd >= start_fd:
|
||||||
os.closerange(start_fd, fd)
|
os.closerange(start_fd, fd)
|
||||||
start_fd = fd + 1
|
start_fd = fd + 1
|
||||||
if start_fd <= MAXFD:
|
if start_fd <= MAXFD:
|
||||||
|
|
|
@ -1042,6 +1042,37 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
"Some fds were left open")
|
"Some fds were left open")
|
||||||
self.assertIn(1, remaining_fds, "Subprocess failed")
|
self.assertIn(1, remaining_fds, "Subprocess failed")
|
||||||
|
|
||||||
|
def test_pass_fds(self):
|
||||||
|
fd_status = support.findfile("fd_status.py", subdir="subprocessdata")
|
||||||
|
|
||||||
|
open_fds = set()
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
fds = os.pipe()
|
||||||
|
self.addCleanup(os.close, fds[0])
|
||||||
|
self.addCleanup(os.close, fds[1])
|
||||||
|
open_fds.update(fds)
|
||||||
|
|
||||||
|
for fd in open_fds:
|
||||||
|
p = subprocess.Popen([sys.executable, fd_status],
|
||||||
|
stdout=subprocess.PIPE, close_fds=True,
|
||||||
|
pass_fds=(fd, ))
|
||||||
|
output, ignored = p.communicate()
|
||||||
|
|
||||||
|
remaining_fds = set(map(int, output.split(b',')))
|
||||||
|
to_be_closed = open_fds - {fd}
|
||||||
|
|
||||||
|
self.assertIn(fd, remaining_fds, "fd to be passed not passed")
|
||||||
|
self.assertFalse(remaining_fds & to_be_closed,
|
||||||
|
"fd to be closed passed")
|
||||||
|
|
||||||
|
# pass_fds overrides close_fds with a warning.
|
||||||
|
with self.assertWarns(RuntimeWarning) as context:
|
||||||
|
self.assertFalse(subprocess.call(
|
||||||
|
[sys.executable, "-c", "import sys; sys.exit(0)"],
|
||||||
|
close_fds=False, pass_fds=(fd, )))
|
||||||
|
self.assertIn('overriding close_fds', str(context.warning))
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(mswindows, "Windows specific tests")
|
@unittest.skipUnless(mswindows, "Windows specific tests")
|
||||||
class Win32ProcessTestCase(BaseTestCase):
|
class Win32ProcessTestCase(BaseTestCase):
|
||||||
|
|
|
@ -23,8 +23,12 @@ Library
|
||||||
- Issue #10107: Warn about unsaved files in IDLE on OSX.
|
- Issue #10107: Warn about unsaved files in IDLE on OSX.
|
||||||
|
|
||||||
- Issue #7213: subprocess.Popen's default for close_fds has been changed.
|
- Issue #7213: subprocess.Popen's default for close_fds has been changed.
|
||||||
It is now platform specific, keeping its default of False on Windows and
|
It is now True in most cases other than on Windows when input, output or
|
||||||
changing the default to True on POSIX and other platforms.
|
error handles are provided.
|
||||||
|
|
||||||
|
- Issue #6559: subprocess.Popen has a new pass_fds parameter (actually
|
||||||
|
added in 3.2beta1) to allow specifying a specific list of file descriptors
|
||||||
|
to keep open in the child process.
|
||||||
|
|
||||||
|
|
||||||
What's New in Python 3.2 Beta 1?
|
What's New in Python 3.2 Beta 1?
|
||||||
|
|
|
@ -107,7 +107,7 @@ static void child_exec(char *const exec_array[],
|
||||||
errno = 0; /* We don't want to report an OSError. */
|
errno = 0; /* We don't want to report an OSError. */
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (keep_fd <= start_fd)
|
if (keep_fd < start_fd)
|
||||||
continue;
|
continue;
|
||||||
for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) {
|
for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) {
|
||||||
close(fd_num);
|
close(fd_num);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue