summaryrefslogtreecommitdiffstats
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py79
1 files changed, 55 insertions, 24 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 69b769b..4a8adcf 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -371,8 +371,9 @@ class TimeoutExpired(SubprocessError):
"""This exception is raised when the timeout expires while waiting for a
child process.
"""
- def __init__(self, cmd, output=None):
+ def __init__(self, cmd, timeout, output=None):
self.cmd = cmd
+ self.timeout = timeout
self.output = output
def __str__(self):
@@ -431,7 +432,7 @@ else:
return fds
__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput",
- "getoutput", "check_output", "CalledProcessError"]
+ "getoutput", "check_output", "CalledProcessError", "DEVNULL"]
if mswindows:
from _subprocess import CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP
@@ -456,6 +457,7 @@ def _cleanup():
PIPE = -1
STDOUT = -2
+DEVNULL = -3
def _eintr_retry_call(func, *args):
@@ -532,7 +534,7 @@ def check_output(*popenargs, timeout=None, **kwargs):
except TimeoutExpired:
process.kill()
output, unused_err = process.communicate()
- raise TimeoutExpired(process.args, output=output)
+ raise TimeoutExpired(process.args, timeout, output=output)
retcode = process.poll()
if retcode:
raise CalledProcessError(retcode, process.args, output=output)
@@ -800,6 +802,10 @@ class Popen(object):
# Child is still running, keep us alive until we can wait on it.
_active.append(self)
+ def _get_devnull(self):
+ if not hasattr(self, '_devnull'):
+ self._devnull = os.open(os.devnull, os.O_RDWR)
+ return self._devnull
def communicate(self, input=None, timeout=None):
"""Interact with process: Send data to stdin. Read data from
@@ -839,7 +845,7 @@ class Popen(object):
return (stdout, stderr)
try:
- stdout, stderr = self._communicate(input, endtime)
+ stdout, stderr = self._communicate(input, endtime, timeout)
finally:
self._communication_started = True
@@ -860,12 +866,12 @@ class Popen(object):
return endtime - time.time()
- def _check_timeout(self, endtime):
+ def _check_timeout(self, endtime, orig_timeout):
"""Convenience for checking if a timeout has expired."""
if endtime is None:
return
if time.time() > endtime:
- raise TimeoutExpired(self.args)
+ raise TimeoutExpired(self.args, orig_timeout)
if mswindows:
@@ -889,6 +895,8 @@ class Popen(object):
p2cread, _ = _subprocess.CreatePipe(None, 0)
elif stdin == PIPE:
p2cread, p2cwrite = _subprocess.CreatePipe(None, 0)
+ elif stdin == DEVNULL:
+ p2cread = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdin, int):
p2cread = msvcrt.get_osfhandle(stdin)
else:
@@ -902,6 +910,8 @@ class Popen(object):
_, c2pwrite = _subprocess.CreatePipe(None, 0)
elif stdout == PIPE:
c2pread, c2pwrite = _subprocess.CreatePipe(None, 0)
+ elif stdout == DEVNULL:
+ c2pwrite = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdout, int):
c2pwrite = msvcrt.get_osfhandle(stdout)
else:
@@ -917,6 +927,8 @@ class Popen(object):
errread, errwrite = _subprocess.CreatePipe(None, 0)
elif stderr == STDOUT:
errwrite = c2pwrite
+ elif stderr == DEVNULL:
+ errwrite = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stderr, int):
errwrite = msvcrt.get_osfhandle(stderr)
else:
@@ -1010,7 +1022,7 @@ class Popen(object):
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 simliar), but
+ # translate errno using _sys_errlist (or similar), but
# how can this be done from Python?
raise WindowsError(*e.args)
finally:
@@ -1026,6 +1038,8 @@ class Popen(object):
c2pwrite.Close()
if errwrite != -1:
errwrite.Close()
+ if hasattr(self, '_devnull'):
+ os.close(self._devnull)
# Retain the process handle, but close the thread handle
self._child_created = True
@@ -1050,9 +1064,11 @@ class Popen(object):
return self.returncode
- def wait(self, timeout=None):
+ def wait(self, timeout=None, endtime=None):
"""Wait for child process to terminate. Returns returncode
attribute."""
+ if endtime is not None:
+ timeout = self._remaining_time(endtime)
if timeout is None:
timeout = _subprocess.INFINITE
else:
@@ -1060,7 +1076,7 @@ class Popen(object):
if self.returncode is None:
result = _subprocess.WaitForSingleObject(self._handle, timeout)
if result == _subprocess.WAIT_TIMEOUT:
- raise TimeoutExpired(self.args)
+ raise TimeoutExpired(self.args, timeout)
self.returncode = _subprocess.GetExitCodeProcess(self._handle)
return self.returncode
@@ -1070,7 +1086,7 @@ class Popen(object):
fh.close()
- def _communicate(self, input, endtime):
+ def _communicate(self, input, endtime, orig_timeout):
# Start reader threads feeding into a list hanging off of this
# object, unless they've already been started.
if self.stdout and not hasattr(self, "_stdout_buff"):
@@ -1159,6 +1175,8 @@ class Popen(object):
pass
elif stdin == PIPE:
p2cread, p2cwrite = _create_pipe()
+ elif stdin == DEVNULL:
+ p2cread = self._get_devnull()
elif isinstance(stdin, int):
p2cread = stdin
else:
@@ -1169,6 +1187,8 @@ class Popen(object):
pass
elif stdout == PIPE:
c2pread, c2pwrite = _create_pipe()
+ elif stdout == DEVNULL:
+ c2pwrite = self._get_devnull()
elif isinstance(stdout, int):
c2pwrite = stdout
else:
@@ -1181,6 +1201,8 @@ class Popen(object):
errread, errwrite = _create_pipe()
elif stderr == STDOUT:
errwrite = c2pwrite
+ elif stderr == DEVNULL:
+ errwrite = self._get_devnull()
elif isinstance(stderr, int):
errwrite = stderr
else:
@@ -1374,6 +1396,8 @@ class Popen(object):
os.close(c2pwrite)
if errwrite != -1 and errread != -1:
os.close(errwrite)
+ if hasattr(self, '_devnull'):
+ os.close(self._devnull)
# Wait for exec to fail or succeed; possibly raising an
# exception (limited in size)
@@ -1468,13 +1492,18 @@ class Popen(object):
def wait(self, timeout=None, endtime=None):
"""Wait for child process to terminate. Returns returncode
attribute."""
- # If timeout was passed but not endtime, compute endtime in terms of
- # timeout.
- if endtime is None and timeout is not None:
- endtime = time.time() + timeout
if self.returncode is not None:
return self.returncode
- elif endtime is not None:
+
+ # endtime is preferred to timeout. timeout is only used for
+ # printing.
+ if endtime is not None or timeout is not None:
+ if endtime is None:
+ endtime = time.time() + timeout
+ elif timeout is None:
+ timeout = self._remaining_time(endtime)
+
+ if endtime is not None:
# Enter a busy loop if we have a timeout. This busy loop was
# cribbed from Lib/threading.py in Thread.wait() at r71065.
delay = 0.0005 # 500 us -> initial delay of 1 ms
@@ -1486,7 +1515,7 @@ class Popen(object):
break
remaining = self._remaining_time(endtime)
if remaining <= 0:
- raise TimeoutExpired(self.args)
+ raise TimeoutExpired(self.args, timeout)
delay = min(delay * 2, remaining, .05)
time.sleep(delay)
elif self.returncode is None:
@@ -1495,7 +1524,7 @@ class Popen(object):
return self.returncode
- def _communicate(self, input, endtime):
+ def _communicate(self, input, endtime, orig_timeout):
if self.stdin and not self._communication_started:
# Flush stdio buffer. This might block, if the user has
# been writing to .stdin in an uncontrolled fashion.
@@ -1504,9 +1533,11 @@ class Popen(object):
self.stdin.close()
if _has_poll:
- stdout, stderr = self._communicate_with_poll(input, endtime)
+ stdout, stderr = self._communicate_with_poll(input, endtime,
+ orig_timeout)
else:
- stdout, stderr = self._communicate_with_select(input, endtime)
+ stdout, stderr = self._communicate_with_select(input, endtime,
+ orig_timeout)
self.wait(timeout=self._remaining_time(endtime))
@@ -1529,7 +1560,7 @@ class Popen(object):
return (stdout, stderr)
- def _communicate_with_poll(self, input, endtime):
+ def _communicate_with_poll(self, input, endtime, orig_timeout):
stdout = None # Return
stderr = None # Return
@@ -1580,7 +1611,7 @@ class Popen(object):
if e.args[0] == errno.EINTR:
continue
raise
- self._check_timeout(endtime)
+ 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!
@@ -1604,7 +1635,7 @@ class Popen(object):
return (stdout, stderr)
- def _communicate_with_select(self, input, endtime):
+ def _communicate_with_select(self, input, endtime, orig_timeout):
if not self._communication_started:
self._read_set = []
self._write_set = []
@@ -1646,9 +1677,9 @@ class Popen(object):
# According to the docs, returning three empty lists indicates
# that the timeout expired.
if not (rlist or wlist or xlist):
- raise TimeoutExpired(self.args)
+ raise TimeoutExpired(self.args, orig_timeout)
# We also check what time it is ourselves for good measure.
- self._check_timeout(endtime)
+ 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!