diff options
author | orivej <[email protected]> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/tools/python3/src/Lib/subprocess.py | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/python3/src/Lib/subprocess.py')
-rw-r--r-- | contrib/tools/python3/src/Lib/subprocess.py | 3230 |
1 files changed, 1615 insertions, 1615 deletions
diff --git a/contrib/tools/python3/src/Lib/subprocess.py b/contrib/tools/python3/src/Lib/subprocess.py index 4effc1d8b3f..e767de3ac6f 100644 --- a/contrib/tools/python3/src/Lib/subprocess.py +++ b/contrib/tools/python3/src/Lib/subprocess.py @@ -1,59 +1,59 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# Copyright (c) 2003-2005 by Peter Astrand <[email protected]> -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. - -For a complete description of this module see the Python documentation. - -Main API -======== -run(...): Runs a command, waits for it to complete, then returns a - CompletedProcess instance. -Popen(...): A class for flexibly executing a command in a new process - -Constants ---------- -DEVNULL: Special value that indicates that os.devnull should be used -PIPE: Special value that indicates a pipe should be created -STDOUT: Special value that indicates that stderr should go to stdout - - -Older API -========= -call(...): Runs a command, waits for it to complete, then returns - the return code. -check_call(...): Same as call() but raises CalledProcessError() - if return code is not 0 -check_output(...): Same as check_call() but returns the contents of - stdout instead of a return code -getoutput(...): Runs a command in the shell, waits for it to complete, - then returns the output -getstatusoutput(...): Runs a command in the shell, waits for it to complete, - then returns a (exitcode, output) tuple -""" - +# subprocess - Subprocesses with accessible I/O streams +# +# For more information about this module, see PEP 324. +# +# Copyright (c) 2003-2005 by Peter Astrand <[email protected]> +# +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. + +r"""Subprocesses with accessible I/O streams + +This module allows you to spawn processes, connect to their +input/output/error pipes, and obtain their return codes. + +For a complete description of this module see the Python documentation. + +Main API +======== +run(...): Runs a command, waits for it to complete, then returns a + CompletedProcess instance. +Popen(...): A class for flexibly executing a command in a new process + +Constants +--------- +DEVNULL: Special value that indicates that os.devnull should be used +PIPE: Special value that indicates a pipe should be created +STDOUT: Special value that indicates that stderr should go to stdout + + +Older API +========= +call(...): Runs a command, waits for it to complete, then returns + the return code. +check_call(...): Same as call() but raises CalledProcessError() + if return code is not 0 +check_output(...): Same as check_call() but returns the contents of + stdout instead of a return code +getoutput(...): Runs a command in the shell, waits for it to complete, + then returns the output +getstatusoutput(...): Runs a command in the shell, waits for it to complete, + then returns a (exitcode, output) tuple +""" + import builtins import errno -import io -import os -import time -import signal +import io +import os +import time +import signal import sys import threading -import warnings +import warnings import contextlib -from time import monotonic as _time +from time import monotonic as _time import types - + try: import pwd except ImportError: @@ -101,123 +101,123 @@ else: "CREATE_DEFAULT_ERROR_MODE", "CREATE_BREAKAWAY_FROM_JOB"]) -# Exception classes used by this module. -class SubprocessError(Exception): pass - - -class CalledProcessError(SubprocessError): - """Raised when run() is called with check=True and the process - returns a non-zero exit status. - - Attributes: - cmd, returncode, stdout, stderr, output - """ - def __init__(self, returncode, cmd, output=None, stderr=None): - self.returncode = returncode - self.cmd = cmd - self.output = output - self.stderr = stderr - - def __str__(self): - 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): - """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. - - Attributes: - cmd, output, stdout, stderr, timeout - """ - 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: - class STARTUPINFO: - def __init__(self, *, dwFlags=0, hStdInput=None, hStdOutput=None, - hStdError=None, wShowWindow=0, lpAttributeList=None): - self.dwFlags = dwFlags - self.hStdInput = hStdInput - self.hStdOutput = hStdOutput - self.hStdError = hStdError - self.wShowWindow = wShowWindow - self.lpAttributeList = lpAttributeList or {"handle_list": []} - +# Exception classes used by this module. +class SubprocessError(Exception): pass + + +class CalledProcessError(SubprocessError): + """Raised when run() is called with check=True and the process + returns a non-zero exit status. + + Attributes: + cmd, returncode, stdout, stderr, output + """ + def __init__(self, returncode, cmd, output=None, stderr=None): + self.returncode = returncode + self.cmd = cmd + self.output = output + self.stderr = stderr + + def __str__(self): + 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): + """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. + + Attributes: + cmd, output, stdout, stderr, timeout + """ + 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: + class STARTUPINFO: + def __init__(self, *, dwFlags=0, hStdInput=None, hStdOutput=None, + hStdError=None, wShowWindow=0, lpAttributeList=None): + self.dwFlags = dwFlags + self.hStdInput = hStdInput + self.hStdOutput = hStdOutput + self.hStdError = hStdError + self.wShowWindow = wShowWindow + self.lpAttributeList = lpAttributeList or {"handle_list": []} + def copy(self): - attr_list = self.lpAttributeList.copy() - if 'handle_list' in attr_list: - attr_list['handle_list'] = list(attr_list['handle_list']) - - return STARTUPINFO(dwFlags=self.dwFlags, - hStdInput=self.hStdInput, - hStdOutput=self.hStdOutput, - hStdError=self.hStdError, - wShowWindow=self.wShowWindow, - lpAttributeList=attr_list) - - - class Handle(int): - closed = False - - def Close(self, CloseHandle=_winapi.CloseHandle): - if not self.closed: - self.closed = True - CloseHandle(self) - - def Detach(self): - if not self.closed: - self.closed = True - return int(self) - raise ValueError("already closed") - - def __repr__(self): - return "%s(%d)" % (self.__class__.__name__, int(self)) - - __del__ = Close + attr_list = self.lpAttributeList.copy() + if 'handle_list' in attr_list: + attr_list['handle_list'] = list(attr_list['handle_list']) + + return STARTUPINFO(dwFlags=self.dwFlags, + hStdInput=self.hStdInput, + hStdOutput=self.hStdOutput, + hStdError=self.hStdError, + wShowWindow=self.wShowWindow, + lpAttributeList=attr_list) + + + class Handle(int): + closed = False + + def Close(self, CloseHandle=_winapi.CloseHandle): + if not self.closed: + self.closed = True + CloseHandle(self) + + def Detach(self): + if not self.closed: + self.closed = True + return int(self) + raise ValueError("already closed") + + def __repr__(self): + return "%s(%d)" % (self.__class__.__name__, int(self)) + + __del__ = Close else: # 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). @@ -225,7 +225,7 @@ else: _PopenSelector = selectors.PollSelector else: _PopenSelector = selectors.SelectSelector - + if _mswindows: # On Windows we just need to close `Popen._handle` when we no longer need @@ -237,7 +237,7 @@ if _mswindows: # [1] https://docs.microsoft.com/en-us/windows/desktop/ProcThread/ # creating-processes _active = None - + def _cleanup(): pass else: @@ -246,7 +246,7 @@ else: # wait()ed for synchronously from _cleanup() when a new Popen object is # created, to avoid zombie processes. _active = [] - + def _cleanup(): if _active is None: return @@ -260,253 +260,253 @@ else: # It's harmless that it was already removed, so ignore. pass -PIPE = -1 -STDOUT = -2 -DEVNULL = -3 - - -# 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. - -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, sys.warnoptions and sys._xoptions.""" - flag_opt_map = { - 'debug': 'd', - # 'inspect': 'i', - # 'interactive': 'i', - 'dont_write_bytecode': 'B', - 'no_site': 'S', - 'verbose': 'v', - 'bytes_warning': 'b', - 'quiet': 'q', - # -O is handled in _optim_args_from_interpreter_flags() - } - args = _optim_args_from_interpreter_flags() - for flag, opt in flag_opt_map.items(): - v = getattr(sys.flags, flag) - if v > 0: - args.append('-' + opt * v) - - if sys.flags.isolated: - args.append('-I') - else: - if sys.flags.ignore_environment: - args.append('-E') - if sys.flags.no_user_site: - args.append('-s') - - # -W options - warnopts = sys.warnoptions[:] - bytes_warning = sys.flags.bytes_warning - xoptions = getattr(sys, '_xoptions', {}) - dev_mode = ('dev' in xoptions) - - if bytes_warning > 1: - warnopts.remove("error::BytesWarning") - elif bytes_warning: - warnopts.remove("default::BytesWarning") - if dev_mode: - warnopts.remove('default') - for opt in warnopts: - args.append('-W' + opt) - - # -X options - if dev_mode: - args.extend(('-X', 'dev')) - for opt in ('faulthandler', 'tracemalloc', 'importtime', +PIPE = -1 +STDOUT = -2 +DEVNULL = -3 + + +# 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. + +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, sys.warnoptions and sys._xoptions.""" + flag_opt_map = { + 'debug': 'd', + # 'inspect': 'i', + # 'interactive': 'i', + 'dont_write_bytecode': 'B', + 'no_site': 'S', + 'verbose': 'v', + 'bytes_warning': 'b', + 'quiet': 'q', + # -O is handled in _optim_args_from_interpreter_flags() + } + args = _optim_args_from_interpreter_flags() + for flag, opt in flag_opt_map.items(): + v = getattr(sys.flags, flag) + if v > 0: + args.append('-' + opt * v) + + if sys.flags.isolated: + args.append('-I') + else: + if sys.flags.ignore_environment: + args.append('-E') + if sys.flags.no_user_site: + args.append('-s') + + # -W options + warnopts = sys.warnoptions[:] + bytes_warning = sys.flags.bytes_warning + xoptions = getattr(sys, '_xoptions', {}) + dev_mode = ('dev' in xoptions) + + if bytes_warning > 1: + warnopts.remove("error::BytesWarning") + elif bytes_warning: + warnopts.remove("default::BytesWarning") + if dev_mode: + warnopts.remove('default') + for opt in warnopts: + args.append('-W' + opt) + + # -X options + if dev_mode: + args.extend(('-X', 'dev')) + for opt in ('faulthandler', 'tracemalloc', 'importtime', 'showrefcount', 'utf8', 'oldparser'): - if opt in xoptions: - value = xoptions[opt] - if value is True: - arg = opt - else: - arg = '%s=%s' % (opt, value) - args.extend(('-X', arg)) - - return args - - -def call(*popenargs, timeout=None, **kwargs): - """Run command with arguments. Wait for command to complete or - timeout, then return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - with Popen(*popenargs, **kwargs) as p: - try: - return p.wait(timeout=timeout) - except: # Including KeyboardInterrupt, wait handled that. - p.kill() - # We don't call p.wait() again as p.__exit__ does that for us. - raise - - -def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the call function. Example: - - check_call(["ls", "-l"]) - """ - retcode = call(*popenargs, **kwargs) - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - raise CalledProcessError(retcode, cmd) - return 0 - - -def check_output(*popenargs, timeout=None, **kwargs): - r"""Run command with arguments and return its output. - - If the exit code was non-zero it raises a CalledProcessError. The - CalledProcessError object will have the return code in the returncode - attribute and output in the output attribute. - - The arguments are the same as for the Popen constructor. Example: - - >>> check_output(["ls", "-l", "/dev/null"]) - b'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' - - The stdout argument is not allowed as it is used internally. - To capture standard error in the result, use stderr=STDOUT. - - >>> check_output(["/bin/sh", "-c", - ... "ls -l non_existent_file ; exit 0"], - ... 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' - - By default, all communication is in bytes, and therefore any "input" + if opt in xoptions: + value = xoptions[opt] + if value is True: + arg = opt + else: + arg = '%s=%s' % (opt, value) + args.extend(('-X', arg)) + + return args + + +def call(*popenargs, timeout=None, **kwargs): + """Run command with arguments. Wait for command to complete or + timeout, then return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + """ + with Popen(*popenargs, **kwargs) as p: + try: + return p.wait(timeout=timeout) + except: # Including KeyboardInterrupt, wait handled that. + p.kill() + # We don't call p.wait() again as p.__exit__ does that for us. + raise + + +def check_call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete. If + the exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the call function. Example: + + check_call(["ls", "-l"]) + """ + retcode = call(*popenargs, **kwargs) + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return 0 + + +def check_output(*popenargs, timeout=None, **kwargs): + r"""Run command with arguments and return its output. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(["ls", "-l", "/dev/null"]) + b'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(["/bin/sh", "-c", + ... "ls -l non_existent_file ; exit 0"], + ... 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' + + By default, all communication is in bytes, and therefore any "input" should be bytes, and the return value will be bytes. If in text mode, - any "input" should be a string, and the return value will be a string - decoded according to locale encoding, or by "encoding" if set. Text mode - is triggered by setting any of text, encoding, errors or universal_newlines. - """ - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - - 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. + any "input" should be a string, and the return value will be a string + decoded according to locale encoding, or by "encoding" if set. Text mode + is triggered by setting any of text, encoding, errors or universal_newlines. + """ + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + + 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. if kwargs.get('universal_newlines') or kwargs.get('text'): empty = '' else: empty = b'' kwargs['input'] = empty - - 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)) - + + 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)) + __class_getitem__ = classmethod(types.GenericAlias) - 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, capture_output=False, 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 bytes or 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. - - By default, all communication is in bytes, and therefore any "input" should - be bytes, and the stdout and stderr will be bytes. If in text mode, any - "input" should be a string, and stdout and stderr will be strings decoded - according to locale encoding, or by "encoding" if set. Text mode is - triggered by setting any of text, encoding, errors or universal_newlines. - - The other arguments are the same as for the Popen constructor. - """ - if input is not None: + 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, capture_output=False, 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 bytes or 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. + + By default, all communication is in bytes, and therefore any "input" should + be bytes, and the stdout and stderr will be bytes. If in text mode, any + "input" should be a string, and stdout and stderr will be strings decoded + according to locale encoding, or by "encoding" if set. Text mode is + triggered by setting any of text, encoding, errors or universal_newlines. + + The other arguments are the same as for the Popen constructor. + """ + if input is not None: if kwargs.get('stdin') is not None: - raise ValueError('stdin and input arguments may not both be used.') - kwargs['stdin'] = PIPE - - if capture_output: + raise ValueError('stdin and input arguments may not both be used.') + kwargs['stdin'] = PIPE + + if capture_output: if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None: - raise ValueError('stdout and stderr arguments may not be used ' - 'with capture_output.') - kwargs['stdout'] = PIPE - kwargs['stderr'] = PIPE - - with Popen(*popenargs, **kwargs) as process: - try: - stdout, stderr = process.communicate(input, timeout=timeout) + raise ValueError('stdout and stderr arguments may not be used ' + 'with capture_output.') + kwargs['stdout'] = PIPE + kwargs['stderr'] = PIPE + + with Popen(*popenargs, **kwargs) as process: + try: + stdout, stderr = process.communicate(input, timeout=timeout) except TimeoutExpired as exc: - process.kill() + process.kill() if _mswindows: # Windows accumulates the output in a single blocking # read() call run on child threads, with the timeout @@ -519,134 +519,134 @@ def run(*popenargs, # far into the TimeoutExpired exception. process.wait() raise - except: # Including KeyboardInterrupt, communicate handled that. - process.kill() - # We don't call process.wait() as .__exit__ does that for us. - raise - retcode = process.poll() - if check and retcode: - raise CalledProcessError(retcode, process.args, - output=stdout, stderr=stderr) - return CompletedProcess(process.args, retcode, stdout, stderr) - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - # or search http://msdn.microsoft.com for - # "Parsing C++ Command-Line Arguments" - result = [] - needquote = False + except: # Including KeyboardInterrupt, communicate handled that. + process.kill() + # We don't call process.wait() as .__exit__ does that for us. + raise + retcode = process.poll() + if check and retcode: + raise CalledProcessError(retcode, process.args, + output=stdout, stderr=stderr) + return CompletedProcess(process.args, retcode, stdout, stderr) + + +def list2cmdline(seq): + """ + Translate a sequence of arguments into a command line + string, using the same rules as the MS C runtime: + + 1) Arguments are delimited by white space, which is either a + space or a tab. + + 2) A string surrounded by double quotation marks is + interpreted as a single argument, regardless of white space + contained within. A quoted string can be embedded in an + argument. + + 3) A double quotation mark preceded by a backslash is + interpreted as a literal double quotation mark. + + 4) Backslashes are interpreted literally, unless they + immediately precede a double quotation mark. + + 5) If backslashes immediately precede a double quotation mark, + every pair of backslashes is interpreted as a literal + backslash. If the number of backslashes is odd, the last + backslash escapes the next double quotation mark as + described in rule 3. + """ + + # See + # http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + # or search http://msdn.microsoft.com for + # "Parsing C++ Command-Line Arguments" + result = [] + needquote = False for arg in map(os.fsdecode, seq): - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) or not arg - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backslashes. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backslashes, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return ''.join(result) - - -# Various tools for executing commands and looking at their output and status. -# - -def getstatusoutput(cmd): - """Return (exitcode, output) of executing cmd in a shell. - - Execute the string 'cmd' in a shell with 'check_output' and - 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 - according to the rules for the function 'wait'. Example: - - >>> import subprocess - >>> subprocess.getstatusoutput('ls /bin/ls') - (0, '/bin/ls') - >>> subprocess.getstatusoutput('cat /bin/junk') - (1, 'cat: /bin/junk: No such file or directory') - >>> subprocess.getstatusoutput('/bin/junk') - (127, 'sh: /bin/junk: not found') - >>> subprocess.getstatusoutput('/bin/kill $$') - (-15, '') - """ - try: - data = check_output(cmd, shell=True, text=True, stderr=STDOUT) - exitcode = 0 - except CalledProcessError as ex: - data = ex.output - exitcode = ex.returncode - if data[-1:] == '\n': - data = data[:-1] - return exitcode, data - -def getoutput(cmd): - """Return output (stdout or stderr) of executing cmd in a shell. - - Like getstatusoutput(), except the exit status is ignored and the return - value is a string containing the command's output. Example: - - >>> import subprocess - >>> subprocess.getoutput('ls /bin/ls') - '/bin/ls' - """ - return getstatusoutput(cmd)[1] - - + bs_buf = [] + + # Add a space to separate this argument from the others + if result: + result.append(' ') + + needquote = (" " in arg) or ("\t" in arg) or not arg + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + # Don't know if we need to double yet. + bs_buf.append(c) + elif c == '"': + # Double backslashes. + result.append('\\' * len(bs_buf)*2) + bs_buf = [] + result.append('\\"') + else: + # Normal char + if bs_buf: + result.extend(bs_buf) + bs_buf = [] + result.append(c) + + # Add remaining backslashes, if any. + if bs_buf: + result.extend(bs_buf) + + if needquote: + result.extend(bs_buf) + result.append('"') + + return ''.join(result) + + +# Various tools for executing commands and looking at their output and status. +# + +def getstatusoutput(cmd): + """Return (exitcode, output) of executing cmd in a shell. + + Execute the string 'cmd' in a shell with 'check_output' and + 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 + according to the rules for the function 'wait'. Example: + + >>> import subprocess + >>> subprocess.getstatusoutput('ls /bin/ls') + (0, '/bin/ls') + >>> subprocess.getstatusoutput('cat /bin/junk') + (1, 'cat: /bin/junk: No such file or directory') + >>> subprocess.getstatusoutput('/bin/junk') + (127, 'sh: /bin/junk: not found') + >>> subprocess.getstatusoutput('/bin/kill $$') + (-15, '') + """ + try: + data = check_output(cmd, shell=True, text=True, stderr=STDOUT) + exitcode = 0 + except CalledProcessError as ex: + data = ex.output + exitcode = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return exitcode, data + +def getoutput(cmd): + """Return output (stdout or stderr) of executing cmd in a shell. + + Like getstatusoutput(), except the exit status is ignored and the return + value is a string containing the command's output. Example: + + >>> import subprocess + >>> subprocess.getoutput('ls /bin/ls') + '/bin/ls' + """ + return getstatusoutput(cmd)[1] + + def _use_posix_spawn(): """Check if posix_spawn() can be used for subprocess. @@ -697,44 +697,44 @@ def _use_posix_spawn(): _USE_POSIX_SPAWN = _use_posix_spawn() -class Popen(object): - """ Execute a child program in a new process. - - For a complete description of the arguments see the Python documentation. - - Arguments: - args: A string, or a sequence of program arguments. - - bufsize: supplied as the buffering argument to the open() function when - creating the stdin/stdout/stderr pipe file objects - - executable: A replacement program to execute. - - stdin, stdout and stderr: These specify the executed programs' standard - input, standard output and standard error file handles, respectively. - - preexec_fn: (POSIX only) An object to be called in the child process - just before the child is executed. - - close_fds: Controls closing or inheriting of file descriptors. - - shell: If true, the command will be executed through the shell. - - cwd: Sets the current directory before the child is executed. - - env: Defines the environment variables for the new process. - - text: If true, decode stdin, stdout and stderr using the given encoding - (if set) or the system default otherwise. - - universal_newlines: Alias of text, provided for backwards compatibility. - - startupinfo and creationflags (Windows only) - - restore_signals (POSIX only) - - start_new_session (POSIX only) - +class Popen(object): + """ Execute a child program in a new process. + + For a complete description of the arguments see the Python documentation. + + Arguments: + args: A string, or a sequence of program arguments. + + bufsize: supplied as the buffering argument to the open() function when + creating the stdin/stdout/stderr pipe file objects + + executable: A replacement program to execute. + + stdin, stdout and stderr: These specify the executed programs' standard + input, standard output and standard error file handles, respectively. + + preexec_fn: (POSIX only) An object to be called in the child process + just before the child is executed. + + close_fds: Controls closing or inheriting of file descriptors. + + shell: If true, the command will be executed through the shell. + + cwd: Sets the current directory before the child is executed. + + env: Defines the environment variables for the new process. + + text: If true, decode stdin, stdout and stderr using the given encoding + (if set) or the system default otherwise. + + universal_newlines: Alias of text, provided for backwards compatibility. + + startupinfo and creationflags (Windows only) + + restore_signals (POSIX only) + + start_new_session (POSIX only) + group (POSIX only) extra_groups (POSIX only) @@ -743,112 +743,112 @@ class Popen(object): umask (POSIX only) - 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 - """ - _child_created = False # Set here since __del__ checks it - - def __init__(self, args, bufsize=-1, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=True, - shell=False, cwd=None, env=None, universal_newlines=None, - startupinfo=None, creationflags=0, - restore_signals=True, start_new_session=False, + 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 + """ + _child_created = False # Set here since __del__ checks it + + def __init__(self, args, bufsize=-1, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=True, + shell=False, cwd=None, env=None, universal_newlines=None, + startupinfo=None, creationflags=0, + restore_signals=True, start_new_session=False, pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1): - """Create new Popen instance.""" - _cleanup() - # Held while anything is calling waitpid before returncode has been - # updated to prevent clobbering returncode if wait() or poll() are - # called from multiple threads at once. After acquiring the lock, - # code must re-check self.returncode to see if another thread just - # finished a waitpid() call. - self._waitpid_lock = threading.Lock() - - self._input = None - self._communication_started = False - if bufsize is None: - bufsize = -1 # Restore default - if not isinstance(bufsize, int): - raise TypeError("bufsize must be an integer") - - if _mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - else: - # POSIX - if pass_fds and not close_fds: - warnings.warn("pass_fds overriding close_fds.", RuntimeWarning) - close_fds = True - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.args = args - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.encoding = encoding - self.errors = errors - - # Validate the combinations of text and universal_newlines - if (text is not None and universal_newlines is not None - and bool(universal_newlines) != bool(text)): - raise SubprocessError('Cannot disambiguate when both text ' - 'and universal_newlines are supplied but ' - 'different. Pass one or the other.') - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are -1 when not using PIPEs. The child objects are -1 - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - # We wrap OS handles *before* launching the child, otherwise a - # quickly terminating child could make our fds unwrappable - # (see #8458). - - if _mswindows: - if p2cwrite != -1: - p2cwrite = msvcrt.open_osfhandle(p2cwrite.Detach(), 0) - if c2pread != -1: - c2pread = msvcrt.open_osfhandle(c2pread.Detach(), 0) - if errread != -1: - errread = msvcrt.open_osfhandle(errread.Detach(), 0) - - self.text_mode = encoding or errors or text or universal_newlines - - # How long to resume waiting on a child after the first ^C. - # There is no right value for this. The purpose is to be polite - # yet remain good for interactive users trying to exit a tool. - self._sigint_wait_secs = 0.25 # 1/xkcd221.getRandomNumber() - - self._closed_child_pipe_fds = False - + """Create new Popen instance.""" + _cleanup() + # Held while anything is calling waitpid before returncode has been + # updated to prevent clobbering returncode if wait() or poll() are + # called from multiple threads at once. After acquiring the lock, + # code must re-check self.returncode to see if another thread just + # finished a waitpid() call. + self._waitpid_lock = threading.Lock() + + self._input = None + self._communication_started = False + if bufsize is None: + bufsize = -1 # Restore default + if not isinstance(bufsize, int): + raise TypeError("bufsize must be an integer") + + if _mswindows: + if preexec_fn is not None: + raise ValueError("preexec_fn is not supported on Windows " + "platforms") + else: + # POSIX + if pass_fds and not close_fds: + warnings.warn("pass_fds overriding close_fds.", RuntimeWarning) + close_fds = True + if startupinfo is not None: + raise ValueError("startupinfo is only supported on Windows " + "platforms") + if creationflags != 0: + raise ValueError("creationflags is only supported on Windows " + "platforms") + + self.args = args + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = None + self.returncode = None + self.encoding = encoding + self.errors = errors + + # Validate the combinations of text and universal_newlines + if (text is not None and universal_newlines is not None + and bool(universal_newlines) != bool(text)): + raise SubprocessError('Cannot disambiguate when both text ' + 'and universal_newlines are supplied but ' + 'different. Pass one or the other.') + + # Input and output objects. The general principle is like + # this: + # + # Parent Child + # ------ ----- + # p2cwrite ---stdin---> p2cread + # c2pread <--stdout--- c2pwrite + # errread <--stderr--- errwrite + # + # On POSIX, the child objects are file descriptors. On + # Windows, these are Windows file handles. The parent objects + # are file descriptors on both platforms. The parent objects + # are -1 when not using PIPEs. The child objects are -1 + # when not redirecting. + + (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) = self._get_handles(stdin, stdout, stderr) + + # We wrap OS handles *before* launching the child, otherwise a + # quickly terminating child could make our fds unwrappable + # (see #8458). + + if _mswindows: + if p2cwrite != -1: + p2cwrite = msvcrt.open_osfhandle(p2cwrite.Detach(), 0) + if c2pread != -1: + c2pread = msvcrt.open_osfhandle(c2pread.Detach(), 0) + if errread != -1: + errread = msvcrt.open_osfhandle(errread.Detach(), 0) + + self.text_mode = encoding or errors or text or universal_newlines + + # How long to resume waiting on a child after the first ^C. + # There is no right value for this. The purpose is to be polite + # yet remain good for interactive users trying to exit a tool. + self._sigint_wait_secs = 0.25 # 1/xkcd221.getRandomNumber() + + self._closed_child_pipe_fds = False + if self.text_mode: if bufsize == 1: line_buffering = True @@ -930,62 +930,62 @@ class Popen(object): if uid < 0: raise ValueError(f"User ID cannot be negative, got {uid}") - try: - if p2cwrite != -1: - self.stdin = io.open(p2cwrite, 'wb', bufsize) - if self.text_mode: - self.stdin = io.TextIOWrapper(self.stdin, write_through=True, + try: + if p2cwrite != -1: + self.stdin = io.open(p2cwrite, 'wb', bufsize) + if self.text_mode: + self.stdin = io.TextIOWrapper(self.stdin, write_through=True, line_buffering=line_buffering, - encoding=encoding, errors=errors) - if c2pread != -1: - self.stdout = io.open(c2pread, 'rb', bufsize) - if self.text_mode: - self.stdout = io.TextIOWrapper(self.stdout, - encoding=encoding, errors=errors) - if errread != -1: - self.stderr = io.open(errread, 'rb', bufsize) - if self.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, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite, + encoding=encoding, errors=errors) + if c2pread != -1: + self.stdout = io.open(c2pread, 'rb', bufsize) + if self.text_mode: + self.stdout = io.TextIOWrapper(self.stdout, + encoding=encoding, errors=errors) + if errread != -1: + self.stderr = io.open(errread, 'rb', bufsize) + if self.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, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session) - except: - # Cleanup if the child failed starting. - for f in filter(None, (self.stdin, self.stdout, self.stderr)): - try: - f.close() - except OSError: - pass # Ignore EBADF or other errors. - - if not self._closed_child_pipe_fds: - to_close = [] - if stdin == PIPE: - to_close.append(p2cread) - if stdout == PIPE: - to_close.append(c2pwrite) - if stderr == PIPE: - to_close.append(errwrite) - if hasattr(self, '_devnull'): - to_close.append(self._devnull) - for fd in to_close: - try: - if _mswindows and isinstance(fd, Handle): - fd.Close() - else: - os.close(fd) - except OSError: - pass - - raise - + except: + # Cleanup if the child failed starting. + for f in filter(None, (self.stdin, self.stdout, self.stderr)): + try: + f.close() + except OSError: + pass # Ignore EBADF or other errors. + + if not self._closed_child_pipe_fds: + to_close = [] + if stdin == PIPE: + to_close.append(p2cread) + if stdout == PIPE: + to_close.append(c2pwrite) + if stderr == PIPE: + to_close.append(errwrite) + if hasattr(self, '_devnull'): + to_close.append(self._devnull) + for fd in to_close: + try: + if _mswindows and isinstance(fd, Handle): + fd.Close() + else: + os.close(fd) + except OSError: + pass + + raise + def __repr__(self): obj_repr = ( f"<{self.__class__.__name__}: " @@ -997,220 +997,220 @@ class Popen(object): __class_getitem__ = classmethod(types.GenericAlias) - @property - def universal_newlines(self): - # universal_newlines as retained as an alias of text_mode for API - # compatibility. bpo-31756 - return self.text_mode - - @universal_newlines.setter - def universal_newlines(self, universal_newlines): - self.text_mode = bool(universal_newlines) - - def _translate_newlines(self, data, encoding, errors): - data = data.decode(encoding, errors) - return data.replace("\r\n", "\n").replace("\r", "\n") - - def __enter__(self): - return self - - def __exit__(self, exc_type, value, traceback): - if self.stdout: - self.stdout.close() - if self.stderr: - self.stderr.close() - try: # Flushing a BufferedWriter may raise an error - if self.stdin: - self.stdin.close() - finally: - if exc_type == KeyboardInterrupt: - # https://bugs.python.org/issue25942 - # In the case of a KeyboardInterrupt we assume the SIGINT - # was also already sent to our child processes. We can't - # block indefinitely as that is not user friendly. - # If we have not already waited a brief amount of time in - # an interrupted .wait() or .communicate() call, do so here - # for consistency. - if self._sigint_wait_secs > 0: - try: - self._wait(timeout=self._sigint_wait_secs) - except TimeoutExpired: - pass - self._sigint_wait_secs = 0 # Note that this has been done. - return # resume the KeyboardInterrupt - - # Wait for the process to terminate, to avoid zombies. - self.wait() - - 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 zombie 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: - # 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 _stdin_write(self, input): - if input: - try: - self.stdin.write(input) - except BrokenPipeError: - pass # communicate() must ignore broken pipe errors. - except OSError as exc: - if exc.errno == errno.EINVAL: - # bpo-19612, bpo-30418: On Windows, stdin.write() fails - # with EINVAL if the child process exited or if the child - # process is still running but closed the pipe. - pass - else: - raise - - try: - self.stdin.close() - except BrokenPipeError: - pass # communicate() must ignore broken pipe errors. - except OSError as exc: - if exc.errno == errno.EINVAL: - pass - else: - raise - - def communicate(self, input=None, timeout=None): - """Interact with process: Send data to stdin and close it. - Read data from stdout 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. - communicate() returns a tuple (stdout, stderr). - - By default, all communication is in bytes, and therefore any - "input" should be bytes, and the (stdout, stderr) will be bytes. - If in text mode (indicated by self.text_mode), any "input" should - be a string, and (stdout, stderr) will be strings decoded - according to locale encoding, or by "encoding" if set. Text mode - is triggered by setting any of text, encoding, errors or - universal_newlines. - """ - - if self._communication_started and input: - raise ValueError("Cannot send input after starting communication") - - # Optimization: If we are not worried about timeouts, we haven't - # started communicating, and we have one or zero pipes, using select() - # or threads is unnecessary. - if (timeout is None and not self._communication_started and - [self.stdin, self.stdout, self.stderr].count(None) >= 2): - stdout = None - stderr = None - if self.stdin: - self._stdin_write(input) - elif self.stdout: - stdout = self.stdout.read() - self.stdout.close() - elif self.stderr: - stderr = self.stderr.read() - self.stderr.close() - self.wait() - else: - if timeout is not None: - endtime = _time() + timeout - else: - endtime = None - - try: - stdout, stderr = self._communicate(input, endtime, timeout) - except KeyboardInterrupt: - # https://bugs.python.org/issue25942 - # See the detailed comment in .wait(). - if timeout is not None: - sigint_timeout = min(self._sigint_wait_secs, - self._remaining_time(endtime)) - else: - sigint_timeout = self._sigint_wait_secs - self._sigint_wait_secs = 0 # nothing else should wait. - try: - self._wait(timeout=sigint_timeout) - except TimeoutExpired: - pass - raise # resume the KeyboardInterrupt - - finally: - self._communication_started = True - - sts = self.wait(timeout=self._remaining_time(endtime)) - - return (stdout, stderr) - - - def poll(self): - """Check if child process has terminated. Set and return returncode - attribute.""" - return self._internal_poll() - - - def _remaining_time(self, endtime): - """Convenience for _communicate when computing timeouts.""" - if endtime is None: - return None - else: - return endtime - _time() - - + @property + def universal_newlines(self): + # universal_newlines as retained as an alias of text_mode for API + # compatibility. bpo-31756 + return self.text_mode + + @universal_newlines.setter + def universal_newlines(self, universal_newlines): + self.text_mode = bool(universal_newlines) + + def _translate_newlines(self, data, encoding, errors): + data = data.decode(encoding, errors) + return data.replace("\r\n", "\n").replace("\r", "\n") + + def __enter__(self): + return self + + def __exit__(self, exc_type, value, traceback): + if self.stdout: + self.stdout.close() + if self.stderr: + self.stderr.close() + try: # Flushing a BufferedWriter may raise an error + if self.stdin: + self.stdin.close() + finally: + if exc_type == KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # In the case of a KeyboardInterrupt we assume the SIGINT + # was also already sent to our child processes. We can't + # block indefinitely as that is not user friendly. + # If we have not already waited a brief amount of time in + # an interrupted .wait() or .communicate() call, do so here + # for consistency. + if self._sigint_wait_secs > 0: + try: + self._wait(timeout=self._sigint_wait_secs) + except TimeoutExpired: + pass + self._sigint_wait_secs = 0 # Note that this has been done. + return # resume the KeyboardInterrupt + + # Wait for the process to terminate, to avoid zombies. + self.wait() + + 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 zombie 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: + # 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 _stdin_write(self, input): + if input: + try: + self.stdin.write(input) + except BrokenPipeError: + pass # communicate() must ignore broken pipe errors. + except OSError as exc: + if exc.errno == errno.EINVAL: + # bpo-19612, bpo-30418: On Windows, stdin.write() fails + # with EINVAL if the child process exited or if the child + # process is still running but closed the pipe. + pass + else: + raise + + try: + self.stdin.close() + except BrokenPipeError: + pass # communicate() must ignore broken pipe errors. + except OSError as exc: + if exc.errno == errno.EINVAL: + pass + else: + raise + + def communicate(self, input=None, timeout=None): + """Interact with process: Send data to stdin and close it. + Read data from stdout 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. + communicate() returns a tuple (stdout, stderr). + + By default, all communication is in bytes, and therefore any + "input" should be bytes, and the (stdout, stderr) will be bytes. + If in text mode (indicated by self.text_mode), any "input" should + be a string, and (stdout, stderr) will be strings decoded + according to locale encoding, or by "encoding" if set. Text mode + is triggered by setting any of text, encoding, errors or + universal_newlines. + """ + + if self._communication_started and input: + raise ValueError("Cannot send input after starting communication") + + # Optimization: If we are not worried about timeouts, we haven't + # started communicating, and we have one or zero pipes, using select() + # or threads is unnecessary. + if (timeout is None and not self._communication_started and + [self.stdin, self.stdout, self.stderr].count(None) >= 2): + stdout = None + stderr = None + if self.stdin: + self._stdin_write(input) + elif self.stdout: + stdout = self.stdout.read() + self.stdout.close() + elif self.stderr: + stderr = self.stderr.read() + self.stderr.close() + self.wait() + else: + if timeout is not None: + endtime = _time() + timeout + else: + endtime = None + + try: + stdout, stderr = self._communicate(input, endtime, timeout) + except KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # See the detailed comment in .wait(). + if timeout is not None: + sigint_timeout = min(self._sigint_wait_secs, + self._remaining_time(endtime)) + else: + sigint_timeout = self._sigint_wait_secs + self._sigint_wait_secs = 0 # nothing else should wait. + try: + self._wait(timeout=sigint_timeout) + except TimeoutExpired: + pass + raise # resume the KeyboardInterrupt + + finally: + self._communication_started = True + + sts = self.wait(timeout=self._remaining_time(endtime)) + + return (stdout, stderr) + + + def poll(self): + """Check if child process has terminated. Set and return returncode + attribute.""" + return self._internal_poll() + + + def _remaining_time(self, endtime): + """Convenience for _communicate when computing timeouts.""" + if endtime is None: + return None + else: + return endtime - _time() + + def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq, skip_check_and_raise=False): - """Convenience for checking if a timeout has expired.""" - if endtime is None: - return + """Convenience for checking if a timeout has expired.""" + if endtime is None: + return if skip_check_and_raise or _time() > endtime: raise TimeoutExpired( self.args, orig_timeout, output=b''.join(stdout_seq) if stdout_seq else None, stderr=b''.join(stderr_seq) if stderr_seq else None) - - - def wait(self, timeout=None): - """Wait for child process to terminate; returns self.returncode.""" - if timeout is not None: - endtime = _time() + timeout - try: - return self._wait(timeout=timeout) - except KeyboardInterrupt: - # https://bugs.python.org/issue25942 - # The first keyboard interrupt waits briefly for the child to - # exit under the common assumption that it also received the ^C - # generated SIGINT and will exit rapidly. - if timeout is not None: - sigint_timeout = min(self._sigint_wait_secs, - self._remaining_time(endtime)) - else: - sigint_timeout = self._sigint_wait_secs - self._sigint_wait_secs = 0 # nothing else should wait. - try: - self._wait(timeout=sigint_timeout) - except TimeoutExpired: - pass - raise # resume the KeyboardInterrupt - + + + def wait(self, timeout=None): + """Wait for child process to terminate; returns self.returncode.""" + if timeout is not None: + endtime = _time() + timeout + try: + return self._wait(timeout=timeout) + except KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # The first keyboard interrupt waits briefly for the child to + # exit under the common assumption that it also received the ^C + # generated SIGINT and will exit rapidly. + if timeout is not None: + sigint_timeout = min(self._sigint_wait_secs, + self._remaining_time(endtime)) + else: + sigint_timeout = self._sigint_wait_secs + self._sigint_wait_secs = 0 # nothing else should wait. + try: + self._wait(timeout=sigint_timeout) + except TimeoutExpired: + pass + raise # resume the KeyboardInterrupt + def _close_pipe_fds(self, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): # self._devnull is not always defined. devnull_fd = getattr(self, '_devnull', None) - + with contextlib.ExitStack() as stack: if _mswindows: if p2cread != -1: @@ -1233,118 +1233,118 @@ class Popen(object): # Prevent a double close of these handles/fds from __init__ on error. self._closed_child_pipe_fds = True - if _mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tuple with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin is None and stdout is None and stderr is None: - return (-1, -1, -1, -1, -1, -1) - - p2cread, p2cwrite = -1, -1 - c2pread, c2pwrite = -1, -1 - errread, errwrite = -1, -1 - - if stdin is None: - p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE) - if p2cread is None: - p2cread, _ = _winapi.CreatePipe(None, 0) - p2cread = Handle(p2cread) - _winapi.CloseHandle(_) - elif stdin == PIPE: - p2cread, p2cwrite = _winapi.CreatePipe(None, 0) - p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite) - elif stdin == DEVNULL: - p2cread = msvcrt.get_osfhandle(self._get_devnull()) - elif isinstance(stdin, int): - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout is None: - c2pwrite = _winapi.GetStdHandle(_winapi.STD_OUTPUT_HANDLE) - if c2pwrite is None: - _, c2pwrite = _winapi.CreatePipe(None, 0) - c2pwrite = Handle(c2pwrite) - _winapi.CloseHandle(_) - elif stdout == PIPE: - c2pread, c2pwrite = _winapi.CreatePipe(None, 0) - c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite) - elif stdout == DEVNULL: - c2pwrite = msvcrt.get_osfhandle(self._get_devnull()) - elif isinstance(stdout, int): - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr is None: - errwrite = _winapi.GetStdHandle(_winapi.STD_ERROR_HANDLE) - if errwrite is None: - _, errwrite = _winapi.CreatePipe(None, 0) - errwrite = Handle(errwrite) - _winapi.CloseHandle(_) - elif stderr == PIPE: - errread, errwrite = _winapi.CreatePipe(None, 0) - errread, errwrite = Handle(errread), Handle(errwrite) - 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: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - h = _winapi.DuplicateHandle( - _winapi.GetCurrentProcess(), handle, - _winapi.GetCurrentProcess(), 0, 1, - _winapi.DUPLICATE_SAME_ACCESS) - return Handle(h) - - - def _filter_handle_list(self, handle_list): - """Filter out console handles that can't be used - in lpAttributeList["handle_list"] and make sure the list - isn't empty. This also removes duplicate handles.""" - # An handle with it's lowest two bits set might be a special console - # handle that if passed in lpAttributeList["handle_list"], will - # cause it to fail. - return list({handle for handle in handle_list - if handle & 0x3 != 0x3 - or _winapi.GetFileType(handle) != - _winapi.FILE_TYPE_CHAR}) - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - pass_fds, cwd, env, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite, + if _mswindows: + # + # Windows methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tuple with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + if stdin is None and stdout is None and stderr is None: + return (-1, -1, -1, -1, -1, -1) + + p2cread, p2cwrite = -1, -1 + c2pread, c2pwrite = -1, -1 + errread, errwrite = -1, -1 + + if stdin is None: + p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE) + if p2cread is None: + p2cread, _ = _winapi.CreatePipe(None, 0) + p2cread = Handle(p2cread) + _winapi.CloseHandle(_) + elif stdin == PIPE: + p2cread, p2cwrite = _winapi.CreatePipe(None, 0) + p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite) + elif stdin == DEVNULL: + p2cread = msvcrt.get_osfhandle(self._get_devnull()) + elif isinstance(stdin, int): + p2cread = msvcrt.get_osfhandle(stdin) + else: + # Assuming file-like object + p2cread = msvcrt.get_osfhandle(stdin.fileno()) + p2cread = self._make_inheritable(p2cread) + + if stdout is None: + c2pwrite = _winapi.GetStdHandle(_winapi.STD_OUTPUT_HANDLE) + if c2pwrite is None: + _, c2pwrite = _winapi.CreatePipe(None, 0) + c2pwrite = Handle(c2pwrite) + _winapi.CloseHandle(_) + elif stdout == PIPE: + c2pread, c2pwrite = _winapi.CreatePipe(None, 0) + c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite) + elif stdout == DEVNULL: + c2pwrite = msvcrt.get_osfhandle(self._get_devnull()) + elif isinstance(stdout, int): + c2pwrite = msvcrt.get_osfhandle(stdout) + else: + # Assuming file-like object + c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) + c2pwrite = self._make_inheritable(c2pwrite) + + if stderr is None: + errwrite = _winapi.GetStdHandle(_winapi.STD_ERROR_HANDLE) + if errwrite is None: + _, errwrite = _winapi.CreatePipe(None, 0) + errwrite = Handle(errwrite) + _winapi.CloseHandle(_) + elif stderr == PIPE: + errread, errwrite = _winapi.CreatePipe(None, 0) + errread, errwrite = Handle(errread), Handle(errwrite) + 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: + # Assuming file-like object + errwrite = msvcrt.get_osfhandle(stderr.fileno()) + errwrite = self._make_inheritable(errwrite) + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _make_inheritable(self, handle): + """Return a duplicate of handle, which is inheritable""" + h = _winapi.DuplicateHandle( + _winapi.GetCurrentProcess(), handle, + _winapi.GetCurrentProcess(), 0, 1, + _winapi.DUPLICATE_SAME_ACCESS) + return Handle(h) + + + def _filter_handle_list(self, handle_list): + """Filter out console handles that can't be used + in lpAttributeList["handle_list"] and make sure the list + isn't empty. This also removes duplicate handles.""" + # An handle with it's lowest two bits set might be a special console + # handle that if passed in lpAttributeList["handle_list"], will + # cause it to fail. + return list({handle for handle in handle_list + if handle & 0x3 != 0x3 + or _winapi.GetFileType(handle) != + _winapi.FILE_TYPE_CHAR}) + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, unused_restore_signals, unused_gid, unused_gids, unused_uid, unused_umask, unused_start_new_session): - """Execute program (MS Windows version)""" - - assert not pass_fds, "pass_fds not supported on Windows." - + """Execute program (MS Windows version)""" + + assert not pass_fds, "pass_fds not supported on Windows." + if isinstance(args, str): pass elif isinstance(args, bytes): @@ -1357,268 +1357,268 @@ class Popen(object): 'shell is true') args = list2cmdline([args]) else: - args = list2cmdline(args) - + args = list2cmdline(args) + if executable is not None: executable = os.fsdecode(executable) - # Process startup details - if startupinfo is None: - startupinfo = STARTUPINFO() - else: - # bpo-34044: Copy STARTUPINFO since it is modified above, - # so the caller can reuse it multiple times. + # Process startup details + if startupinfo is None: + startupinfo = STARTUPINFO() + else: + # bpo-34044: Copy STARTUPINFO since it is modified above, + # so the caller can reuse it multiple times. startupinfo = startupinfo.copy() - - use_std_handles = -1 not in (p2cread, c2pwrite, errwrite) - if use_std_handles: - startupinfo.dwFlags |= _winapi.STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - attribute_list = startupinfo.lpAttributeList - have_handle_list = bool(attribute_list and - "handle_list" in attribute_list and - attribute_list["handle_list"]) - - # If we were given an handle_list or need to create one - if have_handle_list or (use_std_handles and close_fds): - if attribute_list is None: - attribute_list = startupinfo.lpAttributeList = {} - handle_list = attribute_list["handle_list"] = \ - list(attribute_list.get("handle_list", [])) - - if use_std_handles: - handle_list += [int(p2cread), int(c2pwrite), int(errwrite)] - - handle_list[:] = self._filter_handle_list(handle_list) - - if handle_list: - if not close_fds: - warnings.warn("startupinfo.lpAttributeList['handle_list'] " - "overriding close_fds", RuntimeWarning) - - # When using the handle_list we always request to inherit - # handles but the only handles that will be inherited are - # the ones in the handle_list - close_fds = False - - if shell: - startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = _winapi.SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = '{} /c "{}"'.format (comspec, args) - + + use_std_handles = -1 not in (p2cread, c2pwrite, errwrite) + if use_std_handles: + startupinfo.dwFlags |= _winapi.STARTF_USESTDHANDLES + startupinfo.hStdInput = p2cread + startupinfo.hStdOutput = c2pwrite + startupinfo.hStdError = errwrite + + attribute_list = startupinfo.lpAttributeList + have_handle_list = bool(attribute_list and + "handle_list" in attribute_list and + attribute_list["handle_list"]) + + # If we were given an handle_list or need to create one + if have_handle_list or (use_std_handles and close_fds): + if attribute_list is None: + attribute_list = startupinfo.lpAttributeList = {} + handle_list = attribute_list["handle_list"] = \ + list(attribute_list.get("handle_list", [])) + + if use_std_handles: + handle_list += [int(p2cread), int(c2pwrite), int(errwrite)] + + handle_list[:] = self._filter_handle_list(handle_list) + + if handle_list: + if not close_fds: + warnings.warn("startupinfo.lpAttributeList['handle_list'] " + "overriding close_fds", RuntimeWarning) + + # When using the handle_list we always request to inherit + # handles but the only handles that will be inherited are + # the ones in the handle_list + close_fds = False + + if shell: + startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = _winapi.SW_HIDE + comspec = os.environ.get("COMSPEC", "cmd.exe") + args = '{} /c "{}"'.format (comspec, args) + if cwd is not None: cwd = os.fsdecode(cwd) sys.audit("subprocess.Popen", executable, args, cwd, env) - # Start the process - try: - hp, ht, pid, tid = _winapi.CreateProcess(executable, args, - # no special security - None, None, - int(not close_fds), - creationflags, - env, + # Start the process + try: + hp, ht, pid, tid = _winapi.CreateProcess(executable, args, + # no special security + None, None, + int(not close_fds), + creationflags, + env, cwd, - startupinfo) - finally: - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. + startupinfo) + finally: + # Child is launched. Close the parent's copy of those pipe + # handles that only the child should have open. You need + # to make sure that no handles to the write end of the + # output pipe are maintained in this process or else the + # pipe will not close when the child process exits and the + # ReadFile will hang. self._close_pipe_fds(p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) - - # Retain the process handle, but close the thread handle - self._child_created = True - self._handle = Handle(hp) - self.pid = pid - _winapi.CloseHandle(ht) - - def _internal_poll(self, _deadstate=None, - _WaitForSingleObject=_winapi.WaitForSingleObject, - _WAIT_OBJECT_0=_winapi.WAIT_OBJECT_0, - _GetExitCodeProcess=_winapi.GetExitCodeProcess): - """Check if child process has terminated. Returns returncode - attribute. - - This method is called by __del__, so it can only refer to objects - in its local scope. - - """ - if self.returncode is None: - if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0: - self.returncode = _GetExitCodeProcess(self._handle) - return self.returncode - - - def _wait(self, timeout): - """Internal implementation of wait() on Windows.""" - if timeout is None: - timeout_millis = _winapi.INFINITE - else: - timeout_millis = int(timeout * 1000) - if self.returncode is None: - # API note: Returns immediately if timeout_millis == 0. - result = _winapi.WaitForSingleObject(self._handle, - timeout_millis) - if result == _winapi.WAIT_TIMEOUT: - raise TimeoutExpired(self.args, timeout) - self.returncode = _winapi.GetExitCodeProcess(self._handle) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - fh.close() - - - 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"): - self._stdout_buff = [] - self.stdout_thread = \ - threading.Thread(target=self._readerthread, - args=(self.stdout, self._stdout_buff)) - self.stdout_thread.daemon = True - self.stdout_thread.start() - if self.stderr and not hasattr(self, "_stderr_buff"): - self._stderr_buff = [] - self.stderr_thread = \ - threading.Thread(target=self._readerthread, - args=(self.stderr, self._stderr_buff)) - self.stderr_thread.daemon = True - self.stderr_thread.start() - - if self.stdin: - 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 - # calls communicate again. - if self.stdout is not None: - self.stdout_thread.join(self._remaining_time(endtime)) - if self.stdout_thread.is_alive(): - raise TimeoutExpired(self.args, orig_timeout) - if self.stderr is not None: - self.stderr_thread.join(self._remaining_time(endtime)) - if self.stderr_thread.is_alive(): - raise TimeoutExpired(self.args, orig_timeout) - - # Collect the output from and close both pipes, now that we know - # both have been read successfully. - stdout = None - stderr = None - if self.stdout: - stdout = self._stdout_buff - self.stdout.close() - if self.stderr: - stderr = self._stderr_buff - self.stderr.close() - - # All data exchanged. Translate lists into strings. + + # Retain the process handle, but close the thread handle + self._child_created = True + self._handle = Handle(hp) + self.pid = pid + _winapi.CloseHandle(ht) + + def _internal_poll(self, _deadstate=None, + _WaitForSingleObject=_winapi.WaitForSingleObject, + _WAIT_OBJECT_0=_winapi.WAIT_OBJECT_0, + _GetExitCodeProcess=_winapi.GetExitCodeProcess): + """Check if child process has terminated. Returns returncode + attribute. + + This method is called by __del__, so it can only refer to objects + in its local scope. + + """ + if self.returncode is None: + if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0: + self.returncode = _GetExitCodeProcess(self._handle) + return self.returncode + + + def _wait(self, timeout): + """Internal implementation of wait() on Windows.""" + if timeout is None: + timeout_millis = _winapi.INFINITE + else: + timeout_millis = int(timeout * 1000) + if self.returncode is None: + # API note: Returns immediately if timeout_millis == 0. + result = _winapi.WaitForSingleObject(self._handle, + timeout_millis) + if result == _winapi.WAIT_TIMEOUT: + raise TimeoutExpired(self.args, timeout) + self.returncode = _winapi.GetExitCodeProcess(self._handle) + return self.returncode + + + def _readerthread(self, fh, buffer): + buffer.append(fh.read()) + fh.close() + + + 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"): + self._stdout_buff = [] + self.stdout_thread = \ + threading.Thread(target=self._readerthread, + args=(self.stdout, self._stdout_buff)) + self.stdout_thread.daemon = True + self.stdout_thread.start() + if self.stderr and not hasattr(self, "_stderr_buff"): + self._stderr_buff = [] + self.stderr_thread = \ + threading.Thread(target=self._readerthread, + args=(self.stderr, self._stderr_buff)) + self.stderr_thread.daemon = True + self.stderr_thread.start() + + if self.stdin: + 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 + # calls communicate again. + if self.stdout is not None: + self.stdout_thread.join(self._remaining_time(endtime)) + if self.stdout_thread.is_alive(): + raise TimeoutExpired(self.args, orig_timeout) + if self.stderr is not None: + self.stderr_thread.join(self._remaining_time(endtime)) + if self.stderr_thread.is_alive(): + raise TimeoutExpired(self.args, orig_timeout) + + # Collect the output from and close both pipes, now that we know + # both have been read successfully. + stdout = None + stderr = None + if self.stdout: + stdout = self._stdout_buff + self.stdout.close() + if self.stderr: + stderr = self._stderr_buff + self.stderr.close() + + # All data exchanged. Translate lists into strings. stdout = stdout[0] if stdout else None stderr = stderr[0] if stderr else None - - return (stdout, stderr) - - def send_signal(self, sig): - """Send a signal to the process.""" - # Don't signal a process that we know has already died. - if self.returncode is not None: - return - if sig == signal.SIGTERM: - self.terminate() - elif sig == signal.CTRL_C_EVENT: - os.kill(self.pid, signal.CTRL_C_EVENT) - elif sig == signal.CTRL_BREAK_EVENT: - os.kill(self.pid, signal.CTRL_BREAK_EVENT) - else: - raise ValueError("Unsupported signal: {}".format(sig)) - - def terminate(self): - """Terminates the process.""" - # Don't terminate a process that we know has already died. - if self.returncode is not None: - return - try: - _winapi.TerminateProcess(self._handle, 1) - except PermissionError: - # ERROR_ACCESS_DENIED (winerror 5) is received when the - # process already died. - rc = _winapi.GetExitCodeProcess(self._handle) - if rc == _winapi.STILL_ACTIVE: - raise - self.returncode = rc - - kill = terminate - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tuple with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = -1, -1 - c2pread, c2pwrite = -1, -1 - errread, errwrite = -1, -1 - - if stdin is None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif stdin == DEVNULL: - p2cread = self._get_devnull() - elif isinstance(stdin, int): - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout is None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif stdout == DEVNULL: - c2pwrite = self._get_devnull() - elif isinstance(stdout, int): - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr is None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - if c2pwrite != -1: - errwrite = c2pwrite - else: # child's stdout is not set, use parent's stdout - errwrite = sys.__stdout__.fileno() - elif stderr == DEVNULL: - errwrite = self._get_devnull() - elif isinstance(stderr, int): - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - + + return (stdout, stderr) + + def send_signal(self, sig): + """Send a signal to the process.""" + # Don't signal a process that we know has already died. + if self.returncode is not None: + return + if sig == signal.SIGTERM: + self.terminate() + elif sig == signal.CTRL_C_EVENT: + os.kill(self.pid, signal.CTRL_C_EVENT) + elif sig == signal.CTRL_BREAK_EVENT: + os.kill(self.pid, signal.CTRL_BREAK_EVENT) + else: + raise ValueError("Unsupported signal: {}".format(sig)) + + def terminate(self): + """Terminates the process.""" + # Don't terminate a process that we know has already died. + if self.returncode is not None: + return + try: + _winapi.TerminateProcess(self._handle, 1) + except PermissionError: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + rc = _winapi.GetExitCodeProcess(self._handle) + if rc == _winapi.STILL_ACTIVE: + raise + self.returncode = rc + + kill = terminate + + else: + # + # POSIX methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tuple with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + p2cread, p2cwrite = -1, -1 + c2pread, c2pwrite = -1, -1 + errread, errwrite = -1, -1 + + if stdin is None: + pass + elif stdin == PIPE: + p2cread, p2cwrite = os.pipe() + elif stdin == DEVNULL: + p2cread = self._get_devnull() + elif isinstance(stdin, int): + p2cread = stdin + else: + # Assuming file-like object + p2cread = stdin.fileno() + + if stdout is None: + pass + elif stdout == PIPE: + c2pread, c2pwrite = os.pipe() + elif stdout == DEVNULL: + c2pwrite = self._get_devnull() + elif isinstance(stdout, int): + c2pwrite = stdout + else: + # Assuming file-like object + c2pwrite = stdout.fileno() + + if stderr is None: + pass + elif stderr == PIPE: + errread, errwrite = os.pipe() + elif stderr == STDOUT: + if c2pwrite != -1: + errwrite = c2pwrite + else: # child's stdout is not set, use parent's stdout + errwrite = sys.__stdout__.fileno() + elif stderr == DEVNULL: + errwrite = self._get_devnull() + elif isinstance(stderr, int): + errwrite = stderr + else: + # Assuming file-like object + errwrite = stderr.fileno() + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + def _posix_spawn(self, args, executable, env, restore_signals, p2cread, p2cwrite, c2pread, c2pwrite, @@ -1658,37 +1658,37 @@ class Popen(object): c2pread, c2pwrite, errread, errwrite) - def _execute_child(self, args, executable, preexec_fn, close_fds, - pass_fds, cwd, env, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite, + def _execute_child(self, args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session): - """Execute program (POSIX version)""" - - if isinstance(args, (str, bytes)): - args = [args] + """Execute program (POSIX version)""" + + if isinstance(args, (str, bytes)): + args = [args] elif isinstance(args, os.PathLike): if shell: raise TypeError('path-like args is not allowed when ' 'shell is true') args = [args] - else: - args = list(args) - - if shell: - # On Android the default shell is at '/system/bin/sh'. - unix_shell = ('/system/bin/sh' if - hasattr(sys, 'getandroidapilevel') else '/bin/sh') - args = [unix_shell, "-c"] + args - if executable: - args[0] = executable - - if executable is None: - executable = args[0] + else: + args = list(args) + + if shell: + # On Android the default shell is at '/system/bin/sh'. + unix_shell = ('/system/bin/sh' if + hasattr(sys, 'getandroidapilevel') else '/bin/sh') + args = [unix_shell, "-c"] + args + if executable: + args[0] = executable + + if executable is None: + executable = args[0] sys.audit("subprocess.Popen", executable, args, cwd, env) @@ -1712,333 +1712,333 @@ class Popen(object): errread, errwrite) return - orig_executable = executable - - # 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 = 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 - # malloc or free in the child process to avoid - # potential deadlocks, thus we do all this here. - # and pass it to fork_exec() - - if env is not None: - env_list = [] - for k, v in env.items(): - k = os.fsencode(k) - if b'=' in k: - raise ValueError("illegal environment variable name") - env_list.append(k + b'=' + os.fsencode(v)) - else: - env_list = None # Use execv instead of execve. - executable = os.fsencode(executable) - if os.path.dirname(executable): - executable_list = (executable,) - else: - # This matches the behavior of os._execvpe(). - executable_list = tuple( - os.path.join(os.fsencode(dir), executable) - for dir in os.get_exec_path(env)) - fds_to_keep = set(pass_fds) - fds_to_keep.add(errpipe_write) - self.pid = _posixsubprocess.fork_exec( - args, executable_list, - close_fds, tuple(sorted(map(int, fds_to_keep))), - cwd, env_list, - p2cread, p2cwrite, c2pread, c2pwrite, - errread, errwrite, - errpipe_read, errpipe_write, + orig_executable = executable + + # 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 = 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 + # malloc or free in the child process to avoid + # potential deadlocks, thus we do all this here. + # and pass it to fork_exec() + + if env is not None: + env_list = [] + for k, v in env.items(): + k = os.fsencode(k) + if b'=' in k: + raise ValueError("illegal environment variable name") + env_list.append(k + b'=' + os.fsencode(v)) + else: + env_list = None # Use execv instead of execve. + executable = os.fsencode(executable) + if os.path.dirname(executable): + executable_list = (executable,) + else: + # This matches the behavior of os._execvpe(). + executable_list = tuple( + os.path.join(os.fsencode(dir), executable) + for dir in os.get_exec_path(env)) + fds_to_keep = set(pass_fds) + fds_to_keep.add(errpipe_write) + self.pid = _posixsubprocess.fork_exec( + args, executable_list, + close_fds, tuple(sorted(map(int, fds_to_keep))), + cwd, env_list, + p2cread, p2cwrite, c2pread, c2pwrite, + errread, errwrite, + errpipe_read, errpipe_write, restore_signals, start_new_session, gid, gids, uid, umask, preexec_fn) - self._child_created = True - finally: - # be sure the FD is closed no matter what - os.close(errpipe_write) - + self._child_created = True + finally: + # be sure the FD is closed no matter what + os.close(errpipe_write) + self._close_pipe_fds(p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) - - # Wait for exec to fail or succeed; possibly raising an - # exception (limited in size) - errpipe_data = bytearray() - while True: - part = os.read(errpipe_read, 50000) - errpipe_data += part - if not part or len(errpipe_data) > 50000: - break - finally: - # be sure the FD is closed no matter what - os.close(errpipe_read) - - if errpipe_data: - try: - 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)) - # The encoding here should match the encoding - # written in by the subprocess implementations - # like _posixsubprocess - err_msg = err_msg.decode() - except ValueError: - exception_name = b'SubprocessError' - hex_errno = b'0' - err_msg = 'Bad exception data from child: {!r}'.format( - bytes(errpipe_data)) - child_exception_type = getattr( - builtins, exception_name.decode('ascii'), - SubprocessError) - if issubclass(child_exception_type, OSError) and hex_errno: - errno_num = int(hex_errno, 16) - child_exec_never_called = (err_msg == "noexec") - if child_exec_never_called: - err_msg = "" - # The error must be from chdir(cwd). - err_filename = cwd - else: - err_filename = orig_executable - if errno_num != 0: - err_msg = os.strerror(errno_num) - raise child_exception_type(errno_num, err_msg, err_filename) - raise child_exception_type(err_msg) - - + + # Wait for exec to fail or succeed; possibly raising an + # exception (limited in size) + errpipe_data = bytearray() + while True: + part = os.read(errpipe_read, 50000) + errpipe_data += part + if not part or len(errpipe_data) > 50000: + break + finally: + # be sure the FD is closed no matter what + os.close(errpipe_read) + + if errpipe_data: + try: + 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)) + # The encoding here should match the encoding + # written in by the subprocess implementations + # like _posixsubprocess + err_msg = err_msg.decode() + except ValueError: + exception_name = b'SubprocessError' + hex_errno = b'0' + err_msg = 'Bad exception data from child: {!r}'.format( + bytes(errpipe_data)) + child_exception_type = getattr( + builtins, exception_name.decode('ascii'), + SubprocessError) + if issubclass(child_exception_type, OSError) and hex_errno: + errno_num = int(hex_errno, 16) + child_exec_never_called = (err_msg == "noexec") + if child_exec_never_called: + err_msg = "" + # The error must be from chdir(cwd). + err_filename = cwd + else: + err_filename = orig_executable + if errno_num != 0: + err_msg = os.strerror(errno_num) + raise child_exception_type(errno_num, err_msg, err_filename) + raise child_exception_type(err_msg) + + def _handle_exitstatus(self, sts, waitstatus_to_exitcode=os.waitstatus_to_exitcode, _WIFSTOPPED=os.WIFSTOPPED, _WSTOPSIG=os.WSTOPSIG): - """All callers to this function MUST hold self._waitpid_lock.""" - # This method is called (indirectly) by __del__, so it cannot - # refer to anything outside of its local scope. + """All callers to this function MUST hold self._waitpid_lock.""" + # This method is called (indirectly) by __del__, so it cannot + # refer to anything outside of its local scope. if _WIFSTOPPED(sts): - self.returncode = -_WSTOPSIG(sts) - else: + self.returncode = -_WSTOPSIG(sts) + else: self.returncode = waitstatus_to_exitcode(sts) - - def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, - _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): - """Check if child process has terminated. Returns returncode - attribute. - - This method is called by __del__, so it cannot reference anything - outside of the local scope (nor can any methods it calls). - - """ - if self.returncode is None: - if not self._waitpid_lock.acquire(False): - # Something else is busy calling waitpid. Don't allow two - # at once. We know nothing yet. - return None - try: - if self.returncode is not None: - return self.returncode # Another thread waited. - pid, sts = _waitpid(self.pid, _WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except OSError as e: - if _deadstate is not None: - self.returncode = _deadstate - elif e.errno == _ECHILD: - # 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. - # http://bugs.python.org/issue15756 - self.returncode = 0 - finally: - self._waitpid_lock.release() - return self.returncode - - - def _try_wait(self, wait_flags): - """All callers to this function MUST hold self._waitpid_lock.""" - try: - (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 - # process. This child is dead, we can't get the status. - pid = self.pid - sts = 0 - return (pid, sts) - - - def _wait(self, timeout): - """Internal implementation of wait() on POSIX.""" - if self.returncode is not None: - return self.returncode - - if timeout is not None: - endtime = _time() + timeout - # 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 - while True: - if self._waitpid_lock.acquire(False): - try: - if self.returncode is not None: - break # Another thread waited. - (pid, sts) = self._try_wait(os.WNOHANG) - assert pid == self.pid or pid == 0 - if pid == self.pid: - self._handle_exitstatus(sts) - break - finally: - self._waitpid_lock.release() - remaining = self._remaining_time(endtime) - if remaining <= 0: - raise TimeoutExpired(self.args, timeout) - delay = min(delay * 2, remaining, .05) - time.sleep(delay) - else: - while self.returncode is None: - with self._waitpid_lock: - if self.returncode is not None: - break # Another thread waited. - (pid, sts) = self._try_wait(0) - # Check the pid and loop as waitpid has been known to - # return 0 even without WNOHANG in odd situations. - # http://bugs.python.org/issue14396. - if pid == self.pid: - self._handle_exitstatus(sts) - return self.returncode - - - 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. - try: - self.stdin.flush() - except BrokenPipeError: - pass # communicate() must ignore BrokenPipeError. - if not input: - try: - self.stdin.close() - except BrokenPipeError: - pass # communicate() must ignore BrokenPipeError. - - 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) + + def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, + _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): + """Check if child process has terminated. Returns returncode + attribute. + + This method is called by __del__, so it cannot reference anything + outside of the local scope (nor can any methods it calls). + + """ + if self.returncode is None: + if not self._waitpid_lock.acquire(False): + # Something else is busy calling waitpid. Don't allow two + # at once. We know nothing yet. + return None + try: + if self.returncode is not None: + return self.returncode # Another thread waited. + pid, sts = _waitpid(self.pid, _WNOHANG) + if pid == self.pid: + self._handle_exitstatus(sts) + except OSError as e: + if _deadstate is not None: + self.returncode = _deadstate + elif e.errno == _ECHILD: + # 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. + # http://bugs.python.org/issue15756 + self.returncode = 0 + finally: + self._waitpid_lock.release() + return self.returncode + + + def _try_wait(self, wait_flags): + """All callers to this function MUST hold self._waitpid_lock.""" + try: + (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 + # process. This child is dead, we can't get the status. + pid = self.pid + sts = 0 + return (pid, sts) + + + def _wait(self, timeout): + """Internal implementation of wait() on POSIX.""" + if self.returncode is not None: + return self.returncode + + if timeout is not None: + endtime = _time() + timeout + # 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 + while True: + if self._waitpid_lock.acquire(False): + try: + if self.returncode is not None: + break # Another thread waited. + (pid, sts) = self._try_wait(os.WNOHANG) + assert pid == self.pid or pid == 0 + if pid == self.pid: + self._handle_exitstatus(sts) + break + finally: + self._waitpid_lock.release() + remaining = self._remaining_time(endtime) + if remaining <= 0: + raise TimeoutExpired(self.args, timeout) + delay = min(delay * 2, remaining, .05) + time.sleep(delay) + else: + while self.returncode is None: + with self._waitpid_lock: + if self.returncode is not None: + break # Another thread waited. + (pid, sts) = self._try_wait(0) + # Check the pid and loop as waitpid has been known to + # return 0 even without WNOHANG in odd situations. + # http://bugs.python.org/issue14396. + if pid == self.pid: + self._handle_exitstatus(sts) + return self.returncode + + + 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. + try: + self.stdin.flush() + except BrokenPipeError: + pass # communicate() must ignore BrokenPipeError. + if not input: + try: + self.stdin.close() + except BrokenPipeError: + pass # communicate() must ignore BrokenPipeError. + + 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 and not self.stdout.closed: - selector.register(self.stdout, selectors.EVENT_READ) + selector.register(self.stdout, selectors.EVENT_READ) if self.stderr and not self.stderr.closed: - selector.register(self.stderr, selectors.EVENT_READ) - - while selector.get_map(): - timeout = self._remaining_time(endtime) - if timeout is not None and timeout < 0: + selector.register(self.stderr, selectors.EVENT_READ) + + while selector.get_map(): + timeout = self._remaining_time(endtime) + if timeout is not None and timeout < 0: self._check_timeout(endtime, orig_timeout, stdout, stderr, skip_check_and_raise=True) raise RuntimeError( # Impossible :) '_check_timeout(..., skip_check_and_raise=True) ' 'failed to raise TimeoutExpired.') - - ready = selector.select(timeout) + + ready = selector.select(timeout) self._check_timeout(endtime, orig_timeout, stdout, stderr) - - # 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 BrokenPipeError: - selector.unregister(key.fileobj) - key.fileobj.close() - 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)) - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = b''.join(stdout) - if stderr is not None: - stderr = b''.join(stderr) - - # Translate newlines, if requested. - # This also turns bytes into strings. - if self.text_mode: - if stdout is not None: - stdout = self._translate_newlines(stdout, - self.stdout.encoding, - self.stdout.errors) - if stderr is not None: - stderr = self._translate_newlines(stderr, - self.stderr.encoding, - self.stderr.errors) - - return (stdout, stderr) - - - def _save_input(self, input): - # This method is called from the _communicate_with_*() methods - # so that if we time out while communicating, we can continue - # sending input if we retry. - if self.stdin and self._input is None: - self._input_offset = 0 - self._input = input - if input is not None and self.text_mode: - self._input = self._input.encode(self.stdin.encoding, - self.stdin.errors) - - - def send_signal(self, sig): - """Send a signal to the process.""" + + # 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 BrokenPipeError: + selector.unregister(key.fileobj) + key.fileobj.close() + 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)) + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = b''.join(stdout) + if stderr is not None: + stderr = b''.join(stderr) + + # Translate newlines, if requested. + # This also turns bytes into strings. + if self.text_mode: + if stdout is not None: + stdout = self._translate_newlines(stdout, + self.stdout.encoding, + self.stdout.errors) + if stderr is not None: + stderr = self._translate_newlines(stderr, + self.stderr.encoding, + self.stderr.errors) + + return (stdout, stderr) + + + def _save_input(self, input): + # This method is called from the _communicate_with_*() methods + # so that if we time out while communicating, we can continue + # sending input if we retry. + if self.stdin and self._input is None: + self._input_offset = 0 + self._input = input + if input is not None and self.text_mode: + self._input = self._input.encode(self.stdin.encoding, + self.stdin.errors) + + + def send_signal(self, sig): + """Send a signal to the process.""" # bpo-38630: Polling reduces the risk of sending a signal to the # wrong process if the process completed, the Popen.returncode # attribute is still None, and the pid has been reassigned @@ -2064,17 +2064,17 @@ class Popen(object): # described above happens between the returncode test # and the kill() call. try: - os.kill(self.pid, sig) + os.kill(self.pid, sig) except ProcessLookupError: # Supress the race condition error; bpo-40550. pass - - def terminate(self): - """Terminate the process with SIGTERM - """ - self.send_signal(signal.SIGTERM) - - def kill(self): - """Kill the process with SIGKILL - """ - self.send_signal(signal.SIGKILL) + + def terminate(self): + """Terminate the process with SIGTERM + """ + self.send_signal(signal.SIGTERM) + + def kill(self): + """Kill the process with SIGKILL + """ + self.send_signal(signal.SIGKILL) |