summaryrefslogtreecommitdiff
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py115
1 files changed, 83 insertions, 32 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 21655242a4..822ddb459e 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -71,7 +71,16 @@ class CalledProcessError(SubprocessError):
self.stderr = stderr
def __str__(self):
- return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+ if self.returncode and self.returncode < 0:
+ try:
+ return "Command '%s' died with %r." % (
+ self.cmd, signal.Signals(-self.returncode))
+ except ValueError:
+ return "Command '%s' died with unknown signal %d." % (
+ self.cmd, -self.returncode)
+ else:
+ return "Command '%s' returned non-zero exit status %d." % (
+ self.cmd, self.returncode)
@property
def stdout(self):
@@ -161,7 +170,8 @@ if _mswindows:
__all__.extend(["CREATE_NEW_CONSOLE", "CREATE_NEW_PROCESS_GROUP",
"STD_INPUT_HANDLE", "STD_OUTPUT_HANDLE",
"STD_ERROR_HANDLE", "SW_HIDE",
- "STARTF_USESTDHANDLES", "STARTF_USESHOWWINDOW"])
+ "STARTF_USESTDHANDLES", "STARTF_USESHOWWINDOW",
+ "STARTUPINFO"])
class Handle(int):
closed = False
@@ -210,6 +220,16 @@ DEVNULL = -3
# but it's here so that it can be imported when Python is compiled without
# threads.
+def _optim_args_from_interpreter_flags():
+ """Return a list of command-line arguments reproducing the current
+ optimization settings in sys.flags."""
+ args = []
+ value = sys.flags.optimize
+ if value > 0:
+ args.append('-' + 'O' * value)
+ return args
+
+
def _args_from_interpreter_flags():
"""Return a list of command-line arguments reproducing the current
settings in sys.flags and sys.warnoptions."""
@@ -217,7 +237,6 @@ def _args_from_interpreter_flags():
'debug': 'd',
# 'inspect': 'i',
# 'interactive': 'i',
- 'optimize': 'O',
'dont_write_bytecode': 'B',
'no_user_site': 's',
'no_site': 'S',
@@ -225,8 +244,9 @@ def _args_from_interpreter_flags():
'verbose': 'v',
'bytes_warning': 'b',
'quiet': 'q',
+ # -O is handled in _optim_args_from_interpreter_flags()
}
- args = []
+ args = _optim_args_from_interpreter_flags()
for flag, opt in flag_opt_map.items():
v = getattr(sys.flags, flag)
if v > 0:
@@ -476,8 +496,8 @@ def getstatusoutput(cmd):
""" 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.
+ return a 2-tuple (status, output). The locale encoding is used
+ to decode the output and process newlines.
A trailing newline is stripped from the output.
The exit status for the command can be interpreted
@@ -555,6 +575,9 @@ class Popen(object):
pass_fds (POSIX only)
+ encoding and errors: Text mode encoding and error handling to use for
+ file objects stdin, stdout and stderr.
+
Attributes:
stdin, stdout, stderr, pid, returncode
"""
@@ -566,7 +589,7 @@ class Popen(object):
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
- pass_fds=()):
+ pass_fds=(), *, encoding=None, errors=None):
"""Create new Popen instance."""
_cleanup()
# Held while anything is calling waitpid before returncode has been
@@ -619,6 +642,8 @@ class Popen(object):
self.pid = None
self.returncode = None
self.universal_newlines = universal_newlines
+ self.encoding = encoding
+ self.errors = errors
# Input and output objects. The general principle is like
# this:
@@ -651,22 +676,28 @@ class Popen(object):
if errread != -1:
errread = msvcrt.open_osfhandle(errread.Detach(), 0)
- if p2cwrite != -1:
- self.stdin = io.open(p2cwrite, 'wb', bufsize)
- if universal_newlines:
- self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
- line_buffering=(bufsize == 1))
- if c2pread != -1:
- self.stdout = io.open(c2pread, 'rb', bufsize)
- if universal_newlines:
- self.stdout = io.TextIOWrapper(self.stdout)
- if errread != -1:
- self.stderr = io.open(errread, 'rb', bufsize)
- if universal_newlines:
- self.stderr = io.TextIOWrapper(self.stderr)
+ text_mode = encoding or errors or universal_newlines
self._closed_child_pipe_fds = False
+
try:
+ if p2cwrite != -1:
+ self.stdin = io.open(p2cwrite, 'wb', bufsize)
+ if text_mode:
+ self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
+ line_buffering=(bufsize == 1),
+ encoding=encoding, errors=errors)
+ if c2pread != -1:
+ self.stdout = io.open(c2pread, 'rb', bufsize)
+ if text_mode:
+ self.stdout = io.TextIOWrapper(self.stdout,
+ encoding=encoding, errors=errors)
+ if errread != -1:
+ self.stderr = io.open(errread, 'rb', bufsize)
+ if text_mode:
+ self.stderr = io.TextIOWrapper(self.stderr,
+ encoding=encoding, errors=errors)
+
self._execute_child(args, executable, preexec_fn, close_fds,
pass_fds, cwd, env,
startupinfo, creationflags, shell,
@@ -700,9 +731,8 @@ class Popen(object):
raise
-
- def _translate_newlines(self, data, encoding):
- data = data.decode(encoding)
+ def _translate_newlines(self, data, encoding, errors):
+ data = data.decode(encoding, errors)
return data.replace("\r\n", "\n").replace("\r", "\n")
def __enter__(self):
@@ -720,10 +750,15 @@ class Popen(object):
# Wait for the process to terminate, to avoid zombies.
self.wait()
- def __del__(self, _maxsize=sys.maxsize):
+ def __del__(self, _maxsize=sys.maxsize, _warn=warnings.warn):
if not self._child_created:
# We didn't get to successfully create a child process.
return
+ if self.returncode is None:
+ # Not reading subprocess exit status creates a zombi process which
+ # is only destroyed at the parent python process exit
+ _warn("subprocess %s is still running" % self.pid,
+ ResourceWarning, source=self)
# In case the child hasn't been waited on, check if it's done.
self._internal_poll(_deadstate=_maxsize)
if self.returncode is None and _active is not None:
@@ -996,6 +1031,10 @@ class Popen(object):
"""Wait for child process to terminate. Returns returncode
attribute."""
if endtime is not None:
+ warnings.warn(
+ "'endtime' argument is deprecated; use 'timeout'.",
+ DeprecationWarning,
+ stacklevel=2)
timeout = self._remaining_time(endtime)
if timeout is None:
timeout_millis = _winapi.INFINITE
@@ -1251,9 +1290,14 @@ class Popen(object):
if errpipe_data:
try:
- os.waitpid(self.pid, 0)
+ pid, sts = os.waitpid(self.pid, 0)
+ if pid == self.pid:
+ self._handle_exitstatus(sts)
+ else:
+ self.returncode = sys.maxsize
except ChildProcessError:
pass
+
try:
exception_name, hex_errno, err_msg = (
errpipe_data.split(b':', 2))
@@ -1355,8 +1399,11 @@ class Popen(object):
if self.returncode is not None:
return self.returncode
- # endtime is preferred to timeout. timeout is only used for
- # printing.
+ if endtime is not None:
+ warnings.warn(
+ "'endtime' argument is deprecated; use 'timeout'.",
+ DeprecationWarning,
+ stacklevel=2)
if endtime is not None or timeout is not None:
if endtime is None:
endtime = _time() + timeout
@@ -1482,13 +1529,15 @@ class Popen(object):
# Translate newlines, if requested.
# This also turns bytes into strings.
- if self.universal_newlines:
+ if self.encoding or self.errors or self.universal_newlines:
if stdout is not None:
stdout = self._translate_newlines(stdout,
- self.stdout.encoding)
+ self.stdout.encoding,
+ self.stdout.errors)
if stderr is not None:
stderr = self._translate_newlines(stderr,
- self.stderr.encoding)
+ self.stderr.encoding,
+ self.stderr.errors)
return (stdout, stderr)
@@ -1500,8 +1549,10 @@ class Popen(object):
if self.stdin and self._input is None:
self._input_offset = 0
self._input = input
- if self.universal_newlines and input is not None:
- self._input = self._input.encode(self.stdin.encoding)
+ if input is not None and (
+ self.encoding or self.errors or self.universal_newlines):
+ self._input = self._input.encode(self.stdin.encoding,
+ self.stdin.errors)
def send_signal(self, sig):