From 46a73c7e4c6c6ad5bea4134fe60268fee03cbf6c Mon Sep 17 00:00:00 2001 From: Andrew Kuchling Date: Mon, 14 Apr 2014 15:08:18 -0400 Subject: #10481: describe universal_newlines' effect on communicate()/check_output() output (alternately bytes or strings) Patch by Sam Kimbrel. --- Lib/subprocess.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 38f0f185e8..45b58e34a1 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -104,17 +104,21 @@ in the child process prior to executing the command. If env is not None, it defines the environment variables for the new process. -If universal_newlines is false, the file objects stdin, stdout and stderr +If universal_newlines is False, the file objects stdin, stdout and stderr are opened as binary files, and no line ending conversion is done. -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', +If universal_newlines is True, the file objects stdout and stderr are +opened as a text file, but lines may be terminated by any of '\n', the Unix end-of-line convention, '\r', the old Macintosh convention or '\r\n', the Windows convention. All of these external representations are seen as '\n' by the Python program. Also, the newlines attribute of the file objects stdout, stdin and stderr are not updated by the communicate() method. +In either case, the process being communicated with should start up +expecting to receive bytes on its standard input and decode them with +the same encoding they are sent in. + The startupinfo and creationflags, if given, will be passed to the underlying CreateProcess() function. They can specify things such as appearance of the main window and priority for the new process. @@ -184,6 +188,9 @@ check_output(*popenargs, **kwargs): pass a string to the subprocess's stdin. If you use this argument you may not also use the Popen constructor's "stdin" argument. + If universal_newlines is set to True, the "input" argument must + be a string rather than bytes, and the return value will be a string. + Exceptions ---------- Exceptions raised in the child process, before the new program has @@ -225,9 +232,13 @@ wait() communicate(input=None) Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to - terminate. The optional input argument should be a string to be + terminate. The optional input argument should be data to be sent to the child process, or None, if no data should be sent to - the child. + the child. If the Popen instance was constructed with universal_newlines + set to True, the input argument should be a string and will be encoded + using the preferred system encoding (see locale.getpreferredencoding); + if universal_newlines is False, the input argument should be a + byte string. communicate() returns a tuple (stdout, stderr). @@ -587,8 +598,8 @@ def check_output(*popenargs, timeout=None, **kwargs): ... 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 universal_newlines=True is passed, the "input" argument must be a + string and the return value will be a string rather than bytes. """ if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') @@ -908,11 +919,16 @@ class Popen(object): def communicate(self, input=None, timeout=None): """Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be - bytes to be sent to the child process, or None, if no data - should be sent to the child. + process to terminate. + + The optional "input" argument should be data to be sent to the + child process (if self.universal_newlines is True, this should + be a string; if it is False, "input" should be bytes), or + None, if no data should be sent to the child. - communicate() returns a tuple (stdout, stderr).""" + communicate() returns a tuple (stdout, stderr). These will be + bytes or, if self.universal_newlines was True, a string. + """ if self._communication_started and input: raise ValueError("Cannot send input after starting communication") -- cgit v1.2.1 From 88fbe58251c5d398455d49250aa4420fdb58751f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 1 Jun 2014 00:33:35 -0700 Subject: Remove the obsolete MAXFD constant and Popen._close_fds() method. They should have been removed years ago when removing the old pure Python implementation from the file. --- Lib/subprocess.py | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index daa3e25c0e..49aa6ab983 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -469,10 +469,6 @@ if mswindows: __del__ = Close __str__ = __repr__ -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except: - MAXFD = 256 # This lists holds Popen instances for which the underlying process had not # exited at the time its __del__ method got called: those processes are wait()ed @@ -1335,16 +1331,6 @@ class Popen(object): errread, errwrite) - def _close_fds(self, fds_to_keep): - start_fd = 3 - for fd in sorted(fds_to_keep): - if fd >= start_fd: - os.closerange(start_fd, fd) - start_fd = fd + 1 - if start_fd <= MAXFD: - os.closerange(start_fd, MAXFD) - - def _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, -- cgit v1.2.1 From bcd8dccf51c716a73884baa8eb3ad84065685a8b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 25 Jul 2014 23:36:00 +0300 Subject: Issue #22033: Reprs of most Python implemened classes now contain actual class name instead of hardcoded one. --- Lib/subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 49aa6ab983..6dfa40b4b9 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -464,7 +464,7 @@ if mswindows: raise ValueError("already closed") def __repr__(self): - return "Handle(%d)" % int(self) + return "%s(%d)" % (self.__class__.__name__, int(self)) __del__ = Close __str__ = __repr__ -- cgit v1.2.1 From f3579a8d0eed6151a1e2f34f6cad0677bfe45246 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Sep 2014 23:18:25 +0200 Subject: Issue #22043: time.monotonic() is now always available threading.Lock.acquire(), threading.RLock.acquire() and socket operations now use a monotonic clock, instead of the system clock, when a timeout is used. --- Lib/subprocess.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6dfa40b4b9..bb7d0dc0b9 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -365,10 +365,7 @@ import signal import builtins import warnings import errno -try: - from time import monotonic as _time -except ImportError: - from time import time as _time +from time import monotonic as _time # Exception classes used by this module. class SubprocessError(Exception): pass -- cgit v1.2.1 From 77b35385c7eab23abcc89881d9bb91f6b55356b5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 14 Jan 2015 17:07:59 +0100 Subject: Closes #23234: Refactor subprocess Use new OSError exceptions, factorize stdin.write() code. --- Lib/subprocess.py | 58 +++++++++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 3ce0ed1bf3..2855d01323 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -920,6 +920,22 @@ class Popen(object): self._devnull = os.open(os.devnull, os.O_RDWR) return self._devnull + def _stdin_write(self, input): + if input: + try: + self.stdin.write(input) + except BrokenPipeError: + # communicate() must ignore broken pipe error + pass + except OSError as e: + if e.errno == errno.EINVAL and self.poll() is not None: + # Issue #19612: On Windows, stdin.write() fails with EINVAL + # if the process already exited before the write + pass + else: + raise + self.stdin.close() + def communicate(self, input=None, timeout=None): """Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for @@ -945,13 +961,7 @@ class Popen(object): stdout = None stderr = None if self.stdin: - if input: - try: - self.stdin.write(input) - except OSError as e: - if e.errno != errno.EPIPE and e.errno != errno.EINVAL: - raise - self.stdin.close() + self._stdin_write(input) elif self.stdout: stdout = _eintr_retry_call(self.stdout.read) self.stdout.close() @@ -1200,21 +1210,7 @@ class Popen(object): self.stderr_thread.start() if self.stdin: - if input is not None: - try: - self.stdin.write(input) - except OSError as e: - if e.errno == errno.EPIPE: - # communicate() should ignore pipe full error - pass - elif (e.errno == errno.EINVAL - and self.poll() is not None): - # Issue #19612: stdin.write() fails with EINVAL - # if the process already exited before the write - pass - else: - raise - self.stdin.close() + self._stdin_write(input) # Wait for the reader threads, or time out. If we time out, the # threads remain reading and the fds left open in case the user @@ -1425,9 +1421,8 @@ class Popen(object): if errpipe_data: try: _eintr_retry_call(os.waitpid, self.pid, 0) - except OSError as e: - if e.errno != errno.ECHILD: - raise + except ChildProcessError: + pass try: exception_name, hex_errno, err_msg = ( errpipe_data.split(b':', 2)) @@ -1511,9 +1506,7 @@ class Popen(object): """All callers to this function MUST hold self._waitpid_lock.""" try: (pid, sts) = _eintr_retry_call(os.waitpid, self.pid, wait_flags) - except OSError as e: - if e.errno != errno.ECHILD: - raise + except ChildProcessError: # This happens if SIGCLD is set to be ignored or waiting # for child processes has otherwise been disabled for our # process. This child is dead, we can't get the status. @@ -1625,12 +1618,9 @@ class Popen(object): 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 + except BrokenPipeError: + selector.unregister(key.fileobj) + key.fileobj.close() else: if self._input_offset >= len(self._input): selector.unregister(key.fileobj) -- cgit v1.2.1 From 166a57a01b10a5398290e02aa4ad6142cf9dc41e Mon Sep 17 00:00:00 2001 From: Charles-Fran?ois Natali Date: Sat, 7 Feb 2015 13:27:50 +0000 Subject: Issue #23285: PEP 475 -- Retry system calls failing with EINTR. --- Lib/subprocess.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 2855d01323..25ffefff6c 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -489,14 +489,6 @@ STDOUT = -2 DEVNULL = -3 -def _eintr_retry_call(func, *args): - while True: - try: - return func(*args) - except InterruptedError: - continue - - # XXX This function is only used by multiprocessing and the test suite, # but it's here so that it can be imported when Python is compiled without # threads. @@ -963,10 +955,10 @@ class Popen(object): if self.stdin: self._stdin_write(input) elif self.stdout: - stdout = _eintr_retry_call(self.stdout.read) + stdout = self.stdout.read() self.stdout.close() elif self.stderr: - stderr = _eintr_retry_call(self.stderr.read) + stderr = self.stderr.read() self.stderr.close() self.wait() else: @@ -1410,7 +1402,7 @@ class Popen(object): # exception (limited in size) errpipe_data = bytearray() while True: - part = _eintr_retry_call(os.read, errpipe_read, 50000) + part = os.read(errpipe_read, 50000) errpipe_data += part if not part or len(errpipe_data) > 50000: break @@ -1420,7 +1412,7 @@ class Popen(object): if errpipe_data: try: - _eintr_retry_call(os.waitpid, self.pid, 0) + os.waitpid(self.pid, 0) except ChildProcessError: pass try: @@ -1505,7 +1497,7 @@ class Popen(object): def _try_wait(self, wait_flags): """All callers to this function MUST hold self._waitpid_lock.""" try: - (pid, sts) = _eintr_retry_call(os.waitpid, self.pid, wait_flags) + (pid, sts) = os.waitpid(self.pid, wait_flags) except ChildProcessError: # This happens if SIGCLD is set to be ignored or waiting # for child processes has otherwise been disabled for our -- cgit v1.2.1 From 17b6d6c9768223c33ae588edfead07fc4e58a7fc Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 7 Apr 2015 15:57:54 -0700 Subject: Addresses Issue #10838: The subprocess now module includes SubprocessError and TimeoutError in its list of exported names for the users wild enough to use "from subprocess import *". MAXFD, mswindows and list2cmdline should be dealt with (renamed or moved) in separate commits. Committed at 35,000ft. Thanks chromebook free gogo wifi passes! --- Lib/subprocess.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6d2c4f5587..1c7eb9e552 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -433,7 +433,10 @@ else: __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", - "getoutput", "check_output", "CalledProcessError", "DEVNULL"] + "getoutput", "check_output", "CalledProcessError", "DEVNULL", + "SubprocessError", "TimeoutExpired"] + # NOTE: We intentionally exclude list2cmdline as it is + # considered an internal implementation detail. issue10838. if mswindows: from _winapi import (CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP, -- cgit v1.2.1 From 94da7c1e1152f1003bc24fc87d72184db82632ea Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 7 Apr 2015 16:11:33 -0700 Subject: issue10838: Rename the subprocess.mswindows internal global to _mswindows. It is internal only, not a documented API. --- Lib/subprocess.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 1c7eb9e552..e92928e9da 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -356,7 +356,7 @@ Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) """ import sys -mswindows = (sys.platform == "win32") +_mswindows = (sys.platform == "win32") import io import os @@ -399,7 +399,7 @@ class TimeoutExpired(SubprocessError): (self.cmd, self.timeout)) -if mswindows: +if _mswindows: import threading import msvcrt import _winapi @@ -438,7 +438,7 @@ __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", # NOTE: We intentionally exclude list2cmdline as it is # considered an internal implementation detail. issue10838. -if mswindows: +if _mswindows: from _winapi import (CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE, SW_HIDE, @@ -765,7 +765,7 @@ class Popen(object): if not isinstance(bufsize, int): raise TypeError("bufsize must be an integer") - if mswindows: + if _mswindows: if preexec_fn is not None: raise ValueError("preexec_fn is not supported on Windows " "platforms") @@ -825,7 +825,7 @@ class Popen(object): # quickly terminating child could make our fds unwrappable # (see #8458). - if mswindows: + if _mswindows: if p2cwrite != -1: p2cwrite = msvcrt.open_osfhandle(p2cwrite.Detach(), 0) if c2pread != -1: @@ -1002,7 +1002,7 @@ class Popen(object): raise TimeoutExpired(self.args, orig_timeout) - if mswindows: + if _mswindows: # # Windows methods # -- cgit v1.2.1 From 5c78a2cbaef358f7d0212250a35d5fb40ad870b0 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 14 Apr 2015 16:14:25 -0700 Subject: Add a subprocess.run() function than returns a CalledProcess instance for a more consistent API than the existing call* functions. (enhancement from issue 23342) --- Lib/subprocess.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 16 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index e92928e9da..b6c437438e 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -377,27 +377,51 @@ class CalledProcessError(SubprocessError): The exit status will be stored in the returncode attribute; check_output() will also store the output in the output attribute. """ - def __init__(self, returncode, cmd, output=None): + def __init__(self, returncode, cmd, output=None, stderr=None): self.returncode = returncode self.cmd = cmd self.output = output + self.stderr = stderr + def __str__(self): return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + @property + def stdout(self): + """Alias for output attribute, to match stderr""" + return self.output + + @stdout.setter + def stdout(self, value): + # There's no obvious reason to set this, but allow it anyway so + # .stdout is a transparent alias for .output + self.output = value + class TimeoutExpired(SubprocessError): """This exception is raised when the timeout expires while waiting for a child process. """ - def __init__(self, cmd, timeout, output=None): + def __init__(self, cmd, timeout, output=None, stderr=None): self.cmd = cmd self.timeout = timeout self.output = output + self.stderr = stderr def __str__(self): return ("Command '%s' timed out after %s seconds" % (self.cmd, self.timeout)) + @property + def stdout(self): + return self.output + + @stdout.setter + def stdout(self, value): + # There's no obvious reason to set this, but allow it anyway so + # .stdout is a transparent alias for .output + self.output = value + if _mswindows: import threading @@ -433,8 +457,8 @@ else: __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", - "getoutput", "check_output", "CalledProcessError", "DEVNULL", - "SubprocessError", "TimeoutExpired"] + "getoutput", "check_output", "run", "CalledProcessError", "DEVNULL", + "SubprocessError", "TimeoutExpired", "CompletedProcess"] # NOTE: We intentionally exclude list2cmdline as it is # considered an internal implementation detail. issue10838. @@ -595,29 +619,97 @@ def check_output(*popenargs, timeout=None, **kwargs): """ if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') - if 'input' in kwargs: + + if 'input' in kwargs and kwargs['input'] is None: + # Explicitly passing input=None was previously equivalent to passing an + # empty string. That is maintained here for backwards compatibility. + kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b'' + + return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, + **kwargs).stdout + + +class CompletedProcess(object): + """A process that has finished running. + + This is returned by run(). + + Attributes: + args: The list or str args passed to run(). + returncode: The exit code of the process, negative for signals. + stdout: The standard output (None if not captured). + stderr: The standard error (None if not captured). + """ + def __init__(self, args, returncode, stdout=None, stderr=None): + self.args = args + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + def __repr__(self): + args = ['args={!r}'.format(self.args), + 'returncode={!r}'.format(self.returncode)] + if self.stdout is not None: + args.append('stdout={!r}'.format(self.stdout)) + if self.stderr is not None: + args.append('stderr={!r}'.format(self.stderr)) + return "{}({})".format(type(self).__name__, ', '.join(args)) + + def check_returncode(self): + """Raise CalledProcessError if the exit code is non-zero.""" + if self.returncode: + raise CalledProcessError(self.returncode, self.args, self.stdout, + self.stderr) + + +def run(*popenargs, input=None, timeout=None, check=False, **kwargs): + """Run command with arguments and return a CompletedProcess instance. + + The returned instance will have attributes args, returncode, stdout and + stderr. By default, stdout and stderr are not captured, and those attributes + will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. + + If check is True and the exit code was non-zero, it raises a + CalledProcessError. The CalledProcessError object will have the return code + in the returncode attribute, and output & stderr attributes if those streams + were captured. + + If timeout is given, and the process takes too long, a TimeoutExpired + exception will be raised. + + There is an 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 will be used internally. + + The other arguments are the same as for the Popen constructor. + + If universal_newlines=True is passed, the "input" argument must be a + string and stdout/stderr in the returned object will be strings rather than + bytes. + """ + if input is not None: 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: + + with Popen(*popenargs, **kwargs) as process: try: - output, unused_err = process.communicate(inputdata, timeout=timeout) + stdout, stderr = process.communicate(input, timeout=timeout) except TimeoutExpired: process.kill() - output, unused_err = process.communicate() - raise TimeoutExpired(process.args, timeout, output=output) + stdout, stderr = process.communicate() + raise TimeoutExpired(process.args, timeout, output=stdout, + stderr=stderr) except: process.kill() process.wait() raise retcode = process.poll() - if retcode: - raise CalledProcessError(retcode, process.args, output=output) - return output + if check and retcode: + raise CalledProcessError(retcode, process.args, + output=stdout, stderr=stderr) + return CompletedProcess(process.args, retcode, stdout, stderr) def list2cmdline(seq): -- cgit v1.2.1 From f10f99298e48e3e471801ff5454a0b2af85f7c50 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 18 Jan 2016 12:15:08 +0100 Subject: subprocess._optim_args_from_interpreter_flags() Issue #26100: * Add subprocess._optim_args_from_interpreter_flags() * Add test.support.optim_args_from_interpreter_flags() * Use new functions in distutils, test_cmd_line_script, test_compileall and test_inspect The change enables test_details() test of test_inspect when -O or -OO command line option is used. --- Lib/subprocess.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index d1324b8aed..640519d8db 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -520,6 +520,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.""" @@ -527,7 +537,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', @@ -535,8 +544,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: -- cgit v1.2.1 From ea98cef77dfdfdc050d8033bd1b2fb865327f70a Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 16 Apr 2016 23:42:37 +0000 Subject: Issue #26782: Add STARTUPINFO to subprocess.__all__ on Windows --- Lib/subprocess.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 640519d8db..e9803497d5 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -471,7 +471,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 -- cgit v1.2.1 From 9333f943642e90267d2eaa66bf97772bea26df8d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 May 2016 12:08:12 +0200 Subject: Issue #26741: POSIX implementation of subprocess.Popen._execute_child() now sets the returncode attribute using the child process exit status when exec failed. --- Lib/subprocess.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 642c7f2182..41a9de10f5 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1524,9 +1524,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)) -- cgit v1.2.1 From 6a12ea8b7770699a457c4ec292e635c1983bea92 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 May 2016 12:11:15 +0200 Subject: subprocess now emits a ResourceWarning warning Issue #26741: subprocess.Popen destructor now emits a ResourceWarning warning if the child process is still running. --- Lib/subprocess.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 41a9de10f5..b853f4d593 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1006,6 +1006,9 @@ class Popen(object): if not self._child_created: # We didn't get to successfully create a child process. return + if self.returncode is None: + warnings.warn("running subprocess %r" % self, 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: -- cgit v1.2.1 From 92f91b56022bdbfd8c427f2e045d128fe2882f43 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 3 Jun 2016 06:14:06 +0000 Subject: Issue #27167: Clarify the subprocess.CalledProcessError error message text when the child process died due to a signal. --- Lib/subprocess.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index b853f4d593..716f7fff53 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -372,9 +372,11 @@ class SubprocessError(Exception): pass class CalledProcessError(SubprocessError): - """This exception is raised when a process run by check_call() or - check_output() returns a non-zero exit status. - The exit status will be stored in the returncode attribute; + """Raised when a check_call() or check_output() process returns non-zero. + + The exit status will be stored in the returncode attribute, negative + if it represents a signal number. + check_output() will also store the output in the output attribute. """ def __init__(self, returncode, cmd, output=None, stderr=None): @@ -384,7 +386,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): -- cgit v1.2.1 From 4d7898f73a43a65f4c13bd306cb4c1076a5f90ca Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2016 16:42:59 +0200 Subject: subprocess: enhance ResourceWarning message * Add the process identifier to the warning message * Add also a comment to explain the issue --- Lib/subprocess.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 98f339ee5c..3dea089e6a 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -993,7 +993,6 @@ class Popen(object): raise - def _translate_newlines(self, data, encoding): data = data.decode(encoding) return data.replace("\r\n", "\n").replace("\r", "\n") @@ -1018,8 +1017,10 @@ class Popen(object): # We didn't get to successfully create a child process. return if self.returncode is None: - warnings.warn("running subprocess %r" % self, ResourceWarning, - source=self) + # Not reading subprocess exit status creates a zombi process which + # is only destroyed at the parent python process exit + warnings.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: -- cgit v1.2.1 From b54005a701444260deeb4b2880baa17d9a514eb1 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 6 Sep 2016 20:16:17 -0700 Subject: Issue #6135: Adds encoding and errors parameters to subprocess --- Lib/subprocess.py | 87 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 3dea089e6a..9df9318245 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -30,7 +30,8 @@ class Popen(args, bufsize=-1, executable=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=()): + restore_signals=True, start_new_session=False, pass_fds=(), + *, encoding=None, errors=None): Arguments are: @@ -104,20 +105,13 @@ in the child process prior to executing the command. If env is not None, it defines the environment variables for the new process. -If universal_newlines is False, the file objects stdin, stdout and stderr -are opened as binary files, and no line ending conversion is done. +If encoding or errors are specified or universal_newlines is True, the file +objects stdout and stderr are opened in text mode. See io.TextIOWrapper for +the interpretation of these parameters are used. -If universal_newlines is True, the file objects stdout and stderr are -opened as a text file, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the old Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Also, the newlines attribute -of the file objects stdout, stdin and stderr are not updated by the -communicate() method. - -In either case, the process being communicated with should start up -expecting to receive bytes on its standard input and decode them with -the same encoding they are sent in. +If no encoding is specified and universal_newlines is False, the file +objects stdin, stdout and stderr are opened as binary files, and no +line ending conversion is done. The startupinfo and creationflags, if given, will be passed to the underlying CreateProcess() function. They can specify things such as @@ -234,11 +228,8 @@ communicate(input=None) and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be data to be sent to the child process, or None, if no data should be sent to - the child. If the Popen instance was constructed with universal_newlines - set to True, the input argument should be a string and will be encoded - using the preferred system encoding (see locale.getpreferredencoding); - if universal_newlines is False, the input argument should be a - byte string. + the child. If the Popen instance was constructed in text mode, the + input argument should be a string. Otherwise, it should be bytes. communicate() returns a tuple (stdout, stderr). @@ -808,8 +799,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 @@ -859,7 +850,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 @@ -912,6 +903,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: @@ -944,22 +937,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, @@ -993,8 +992,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): @@ -1779,13 +1778,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) @@ -1797,8 +1798,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): -- cgit v1.2.1 From 4d1a9e7cd0574ee116a8fd9cc09e7b65b052e6f0 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 20 Nov 2016 16:25:14 -0800 Subject: Issue #20572: The subprocess.Popen.wait method's undocumented endtime parameter now raises a DeprecationWarning. It was deprecated in 3.4. It was never documented prior to that. --- Lib/subprocess.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index e742a4e199..0b880f68d9 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1031,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 @@ -1392,8 +1396,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 -- cgit v1.2.1 From 3a2730f9bd5713020e5e3b1d60a71b048728af0a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Jan 2017 10:44:44 +0100 Subject: Fix subprocess.Popen.__del__() fox Python shutdown Issue #29174, #26741: subprocess.Popen.__del__() now keeps a strong reference to warnings.warn() function. --- Lib/subprocess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib/subprocess.py') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 0b880f68d9..13b9d44d31 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -750,15 +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 - warnings.warn("subprocess %s is still running" % self.pid, - ResourceWarning, source=self) + _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: -- cgit v1.2.1