[Python-checkins] cpython: Issue #16624: `subprocess.check_output` now accepts an `input` argument,

serhiy.storchaka python-checkins at python.org
Mon Apr 22 19:21:53 CEST 2013


http://hg.python.org/cpython/rev/5c45d0ca984f
changeset:   83495:5c45d0ca984f
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Mon Apr 22 20:20:54 2013 +0300
summary:
  Issue #16624: `subprocess.check_output` now accepts an `input` argument,
allowing the subprocess's stdin to be provided as a (byte) string.
Patch by Zack Weinberg.

files:
  Doc/library/subprocess.rst  |  26 ++++++++++++-----
  Lib/subprocess.py           |  22 ++++++++++++++-
  Lib/test/test_subprocess.py |  36 ++++++++++++++++++++++++-
  Misc/NEWS                   |   4 ++
  4 files changed, 78 insertions(+), 10 deletions(-)


diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -116,7 +116,7 @@
       *timeout* was added.
 
 
-.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
+.. function:: check_output(args, *, input=None, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
 
    Run command with arguments and return its output.
 
@@ -129,15 +129,21 @@
    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 - this functions passes all
-   supplied arguments other than *timeout* directly through to that interface.
-   In addition, *stdout* is not permitted as an argument, as it is used
-   internally to collect the output from the subprocess.
+   supplied arguments other than *input* and *timeout* directly through to
+   that interface.  In addition, *stdout* is not permitted as an argument, as
+   it is used internally to collect the output from the subprocess.
 
    The *timeout* argument is passed to :meth:`Popen.wait`. If the timeout
    expires, the child process will be killed and then waited for again.  The
    :exc:`TimeoutExpired` exception will be re-raised after the child process
    has terminated.
 
+   The *input* argument is passed to :meth:`Popen.communicate` and thus to the
+   subprocess's stdin.  If used it must be a byte sequence, or a string if
+   ``universal_newlines=True``.  When used, the internal :class:`Popen` object
+   is automatically created with ``stdin=PIPE``, and the *stdin* argument may
+   not be used as well.
+
    Examples::
 
       >>> subprocess.check_output(["echo", "Hello World!"])
@@ -146,6 +152,10 @@
       >>> subprocess.check_output(["echo", "Hello World!"], universal_newlines=True)
       'Hello World!\n'
 
+      >>> subprocess.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'
+
       >>> subprocess.check_output("exit 1", shell=True)
       Traceback (most recent call last):
          ...
@@ -167,10 +177,6 @@
       ...     shell=True)
       'ls: non_existent_file: No such file or directory\n'
 
-   .. versionadded:: 3.1
-
-   ..
-
    .. warning::
 
       Invoking the system shell with ``shell=True`` can be a security hazard
@@ -183,9 +189,13 @@
       read in the current process, the child process may block if it
       generates enough output to the pipe to fill up the OS pipe buffer.
 
+   .. versionadded:: 3.1
+
    .. versionchanged:: 3.3
       *timeout* was added.
 
+   .. versionchanged:: 3.4
+      *input* was added.
 
 .. data:: DEVNULL
 
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -175,6 +175,9 @@
 
     >>> output = subprocess.check_output(["ls", "-l", "/dev/null"])
 
+    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.
 
 Exceptions
 ----------
@@ -563,14 +566,31 @@
     ...              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'
+
     If universal_newlines=True is passed, the return value will be a
     string rather than bytes.
     """
     if 'stdout' in kwargs:
         raise ValueError('stdout argument not allowed, it will be overridden.')
+    if 'input' in kwargs:
+        if 'stdin' in kwargs:
+            raise ValueError('stdin and input arguments may not both be used.')
+        inputdata = kwargs['input']
+        del kwargs['input']
+        kwargs['stdin'] = PIPE
+    else:
+        inputdata = None
     with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
         try:
-            output, unused_err = process.communicate(timeout=timeout)
+            output, unused_err = process.communicate(inputdata, timeout=timeout)
         except TimeoutExpired:
             process.kill()
             output, unused_err = process.communicate()
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -158,8 +158,28 @@
                 stderr=subprocess.STDOUT)
         self.assertIn(b'BDFL', output)
 
+    def test_check_output_stdin_arg(self):
+        # check_output() can be called with stdin set to a file
+        tf = tempfile.TemporaryFile()
+        self.addCleanup(tf.close)
+        tf.write(b'pear')
+        tf.seek(0)
+        output = subprocess.check_output(
+                [sys.executable, "-c",
+                 "import sys; sys.stdout.write(sys.stdin.read().upper())"],
+                stdin=tf)
+        self.assertIn(b'PEAR', output)
+
+    def test_check_output_input_arg(self):
+        # check_output() can be called with input set to a string
+        output = subprocess.check_output(
+                [sys.executable, "-c",
+                 "import sys; sys.stdout.write(sys.stdin.read().upper())"],
+                input=b'pear')
+        self.assertIn(b'PEAR', output)
+
     def test_check_output_stdout_arg(self):
-        # check_output() function stderr redirected to stdout
+        # check_output() refuses to accept 'stdout' argument
         with self.assertRaises(ValueError) as c:
             output = subprocess.check_output(
                     [sys.executable, "-c", "print('will not be run')"],
@@ -167,6 +187,20 @@
             self.fail("Expected ValueError when stdout arg supplied.")
         self.assertIn('stdout', c.exception.args[0])
 
+    def test_check_output_stdin_with_input_arg(self):
+        # check_output() refuses to accept 'stdin' with 'input'
+        tf = tempfile.TemporaryFile()
+        self.addCleanup(tf.close)
+        tf.write(b'pear')
+        tf.seek(0)
+        with self.assertRaises(ValueError) as c:
+            output = subprocess.check_output(
+                    [sys.executable, "-c", "print('will not be run')"],
+                    stdin=tf, input=b'hare')
+            self.fail("Expected ValueError when stdin and input args supplied.")
+        self.assertIn('stdin', c.exception.args[0])
+        self.assertIn('input', c.exception.args[0])
+
     def test_check_output_timeout(self):
         # check_output() function with timeout arg
         with self.assertRaises(subprocess.TimeoutExpired) as c:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,10 @@
 Library
 -------
 
+- Issue #16624: `subprocess.check_output` now accepts an `input` argument,
+  allowing the subprocess's stdin to be provided as a (byte) string.
+  Patch by Zack Weinberg.
+
 - Issue #17795: Reverted backwards-incompatible change in SysLogHandler with
   Unix domain sockets.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list