summaryrefslogtreecommitdiff
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
authorandyclegg <andy2.0@gmail.com>2017-10-23 03:01:19 +0100
committerGregory P. Smith <greg@krypto.org>2017-10-22 19:01:19 -0700
commit7fed7bd8bb628f0f09c6011871a4ce68afb41b18 (patch)
treea89c99e634133cae2dc33671182f8f4ae0723226 /Lib/subprocess.py
parentae3087c6382011c47db82fea4d05f8bbf514265d (diff)
downloadcpython-git-7fed7bd8bb628f0f09c6011871a4ce68afb41b18.tar.gz
bpo-31756: subprocess.run should alias universal_newlines to text (#4049)
Improve human friendliness of the Popen API: Add text=False as a keyword-only argument to subprocess.Popen along with a Popen attribute .text_mode and set this based on the encoding/errors/universal_newlines/text arguments. The universal_newlines parameter and attribute are maintained for backwards compatibility.
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py78
1 files changed, 52 insertions, 26 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index dd994e2aaf..c7e568fafe 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -320,8 +320,11 @@ def check_output(*popenargs, timeout=None, **kwargs):
... input=b"when in the course of fooman events\n")
b'when in the course of barman events\n'
- If universal_newlines=True is passed, the "input" argument must be a
- string and the return value will be a string rather than bytes.
+ By default, all communication is in bytes, and therefore any "input"
+ should be bytes, and the return value wil 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.')
@@ -384,15 +387,17 @@ def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
exception will be raised.
There is an optional argument "input", allowing you to
- pass a string to the subprocess's stdin. If you use this argument
+ 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.
- The other arguments are the same as for the Popen constructor.
+ 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.
- If universal_newlines=True is passed, the "input" argument must be a
- string and stdout/stderr in the returned object will be strings rather than
- bytes.
+ The other arguments are the same as for the Popen constructor.
"""
if input is not None:
if 'stdin' in kwargs:
@@ -513,7 +518,7 @@ def getstatusoutput(cmd):
(-15, '')
"""
try:
- data = check_output(cmd, shell=True, universal_newlines=True, stderr=STDOUT)
+ data = check_output(cmd, shell=True, text=True, stderr=STDOUT)
exitcode = 0
except CalledProcessError as ex:
data = ex.output
@@ -565,8 +570,10 @@ class Popen(object):
env: Defines the environment variables for the new process.
- universal_newlines: If true, use universal line endings for file
- objects stdin, stdout and stderr.
+ 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)
@@ -587,10 +594,10 @@ class Popen(object):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
- shell=False, cwd=None, env=None, universal_newlines=False,
+ shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
- pass_fds=(), *, encoding=None, errors=None):
+ pass_fds=(), *, encoding=None, errors=None, text=None):
"""Create new Popen instance."""
_cleanup()
# Held while anything is calling waitpid before returncode has been
@@ -642,10 +649,16 @@ class Popen(object):
self.stderr = None
self.pid = None
self.returncode = None
- self.universal_newlines = universal_newlines
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:
#
@@ -677,25 +690,25 @@ class Popen(object):
if errread != -1:
errread = msvcrt.open_osfhandle(errread.Detach(), 0)
- text_mode = encoding or errors or universal_newlines
+ self.text_mode = encoding or errors or text or universal_newlines
self._closed_child_pipe_fds = False
try:
if p2cwrite != -1:
self.stdin = io.open(p2cwrite, 'wb', bufsize)
- if text_mode:
+ if self.text_mode:
self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
line_buffering=(bufsize == 1),
encoding=encoding, errors=errors)
if c2pread != -1:
self.stdout = io.open(c2pread, 'rb', bufsize)
- if text_mode:
+ 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 text_mode:
+ if self.text_mode:
self.stderr = io.TextIOWrapper(self.stderr,
encoding=encoding, errors=errors)
@@ -735,6 +748,16 @@ class Popen(object):
raise
+ @property
+ def universal_newlines(self):
+ # universal_newlines as retained as an alias of text_mode for API
+ # compatability. 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")
@@ -805,12 +828,16 @@ class Popen(object):
reached. Wait for process to terminate.
The optional "input" argument should be data to be sent to the
- child process (if self.universal_newlines is True, this should
- be a string; if it is False, "input" should be bytes), or
- None, if no data should be sent to the child.
-
- communicate() returns a tuple (stdout, stderr). These will be
- bytes or, if self.universal_newlines was True, a string.
+ 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:
@@ -1533,7 +1560,7 @@ class Popen(object):
# Translate newlines, if requested.
# This also turns bytes into strings.
- if self.encoding or self.errors or self.universal_newlines:
+ if self.text_mode:
if stdout is not None:
stdout = self._translate_newlines(stdout,
self.stdout.encoding,
@@ -1553,8 +1580,7 @@ class Popen(object):
if self.stdin and self._input is None:
self._input_offset = 0
self._input = input
- if input is not None and (
- self.encoding or self.errors or self.universal_newlines):
+ if input is not None and self.text_mode:
self._input = self._input.encode(self.stdin.encoding,
self.stdin.errors)