diff options
author | Berker Peksag <berker.peksag@gmail.com> | 2017-02-04 09:18:42 +0300 |
---|---|---|
committer | Berker Peksag <berker.peksag@gmail.com> | 2017-02-04 09:18:42 +0300 |
commit | 678487eb345f9f9dea3d3818ecad7d39145bdc65 (patch) | |
tree | 35c1a98d1ceadc6b67bf7119031d2a89f27f1cb1 /Lib/subprocess.py | |
parent | 50a3761c130e9be725bacb5b99d624012c40414a (diff) | |
parent | 19d8ebcbbd396ec603ed03bc79add9b049c31919 (diff) | |
download | cpython-678487eb345f9f9dea3d3818ecad7d39145bdc65.tar.gz |
Issue #29198: Merge from 3.5
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r-- | Lib/subprocess.py | 115 |
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): |