summaryrefslogtreecommitdiffstats
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py368
1 files changed, 130 insertions, 238 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 78e4fcf..e79e5fd 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -145,11 +145,13 @@ check_call(*popenargs, **kwargs):
getstatusoutput(cmd):
Return (status, output) of executing cmd in a shell.
- Execute the string 'cmd' in a shell with os.popen() and return a 2-tuple
- (status, output). cmd is actually run as '{ cmd ; } 2>&1', so that the
- returned output will contain output or error messages. A trailing newline
- is stripped from the output. The exit status for the command can be
- interpreted according to the rules for the C function wait(). Example:
+ Execute the string 'cmd' in a shell with 'check_output' and
+ return a 2-tuple (status, output). Universal newlines mode is used,
+ meaning that the result with be decoded to a string.
+
+ A trailing newline is stripped from the output.
+ The exit status for the command can be interpreted
+ according to the rules for the function 'wait'. Example:
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')
@@ -178,6 +180,9 @@ check_output(*popenargs, **kwargs):
>>> output = subprocess.check_output(["ls", "-l", "/dev/null"])
+ There is an additional optional argument, "input", allowing you to
+ pass a string to the subprocess's stdin. If you use this argument
+ you may not also use the Popen constructor's "stdin" argument.
Exceptions
----------
@@ -398,19 +403,24 @@ if mswindows:
hStdOutput = None
hStdError = None
wShowWindow = 0
- class pywintypes:
- error = IOError
else:
- import select
- _has_poll = hasattr(select, 'poll')
import _posixsubprocess
- _create_pipe = _posixsubprocess.cloexec_pipe
+ import select
+ import selectors
# When select or poll has indicated that the file is writable,
# we can write up to _PIPE_BUF bytes without risk of blocking.
# POSIX defines PIPE_BUF as >= 512.
_PIPE_BUF = getattr(select, 'PIPE_BUF', 512)
+ # poll/select have the advantage of not requiring any extra file
+ # descriptor, contrarily to epoll/kqueue (also, they require a single
+ # syscall).
+ if hasattr(selectors, 'PollSelector'):
+ _PopenSelector = selectors.PollSelector
+ else:
+ _PopenSelector = selectors.SelectSelector
+
__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput",
"getoutput", "check_output", "CalledProcessError", "DEVNULL"]
@@ -506,6 +516,8 @@ def _args_from_interpreter_flags():
for flag, opt in flag_opt_map.items():
v = getattr(sys.flags, flag)
if v > 0:
+ if flag == 'hash_randomization':
+ v = 1 # Handle specification of an exact seed
args.append('-' + opt * v)
for opt in sys.warnoptions:
args.append('-W' + opt)
@@ -568,14 +580,31 @@ def check_output(*popenargs, timeout=None, **kwargs):
... stderr=STDOUT)
b'ls: non_existent_file: No such file or directory\n'
+ There is an additional optional argument, "input", allowing you to
+ pass a string to the subprocess's stdin. If you use this argument
+ you may not also use the Popen constructor's "stdin" argument, as
+ it too will be used internally. Example:
+
+ >>> check_output(["sed", "-e", "s/foo/bar/"],
+ ... input=b"when in the course of fooman events\n")
+ b'when in the course of barman events\n'
+
If universal_newlines=True is passed, the return value will be a
string rather than bytes.
"""
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
+ if 'input' in kwargs:
+ if 'stdin' in kwargs:
+ raise ValueError('stdin and input arguments may not both be used.')
+ inputdata = kwargs['input']
+ del kwargs['input']
+ kwargs['stdin'] = PIPE
+ else:
+ inputdata = None
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
try:
- output, unused_err = process.communicate(timeout=timeout)
+ output, unused_err = process.communicate(inputdata, timeout=timeout)
except TimeoutExpired:
process.kill()
output, unused_err = process.communicate()
@@ -664,13 +693,15 @@ def list2cmdline(seq):
#
def getstatusoutput(cmd):
- """Return (status, output) of executing cmd in a shell.
+ """ Return (status, output) of executing cmd in a shell.
+
+ Execute the string 'cmd' in a shell with 'check_output' and
+ return a 2-tuple (status, output). Universal newlines mode is used,
+ meaning that the result with be decoded to a string.
- Execute the string 'cmd' in a shell with os.popen() and return a 2-tuple
- (status, output). cmd is actually run as '{ cmd ; } 2>&1', so that the
- returned output will contain output or error messages. A trailing newline
- is stripped from the output. The exit status for the command can be
- interpreted according to the rules for the C function wait(). Example:
+ A trailing newline is stripped from the output.
+ The exit status for the command can be interpreted
+ according to the rules for the function 'wait'. Example:
>>> import subprocess
>>> subprocess.getstatusoutput('ls /bin/ls')
@@ -820,7 +851,7 @@ class Popen(object):
for f in filter(None, (self.stdin, self.stdout, self.stderr)):
try:
f.close()
- except EnvironmentError:
+ except OSError:
pass # Ignore EBADF or other errors.
if not self._closed_child_pipe_fds:
@@ -836,7 +867,7 @@ class Popen(object):
for fd in to_close:
try:
os.close(fd)
- except EnvironmentError:
+ except OSError:
pass
raise
@@ -900,7 +931,7 @@ class Popen(object):
if input:
try:
self.stdin.write(input)
- except IOError as e:
+ except OSError as e:
if e.errno != errno.EPIPE and e.errno != errno.EINVAL:
raise
self.stdin.close()
@@ -1032,23 +1063,6 @@ class Popen(object):
return Handle(h)
- def _find_w9xpopen(self):
- """Find and return absolut path to w9xpopen.exe"""
- w9xpopen = os.path.join(
- os.path.dirname(_winapi.GetModuleFileName(0)),
- "w9xpopen.exe")
- if not os.path.exists(w9xpopen):
- # Eeek - file-not-found - possibly an embedding
- # situation - see if we can locate it in sys.exec_prefix
- w9xpopen = os.path.join(os.path.dirname(sys.base_exec_prefix),
- "w9xpopen.exe")
- if not os.path.exists(w9xpopen):
- raise RuntimeError("Cannot locate w9xpopen.exe, which is "
- "needed for Popen to work with your "
- "shell or platform.")
- return w9xpopen
-
-
def _execute_child(self, args, executable, preexec_fn, close_fds,
pass_fds, cwd, env,
startupinfo, creationflags, shell,
@@ -1077,21 +1091,6 @@ class Popen(object):
startupinfo.wShowWindow = _winapi.SW_HIDE
comspec = os.environ.get("COMSPEC", "cmd.exe")
args = '{} /c "{}"'.format (comspec, args)
- if (_winapi.GetVersion() >= 0x80000000 or
- os.path.basename(comspec).lower() == "command.com"):
- # Win9x, or using command.com on NT. We need to
- # use the w9xpopen intermediate program. For more
- # information, see KB Q150956
- # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
- w9xpopen = self._find_w9xpopen()
- args = '"%s" %s' % (w9xpopen, args)
- # Not passing CREATE_NEW_CONSOLE has been known to
- # cause random failures on win9x. Specifically a
- # dialog: "Your program accessed mem currently in
- # use at xxx" and a hopeful warning about the
- # stability of your system. Cost is Ctrl+C won't
- # kill children.
- creationflags |= _winapi.CREATE_NEW_CONSOLE
# Start the process
try:
@@ -1103,12 +1102,6 @@ class Popen(object):
env,
cwd,
startupinfo)
- except pywintypes.error as e:
- # Translate pywintypes.error to WindowsError, which is
- # a subclass of OSError. FIXME: We should really
- # translate errno using _sys_errlist (or similar), but
- # how can this be done from Python?
- raise WindowsError(*e.args)
finally:
# Child is launched. Close the parent's copy of those pipe
# handles that only the child should have open. You need
@@ -1193,7 +1186,7 @@ class Popen(object):
if input is not None:
try:
self.stdin.write(input)
- except IOError as e:
+ except OSError as e:
if e.errno != errno.EPIPE:
raise
self.stdin.close()
@@ -1271,7 +1264,7 @@ class Popen(object):
if stdin is None:
pass
elif stdin == PIPE:
- p2cread, p2cwrite = _create_pipe()
+ p2cread, p2cwrite = os.pipe()
elif stdin == DEVNULL:
p2cread = self._get_devnull()
elif isinstance(stdin, int):
@@ -1283,7 +1276,7 @@ class Popen(object):
if stdout is None:
pass
elif stdout == PIPE:
- c2pread, c2pwrite = _create_pipe()
+ c2pread, c2pwrite = os.pipe()
elif stdout == DEVNULL:
c2pwrite = self._get_devnull()
elif isinstance(stdout, int):
@@ -1295,7 +1288,7 @@ class Popen(object):
if stderr is None:
pass
elif stderr == PIPE:
- errread, errwrite = _create_pipe()
+ errread, errwrite = os.pipe()
elif stderr == STDOUT:
errwrite = c2pwrite
elif stderr == DEVNULL:
@@ -1347,7 +1340,14 @@ class Popen(object):
# For transferring possible exec failure from child to parent.
# Data format: "exception name:hex errno:description"
# Pickle is not used; it is complex and involves memory allocation.
- errpipe_read, errpipe_write = _create_pipe()
+ errpipe_read, errpipe_write = os.pipe()
+ # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
+ low_fds_to_close = []
+ while errpipe_write < 3:
+ low_fds_to_close.append(errpipe_write)
+ errpipe_write = os.dup(errpipe_write)
+ for low_fd in low_fds_to_close:
+ os.close(low_fd)
try:
try:
# We must avoid complex work that could involve
@@ -1417,13 +1417,13 @@ class Popen(object):
exception_name, hex_errno, err_msg = (
errpipe_data.split(b':', 2))
except ValueError:
- exception_name = b'RuntimeError'
+ exception_name = b'SubprocessError'
hex_errno = b'0'
err_msg = (b'Bad exception data from child: ' +
repr(errpipe_data))
child_exception_type = getattr(
builtins, exception_name.decode('ascii'),
- RuntimeError)
+ SubprocessError)
err_msg = err_msg.decode(errors="surrogatepass")
if issubclass(child_exception_type, OSError) and hex_errno:
errno_num = int(hex_errno, 16)
@@ -1453,11 +1453,11 @@ class Popen(object):
self.returncode = _WEXITSTATUS(sts)
else:
# Should never happen
- raise RuntimeError("Unknown child exit status!")
+ raise SubprocessError("Unknown child exit status!")
def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid,
- _WNOHANG=os.WNOHANG, _os_error=os.error, _ECHILD=errno.ECHILD):
+ _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD):
"""Check if child process has terminated. Returns returncode
attribute.
@@ -1470,7 +1470,7 @@ class Popen(object):
pid, sts = _waitpid(self.pid, _WNOHANG)
if pid == self.pid:
self._handle_exitstatus(sts)
- except _os_error as e:
+ except OSError as e:
if _deadstate is not None:
self.returncode = _deadstate
elif e.errno == _ECHILD:
@@ -1544,12 +1544,68 @@ class Popen(object):
if not input:
self.stdin.close()
- if _has_poll:
- stdout, stderr = self._communicate_with_poll(input, endtime,
- orig_timeout)
- else:
- stdout, stderr = self._communicate_with_select(input, endtime,
- orig_timeout)
+ stdout = None
+ stderr = None
+
+ # Only create this mapping if we haven't already.
+ if not self._communication_started:
+ self._fileobj2output = {}
+ if self.stdout:
+ self._fileobj2output[self.stdout] = []
+ if self.stderr:
+ self._fileobj2output[self.stderr] = []
+
+ if self.stdout:
+ stdout = self._fileobj2output[self.stdout]
+ if self.stderr:
+ stderr = self._fileobj2output[self.stderr]
+
+ self._save_input(input)
+
+ if self._input:
+ input_view = memoryview(self._input)
+
+ with _PopenSelector() as selector:
+ if self.stdin and input:
+ selector.register(self.stdin, selectors.EVENT_WRITE)
+ if self.stdout:
+ selector.register(self.stdout, selectors.EVENT_READ)
+ if self.stderr:
+ selector.register(self.stderr, selectors.EVENT_READ)
+
+ while selector.get_map():
+ timeout = self._remaining_time(endtime)
+ if timeout is not None and timeout < 0:
+ raise TimeoutExpired(self.args, orig_timeout)
+
+ ready = selector.select(timeout)
+ self._check_timeout(endtime, orig_timeout)
+
+ # XXX Rewrite these to use non-blocking I/O on the file
+ # objects; they are no longer using C stdio!
+
+ for key, events in ready:
+ if key.fileobj is self.stdin:
+ chunk = input_view[self._input_offset :
+ self._input_offset + _PIPE_BUF]
+ try:
+ self._input_offset += os.write(key.fd, chunk)
+ except OSError as e:
+ if e.errno == errno.EPIPE:
+ selector.unregister(key.fileobj)
+ key.fileobj.close()
+ else:
+ raise
+ else:
+ if self._input_offset >= len(self._input):
+ selector.unregister(key.fileobj)
+ key.fileobj.close()
+ elif key.fileobj in (self.stdout, self.stderr):
+ data = os.read(key.fd, 32768)
+ if not data:
+ selector.unregister(key.fileobj)
+ key.fileobj.close()
+ self._fileobj2output[key.fileobj].append(data)
self.wait(timeout=self._remaining_time(endtime))
@@ -1583,170 +1639,6 @@ class Popen(object):
self._input = self._input.encode(self.stdin.encoding)
- def _communicate_with_poll(self, input, endtime, orig_timeout):
- stdout = None # Return
- stderr = None # Return
-
- if not self._communication_started:
- self._fd2file = {}
-
- poller = select.poll()
- def register_and_append(file_obj, eventmask):
- poller.register(file_obj.fileno(), eventmask)
- self._fd2file[file_obj.fileno()] = file_obj
-
- def close_unregister_and_remove(fd):
- poller.unregister(fd)
- self._fd2file[fd].close()
- self._fd2file.pop(fd)
-
- if self.stdin and input:
- register_and_append(self.stdin, select.POLLOUT)
-
- # Only create this mapping if we haven't already.
- if not self._communication_started:
- self._fd2output = {}
- if self.stdout:
- self._fd2output[self.stdout.fileno()] = []
- if self.stderr:
- self._fd2output[self.stderr.fileno()] = []
-
- select_POLLIN_POLLPRI = select.POLLIN | select.POLLPRI
- if self.stdout:
- register_and_append(self.stdout, select_POLLIN_POLLPRI)
- stdout = self._fd2output[self.stdout.fileno()]
- if self.stderr:
- register_and_append(self.stderr, select_POLLIN_POLLPRI)
- stderr = self._fd2output[self.stderr.fileno()]
-
- self._save_input(input)
-
- if self._input:
- input_view = memoryview(self._input)
-
- while self._fd2file:
- timeout = self._remaining_time(endtime)
- if timeout is not None and timeout < 0:
- raise TimeoutExpired(self.args, orig_timeout)
- try:
- ready = poller.poll(timeout)
- except select.error as e:
- if e.args[0] == errno.EINTR:
- continue
- raise
- self._check_timeout(endtime, orig_timeout)
-
- # XXX Rewrite these to use non-blocking I/O on the
- # file objects; they are no longer using C stdio!
-
- for fd, mode in ready:
- if mode & select.POLLOUT:
- chunk = input_view[self._input_offset :
- self._input_offset + _PIPE_BUF]
- try:
- self._input_offset += os.write(fd, chunk)
- except OSError as e:
- if e.errno == errno.EPIPE:
- close_unregister_and_remove(fd)
- else:
- raise
- else:
- if self._input_offset >= len(self._input):
- close_unregister_and_remove(fd)
- elif mode & select_POLLIN_POLLPRI:
- data = os.read(fd, 32768)
- if not data:
- close_unregister_and_remove(fd)
- self._fd2output[fd].append(data)
- else:
- # Ignore hang up or errors.
- close_unregister_and_remove(fd)
-
- return (stdout, stderr)
-
-
- def _communicate_with_select(self, input, endtime, orig_timeout):
- if not self._communication_started:
- self._read_set = []
- self._write_set = []
- if self.stdin and input:
- self._write_set.append(self.stdin)
- if self.stdout:
- self._read_set.append(self.stdout)
- if self.stderr:
- self._read_set.append(self.stderr)
-
- self._save_input(input)
-
- stdout = None # Return
- stderr = None # Return
-
- if self.stdout:
- if not self._communication_started:
- self._stdout_buff = []
- stdout = self._stdout_buff
- if self.stderr:
- if not self._communication_started:
- self._stderr_buff = []
- stderr = self._stderr_buff
-
- while self._read_set or self._write_set:
- timeout = self._remaining_time(endtime)
- if timeout is not None and timeout < 0:
- raise TimeoutExpired(self.args, orig_timeout)
- try:
- (rlist, wlist, xlist) = \
- select.select(self._read_set, self._write_set, [],
- timeout)
- except select.error as e:
- if e.args[0] == errno.EINTR:
- continue
- raise
-
- # According to the docs, returning three empty lists indicates
- # that the timeout expired.
- if not (rlist or wlist or xlist):
- raise TimeoutExpired(self.args, orig_timeout)
- # We also check what time it is ourselves for good measure.
- self._check_timeout(endtime, orig_timeout)
-
- # XXX Rewrite these to use non-blocking I/O on the
- # file objects; they are no longer using C stdio!
-
- if self.stdin in wlist:
- chunk = self._input[self._input_offset :
- self._input_offset + _PIPE_BUF]
- try:
- bytes_written = os.write(self.stdin.fileno(), chunk)
- except OSError as e:
- if e.errno == errno.EPIPE:
- self.stdin.close()
- self._write_set.remove(self.stdin)
- else:
- raise
- else:
- self._input_offset += bytes_written
- if self._input_offset >= len(self._input):
- self.stdin.close()
- self._write_set.remove(self.stdin)
-
- if self.stdout in rlist:
- data = os.read(self.stdout.fileno(), 1024)
- if not data:
- self.stdout.close()
- self._read_set.remove(self.stdout)
- stdout.append(data)
-
- if self.stderr in rlist:
- data = os.read(self.stderr.fileno(), 1024)
- if not data:
- self.stderr.close()
- self._read_set.remove(self.stderr)
- stderr.append(data)
-
- return (stdout, stderr)
-
-
def send_signal(self, sig):
"""Send a signal to the process
"""