15 """Various utilities. |
15 """Various utilities. |
16 |
16 |
17 Current contents: |
17 Current contents: |
18 - Text colorization using ANSI color codes |
18 - Text colorization using ANSI color codes |
19 - A class to construct and manage paths under a root path. |
19 - A class to construct and manage paths under a root path. |
|
20 - A helper to manage running subprocesses, wrapping the subprocess module. |
20 """ |
21 """ |
21 |
22 |
22 __authors__ = [ |
23 __authors__ = [ |
23 # alphabetical order by last name, please |
24 # alphabetical order by last name, please |
24 '"David Anderson" <dave@natulte.net>', |
25 '"David Anderson" <dave@natulte.net>', |
25 ] |
26 ] |
26 |
27 |
27 import os.path |
28 import os.path |
|
29 import subprocess |
|
30 |
|
31 import error |
|
32 |
|
33 |
|
34 class Error(error.Error): |
|
35 pass |
|
36 |
|
37 |
|
38 class SubprocessFailed(Error): |
|
39 """A subprocess returned a non-zero error code.""" |
28 |
40 |
29 |
41 |
30 # The magic escape sequence understood by modern terminal emulators to |
42 # The magic escape sequence understood by modern terminal emulators to |
31 # configure fore/background colors and other basic text display |
43 # configure fore/background colors and other basic text display |
32 # settings. |
44 # settings. |
106 |
118 |
107 Returns: |
119 Returns: |
108 True if the path exists, False otherwise. |
120 True if the path exists, False otherwise. |
109 """ |
121 """ |
110 return os.path.exists(self.path(path)) |
122 return os.path.exists(self.path(path)) |
|
123 |
|
124 |
|
125 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''): |
|
126 """Run the given command and optionally return its output. |
|
127 |
|
128 Note that if you set capture=True, the command's output is |
|
129 buffered in memory. Output capture should only be used with |
|
130 commands that output small amounts of data. O(kB) is fine, O(MB) |
|
131 is starting to push it a little. |
|
132 |
|
133 Args: |
|
134 argv: A list containing the name of the program to run, followed |
|
135 by its argument vector. |
|
136 cwd: Run the program from this directory. |
|
137 capture: If True, capture the program's stdout stream. If False, |
|
138 stdout will output to sys.stdout. |
|
139 split_capture: If True, return the captured output as a list of |
|
140 lines. Else, return as a single unaltered string. |
|
141 stdin: The string to feed to the program's stdin stream. |
|
142 |
|
143 Returns: |
|
144 If capture is True, a string containing the combined |
|
145 stdout/stderr output of the program. If capture is False, |
|
146 nothing is returned. |
|
147 |
|
148 Raises: |
|
149 SubprocessFailed: The subprocess exited with a non-zero exit |
|
150 code. |
|
151 """ |
|
152 print colorize('# ' + ' '.join(argv), WHITE, bold=True) |
|
153 |
|
154 process = subprocess.Popen(argv, |
|
155 shell=False, |
|
156 cwd=cwd, |
|
157 stdin=subprocess.PIPE, |
|
158 stdout=(subprocess.PIPE if capture else None), |
|
159 stderr=None) |
|
160 output, _ = process.communicate(input=stdin) |
|
161 if process.returncode != 0: |
|
162 raise SubprocessFailed('Process %s failed with output: %s' % |
|
163 (argv[0], output)) |
|
164 if output is not None and split_capture: |
|
165 return output.strip().split('\n') |
|
166 else: |
|
167 return output |