From 2ccba05e96cbef633efc9ad486b353790c694292 Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Wed, 10 Jan 2018 17:24:35 -0600 Subject: [PATCH 1/6] bpo-32102: Add "capture_output=True" option to subprocess.run --- Lib/subprocess.py | 10 +++++++++- Lib/test/test_subprocess.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index db6342fa491239..d7437fab8a88e7 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -409,7 +409,8 @@ def check_returncode(self): self.stderr) -def run(*popenargs, input=None, timeout=None, check=False, **kwargs): +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 @@ -442,6 +443,13 @@ def run(*popenargs, input=None, timeout=None, check=False, **kwargs): raise ValueError('stdin and input arguments may not both be used.') kwargs['stdin'] = PIPE + if capture_output: + if ('stdout' in kwargs) or ('stderr' in kwargs): + 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) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 540ad34d3f5ea0..058d6207e7b2cf 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1476,6 +1476,38 @@ def test_run_kwargs(self): env=newenv) self.assertEqual(cp.returncode, 33) + def test_capture_output(self): + cp = self.run_python(("import sys;" + "sys.stdout.write('BDFL'); " + "sys.stderr.write('FLUFL')"), + capture_output=True) + self.assertIn(b'BDFL', cp.stdout) + self.assertIn(b'FLUFL', cp.stderr) + + def test_stdout_with_capture_output_arg(self): + # run() refuses to accept 'stdout' with 'capture_output' + tf = tempfile.TemporaryFile() + self.addCleanup(tf.close) + with self.assertRaises(ValueError, + msg=("Expected ValueError when stdout and capture_output " + "args supplied.")) as c: + output = self.run_python("print('will not be run')", + capture_output=True, stdout=tf) + self.assertIn('stdout', c.exception.args[0]) + self.assertIn('capture_output', c.exception.args[0]) + + def test_stderr_with_capture_output_arg(self): + # run() refuses to accept 'stderr' with 'capture_output' + tf = tempfile.TemporaryFile() + self.addCleanup(tf.close) + with self.assertRaises(ValueError, + msg=("Expected ValueError when stderr and capture_output " + "args supplied.")) as c: + output = self.run_python("print('will not be run')", + capture_output=True, stderr=tf) + self.assertIn('stderr', c.exception.args[0]) + self.assertIn('capture_output', c.exception.args[0]) + @unittest.skipIf(mswindows, "POSIX specific tests") class POSIXProcessTestCase(BaseTestCase): From 5761d005983acdcf76afb2c984657b74ca2b7cbd Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Wed, 10 Jan 2018 18:24:13 -0600 Subject: [PATCH 2/6] bpo-32102: Document subprocess.run's capture_output option --- Doc/library/subprocess.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index af96f41ef40a33..69038f0e14324b 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -47,12 +47,14 @@ compatibility with older versions, see the :ref:`call-function-trio` section. The arguments shown above are merely the most common ones, described below in :ref:`frequently-used-arguments` (hence the use of keyword-only notation in the abbreviated signature). The full function signature is largely the - same as that of the :class:`Popen` constructor - apart from *timeout*, - *input* and *check*, all the arguments to this function are passed through to - that interface. + same as that of the :class:`Popen` constructor - most of the arguments to + this function are passed through to that interface. (*timeout*, *input*, + *check*, and *capture_output* are not.) - This does not capture stdout or stderr by default. To do so, pass - :data:`PIPE` for the *stdout* and/or *stderr* arguments. + If *capture_output* is true, stdout and stderr will be captured. + When used, the internal :class:`Popen` object is automatically created with + ``stdout=PIPE`` and ``stderr=PIPE``. The *stdout* and *stderr* arguments may + not be used as well. The *timeout* argument is passed to :meth:`Popen.communicate`. If the timeout expires, the child process will be killed and waited for. The @@ -86,9 +88,9 @@ compatibility with older versions, see the :ref:`call-function-trio` section. ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1 - >>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE) + >>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True) CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0, - stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n') + stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'') .. versionadded:: 3.5 @@ -98,7 +100,7 @@ compatibility with older versions, see the :ref:`call-function-trio` section. .. versionchanged:: 3.7 - Added the *text* parameter, as a more understandable alias of *universal_newlines* + Added the *text* and *capture_output* parameters .. class:: CompletedProcess From 5c4d29f2973064dcd36c2ad619b655f4a2fba46b Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Wed, 10 Jan 2018 18:05:59 -0600 Subject: [PATCH 3/6] bpo-32102: Add Misc/NEWS and Misc/ACKS entries --- Misc/ACKS | 1 + .../NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst diff --git a/Misc/ACKS b/Misc/ACKS index 009b072d680aac..43937c1c8b1abb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -108,6 +108,7 @@ Michael R Bax Anthony Baxter Mike Bayer Samuel L. Bayer +Bo Bayles Tommy Beadle Donald Beaudry David Beazley diff --git a/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst new file mode 100644 index 00000000000000..cd4d0b5a8bd6f0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst @@ -0,0 +1 @@ +New argument ``capture_output`` for subprocess.run From 76b079dea3d034f6aaedfe810ff801e38896df9e Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Thu, 11 Jan 2018 07:47:15 -0600 Subject: [PATCH 4/6] bpo-32102: Correct the issue reference for Misc/NEWS --- ...-32102.9-CZgD.rst => 2018-01-10-18-04-21.bpo-32248.9-CZgD.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2018-01-10-18-04-21.bpo-32102.9-CZgD.rst => 2018-01-10-18-04-21.bpo-32248.9-CZgD.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32248.9-CZgD.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst rename to Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32248.9-CZgD.rst From 5ea4d772df09806758ca4988837674071eb505f1 Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Thu, 11 Jan 2018 07:49:32 -0600 Subject: [PATCH 5/6] Revert "bpo-32102: Correct the issue reference for Misc/NEWS" This reverts commit 76b079dea3d034f6aaedfe810ff801e38896df9e. --- ...-32248.9-CZgD.rst => 2018-01-10-18-04-21.bpo-32102.9-CZgD.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2018-01-10-18-04-21.bpo-32248.9-CZgD.rst => 2018-01-10-18-04-21.bpo-32102.9-CZgD.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32248.9-CZgD.rst b/Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32248.9-CZgD.rst rename to Misc/NEWS.d/next/Library/2018-01-10-18-04-21.bpo-32102.9-CZgD.rst From 1718b5011f17f16a0e8990d06bc18a2cbd6d963b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 29 Jan 2018 22:12:52 -0800 Subject: [PATCH 6/6] be more verbose in the version added this keeps the old description of *text* and makes the *capture_output* mention its own sentence. --- Doc/library/subprocess.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 69038f0e14324b..86f3e06318f49a 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -100,7 +100,8 @@ compatibility with older versions, see the :ref:`call-function-trio` section. .. versionchanged:: 3.7 - Added the *text* and *capture_output* parameters + Added the *text* parameter, as a more understandable alias of *universal_newlines*. + Added the *capture_output* parameter. .. class:: CompletedProcess