Commit | Line | Data |
---|---|---|
dae4fa8a CH |
1 | # The software in this package is distributed under the GNU General |
2 | # Public License version 2 (with a special exception described below). | |
3 | # | |
4 | # A copy of GNU General Public License (GPL) is included in this distribution, | |
5 | # in the file COPYING.GPL. | |
6 | # | |
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. | |
12 | # | |
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. | |
15 | # | |
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. | |
18 | # | |
19 | # Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com> | |
20 | ||
7628bc48 CH |
21 | """ |
22 | Helpers for calling commands, capture their output, return result code. | |
dae4fa8a | 23 | |
7628bc48 CH |
24 | Subprocess library did not provide all the simplicity we would have liked. |
25 | However, this has since changed, so consider using the easy-to-use builtin | |
26 | functions :py:func:`subprocess.run` or :py:func:`subprocess.call` instead. | |
dae4fa8a CH |
27 | """ |
28 | ||
29 | from subprocess import Popen, PIPE | |
30 | ||
31 | ||
32 | def call_and_capture(command, stdin_data=None, split_lines=True, | |
33 | *args, **kwargs): | |
34 | """ call command, captures stdout, stderr and return code, return when done | |
35 | ||
36 | Use only for commands with little output since all output has to be | |
37 | buffered! | |
38 | Quoting :py:mod:`subprocess`: | |
39 | ||
40 | ..note:: The data read is buffered in memory, so do not use this method if | |
41 | the data size is large or unlimited. | |
42 | ||
43 | Forwards all args to Popen constructor, except: | |
44 | stdout=PIPE (forced, ignored if in kwargs) | |
45 | stderr=PIPE (forced, ignored if in kwargs) | |
46 | shell=False (except if set in kwargs) | |
47 | universal_newlines=True (except if set in kwargs) | |
48 | ||
49 | :param command: forwarded as first arg to Popen constructor | |
50 | :param str stdin_data: forwarded to stdin of process through communicate | |
51 | :param bool split_lines: True (default) to split output line-wise and | |
52 | return list of strings; False --> return single | |
53 | string for out and one for err | |
54 | :param args: forwarded to Popen constructor | |
55 | :param kwargs: forwarded to Popen constructor | |
56 | :returns: (return_code, stdout, stderr); stdout and stderr are lists of | |
57 | text lines (without terminating newlines); if universal_newlines is | |
58 | True (default), the lines are of type str, otherwise they are non- | |
7628bc48 CH |
59 | unicode text (type bytes). If split_lines is False (not default), |
60 | then stdout and stderr are single multi-line strings | |
dae4fa8a CH |
61 | |
62 | :raise: OSError (e.g., if command does not exist), ValueError if args are | |
63 | invalid; no :py:class:`subprocess.CalledProcessError` nor | |
64 | :py:class:`subprocess.TimeoutExpired` | |
65 | """ | |
66 | ||
67 | # construct args | |
68 | enforced_params = ('stdout', 'stderr') | |
69 | my_kwargs = dict(stdout=PIPE, stderr=PIPE, | |
70 | shell=False, universal_newlines=True) | |
71 | for key, value in kwargs.items(): | |
72 | if key not in enforced_params: | |
73 | my_kwargs[key] = value | |
74 | ||
75 | # run command | |
76 | proc = Popen(command, *args, **my_kwargs) | |
77 | stdout_data, stderr_data = proc.communicate(stdin_data) | |
78 | ||
79 | # return | |
80 | if split_lines: | |
81 | return proc.returncode, stdout_data.splitlines(), \ | |
82 | stderr_data.splitlines() | |
83 | else: | |
84 | return proc.returncode, stdout_data, stderr_data |