]>
Commit | Line | Data |
---|---|---|
0d24de9d SG |
1 | # Copyright (c) 2011 The Chromium OS Authors. |
2 | # | |
1a459660 | 3 | # SPDX-License-Identifier: GPL-2.0+ |
0d24de9d SG |
4 | # |
5 | ||
6 | import os | |
a10fd93c | 7 | import cros_subprocess |
0d24de9d SG |
8 | |
9 | """Shell command ease-ups for Python.""" | |
10 | ||
a10fd93c SG |
11 | class CommandResult: |
12 | """A class which captures the result of executing a command. | |
13 | ||
14 | Members: | |
15 | stdout: stdout obtained from command, as a string | |
16 | stderr: stderr obtained from command, as a string | |
17 | return_code: Return code from command | |
18 | exception: Exception received, or None if all ok | |
19 | """ | |
20 | def __init__(self): | |
21 | self.stdout = None | |
22 | self.stderr = None | |
82012dd2 | 23 | self.combined = None |
a10fd93c SG |
24 | self.return_code = None |
25 | self.exception = None | |
26 | ||
82012dd2 SG |
27 | def __init__(self, stdout='', stderr='', combined='', return_code=0, |
28 | exception=None): | |
29 | self.stdout = stdout | |
30 | self.stderr = stderr | |
31 | self.combined = combined | |
32 | self.return_code = return_code | |
33 | self.exception = exception | |
34 | ||
35 | ||
36 | # This permits interception of RunPipe for test purposes. If it is set to | |
37 | # a function, then that function is called with the pipe list being | |
38 | # executed. Otherwise, it is assumed to be a CommandResult object, and is | |
39 | # returned as the result for every RunPipe() call. | |
40 | # When this value is None, commands are executed as normal. | |
41 | test_result = None | |
a10fd93c SG |
42 | |
43 | def RunPipe(pipe_list, infile=None, outfile=None, | |
44 | capture=False, capture_stderr=False, oneline=False, | |
dc191505 | 45 | raise_on_error=True, cwd=None, **kwargs): |
0d24de9d SG |
46 | """ |
47 | Perform a command pipeline, with optional input/output filenames. | |
48 | ||
a10fd93c SG |
49 | Args: |
50 | pipe_list: List of command lines to execute. Each command line is | |
51 | piped into the next, and is itself a list of strings. For | |
52 | example [ ['ls', '.git'] ['wc'] ] will pipe the output of | |
53 | 'ls .git' into 'wc'. | |
54 | infile: File to provide stdin to the pipeline | |
55 | outfile: File to store stdout | |
56 | capture: True to capture output | |
57 | capture_stderr: True to capture stderr | |
58 | oneline: True to strip newline chars from output | |
59 | kwargs: Additional keyword arguments to cros_subprocess.Popen() | |
60 | Returns: | |
61 | CommandResult object | |
0d24de9d | 62 | """ |
82012dd2 SG |
63 | if test_result: |
64 | if hasattr(test_result, '__call__'): | |
65 | return test_result(pipe_list=pipe_list) | |
66 | return test_result | |
a10fd93c | 67 | result = CommandResult() |
0d24de9d | 68 | last_pipe = None |
a10fd93c | 69 | pipeline = list(pipe_list) |
dc191505 | 70 | user_pipestr = '|'.join([' '.join(pipe) for pipe in pipe_list]) |
ddaf5c8f SG |
71 | kwargs['stdout'] = None |
72 | kwargs['stderr'] = None | |
0d24de9d SG |
73 | while pipeline: |
74 | cmd = pipeline.pop(0) | |
0d24de9d SG |
75 | if last_pipe is not None: |
76 | kwargs['stdin'] = last_pipe.stdout | |
77 | elif infile: | |
78 | kwargs['stdin'] = open(infile, 'rb') | |
79 | if pipeline or capture: | |
a10fd93c | 80 | kwargs['stdout'] = cros_subprocess.PIPE |
0d24de9d SG |
81 | elif outfile: |
82 | kwargs['stdout'] = open(outfile, 'wb') | |
a10fd93c SG |
83 | if capture_stderr: |
84 | kwargs['stderr'] = cros_subprocess.PIPE | |
0d24de9d | 85 | |
a10fd93c SG |
86 | try: |
87 | last_pipe = cros_subprocess.Popen(cmd, cwd=cwd, **kwargs) | |
88 | except Exception, err: | |
89 | result.exception = err | |
dc191505 SG |
90 | if raise_on_error: |
91 | raise Exception("Error running '%s': %s" % (user_pipestr, str)) | |
92 | result.return_code = 255 | |
93 | return result | |
0d24de9d SG |
94 | |
95 | if capture: | |
a10fd93c SG |
96 | result.stdout, result.stderr, result.combined = ( |
97 | last_pipe.CommunicateFilter(None)) | |
98 | if result.stdout and oneline: | |
99 | result.output = result.stdout.rstrip('\r\n') | |
100 | result.return_code = last_pipe.wait() | |
0d24de9d | 101 | else: |
a10fd93c | 102 | result.return_code = os.waitpid(last_pipe.pid, 0)[1] |
dc191505 SG |
103 | if raise_on_error and result.return_code: |
104 | raise Exception("Error running '%s'" % user_pipestr) | |
a10fd93c | 105 | return result |
0d24de9d SG |
106 | |
107 | def Output(*cmd): | |
dc191505 | 108 | return RunPipe([cmd], capture=True, raise_on_error=False).stdout |
0d24de9d | 109 | |
a10fd93c | 110 | def OutputOneLine(*cmd, **kwargs): |
dc191505 | 111 | raise_on_error = kwargs.pop('raise_on_error', True) |
a10fd93c | 112 | return (RunPipe([cmd], capture=True, oneline=True, |
dc191505 | 113 | raise_on_error=raise_on_error, |
a10fd93c | 114 | **kwargs).stdout.strip()) |
0d24de9d SG |
115 | |
116 | def Run(*cmd, **kwargs): | |
a10fd93c | 117 | return RunPipe([cmd], **kwargs).stdout |
0d24de9d SG |
118 | |
119 | def RunList(cmd): | |
a10fd93c SG |
120 | return RunPipe([cmd], capture=True).stdout |
121 | ||
122 | def StopAll(): | |
123 | cros_subprocess.stay_alive = False |