1 # The software in this package is distributed under the GNU General
2 # Public License version 2 (with a special exception described below).
4 # A copy of GNU General Public License (GPL) is included in this distribution,
5 # in the file COPYING.GPL.
7 # As a special exception, if other files instantiate templates or use macros
8 # or inline functions from this file, or you compile this file and link it
9 # with other works to produce a work based on this file, this file
10 # does not by itself cause the resulting work to be covered
11 # by the GNU General Public License.
13 # However the source code for this file must still be made available
14 # in accordance with section (3) of the GNU General Public License.
16 # This exception does not invalidate any other reasons why a work based
17 # on this file might be covered by the GNU General Public License.
19 # Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
21 """ Helpers for calling commands, capture their output, return result code
23 Subprocess library just does not provide all the simplicity we would like
25 Stay python2 compatible --> no timeouts
28 from subprocess import Popen, PIPE
31 def call_and_capture(command, stdin_data=None, split_lines=True,
33 """ call command, captures stdout, stderr and return code, return when done
35 Use only for commands with little output since all output has to be
37 Quoting :py:mod:`subprocess`:
39 ..note:: The data read is buffered in memory, so do not use this method if
40 the data size is large or unlimited.
42 Forwards all args to Popen constructor, except:
43 stdout=PIPE (forced, ignored if in kwargs)
44 stderr=PIPE (forced, ignored if in kwargs)
45 shell=False (except if set in kwargs)
46 universal_newlines=True (except if set in kwargs)
48 :param command: forwarded as first arg to Popen constructor
49 :param str stdin_data: forwarded to stdin of process through communicate
50 :param bool split_lines: True (default) to split output line-wise and
51 return list of strings; False --> return single
52 string for out and one for err
53 :param args: forwarded to Popen constructor
54 :param kwargs: forwarded to Popen constructor
55 :returns: (return_code, stdout, stderr); stdout and stderr are lists of
56 text lines (without terminating newlines); if universal_newlines is
57 True (default), the lines are of type str, otherwise they are non-
58 unicode text (type str in py2, bytes in py3). If split_lines is False
59 (not default), then stdout and stderr are single multi-line strings
61 :raise: OSError (e.g., if command does not exist), ValueError if args are
62 invalid; no :py:class:`subprocess.CalledProcessError` nor
63 :py:class:`subprocess.TimeoutExpired`
67 enforced_params = ('stdout', 'stderr')
68 my_kwargs = dict(stdout=PIPE, stderr=PIPE,
69 shell=False, universal_newlines=True)
70 for key, value in kwargs.items():
71 if key not in enforced_params:
72 my_kwargs[key] = value
75 proc = Popen(command, *args, **my_kwargs)
76 stdout_data, stderr_data = proc.communicate(stdin_data)
80 return proc.returncode, stdout_data.splitlines(), \
81 stderr_data.splitlines()
83 return proc.returncode, stdout_data, stderr_data