57 GREEN = 32 |
57 GREEN = 32 |
58 WHITE = 37 |
58 WHITE = 37 |
59 |
59 |
60 |
60 |
61 def _ansi_escape(code): |
61 def _ansi_escape(code): |
62 return _ANSI_ESCAPE % code |
62 return _ANSI_ESCAPE % code |
63 |
63 |
64 |
64 |
65 def colorize(text, color, bold=False): |
65 def colorize(text, color, bold=False): |
66 """Colorize some text using ANSI color codes. |
66 """Colorize some text using ANSI color codes. |
67 |
67 |
68 Note that while ANSI color codes look good in a terminal they look |
68 Note that while ANSI color codes look good in a terminal they look |
69 like noise in log files unless viewed in an ANSI color capable |
69 like noise in log files unless viewed in an ANSI color capable |
70 viewer (such as 'less -R'). |
70 viewer (such as 'less -R'). |
71 |
71 |
72 Args: |
72 Args: |
73 text: The text to colorize. |
73 text: The text to colorize. |
74 color: One of the color symbols from this module. |
74 color: One of the color symbols from this module. |
75 bold: If True, make the color brighter. |
75 bold: If True, make the color brighter. |
76 |
76 |
77 Returns: |
77 Returns: |
78 The input text string, appropriately sprinkled with color |
78 The input text string, appropriately sprinkled with color |
79 codes. Colors are reset to terminal defaults after the input |
79 codes. Colors are reset to terminal defaults after the input |
80 text. |
80 text. |
81 """ |
81 """ |
82 bold = _ansi_escape(_BOLD) if bold else '' |
82 bold = _ansi_escape(_BOLD) if bold else '' |
83 return '%s%s%s%s' % (bold, _ansi_escape(color), |
83 return '%s%s%s%s' % (bold, _ansi_escape(color), |
84 text, _ansi_escape(_RESET)) |
84 text, _ansi_escape(_RESET)) |
85 |
85 |
86 |
86 |
87 def decolorize(text): |
87 def decolorize(text): |
88 """Remove ANSI color codes from text.""" |
88 """Remove ANSI color codes from text.""" |
89 return _ANSI_ESCAPE_RE.sub('', text) |
89 return _ANSI_ESCAPE_RE.sub('', text) |
90 |
90 |
91 |
91 |
92 class Paths(object): |
92 class Paths(object): |
93 """A helper to construct and check paths under a given root.""" |
93 """A helper to construct and check paths under a given root.""" |
94 |
94 |
95 def __init__(self, root): |
95 def __init__(self, root): |
96 """Initializer. |
96 """Initializer. |
97 |
97 |
98 Args: |
98 Args: |
99 root: The root of all paths this instance will consider. |
99 root: The root of all paths this instance will consider. |
100 """ |
100 """ |
101 self._root = os.path.abspath( |
101 self._root = os.path.abspath( |
102 os.path.expandvars(os.path.expanduser(root))) |
102 os.path.expandvars(os.path.expanduser(root))) |
103 |
103 |
104 def path(self, path=''): |
104 def path(self, path=''): |
105 """Construct and return a path under the path root. |
105 """Construct and return a path under the path root. |
106 |
106 |
107 Args: |
107 Args: |
108 path: The desired path string relative to the root. |
108 path: The desired path string relative to the root. |
109 |
109 |
110 Returns: |
110 Returns: |
111 The absolute path corresponding to the relative input path. |
111 The absolute path corresponding to the relative input path. |
112 """ |
112 """ |
113 assert not os.path.isabs(path) |
113 assert not os.path.isabs(path) |
114 return os.path.abspath(os.path.join(self._root, path)) |
114 return os.path.abspath(os.path.join(self._root, path)) |
115 |
115 |
116 def exists(self, path=''): |
116 def exists(self, path=''): |
117 """Check for the existence of a path under the path root. |
117 """Check for the existence of a path under the path root. |
118 |
118 |
119 Does not discriminate on the path type (ie. it could be a |
119 Does not discriminate on the path type (ie. it could be a |
120 directory, a file, a symbolic link...), just checks for the |
120 directory, a file, a symbolic link...), just checks for the |
121 existence of the path. |
121 existence of the path. |
122 |
122 |
123 Args: |
123 Args: |
124 path: The path string relative to the root. |
124 path: The path string relative to the root. |
125 |
125 |
126 Returns: |
126 Returns: |
127 True if the path exists, False otherwise. |
127 True if the path exists, False otherwise. |
128 """ |
128 """ |
129 return os.path.exists(self.path(path)) |
129 return os.path.exists(self.path(path)) |
130 |
130 |
131 |
131 |
132 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''): |
132 def run(argv, cwd=None, capture=False, split_capture=True, stdin=''): |
133 """Run the given command and optionally return its output. |
133 """Run the given command and optionally return its output. |
134 |
134 |
135 Note that if you set capture=True, the command's output is |
135 Note that if you set capture=True, the command's output is |
136 buffered in memory. Output capture should only be used with |
136 buffered in memory. Output capture should only be used with |
137 commands that output small amounts of data. O(kB) is fine, O(MB) |
137 commands that output small amounts of data. O(kB) is fine, O(MB) |
138 is starting to push it a little. |
138 is starting to push it a little. |
139 |
139 |
140 Args: |
140 Args: |
141 argv: A list containing the name of the program to run, followed |
141 argv: A list containing the name of the program to run, followed |
142 by its argument vector. |
142 by its argument vector. |
143 cwd: Run the program from this directory. |
143 cwd: Run the program from this directory. |
144 capture: If True, capture the program's stdout stream. If False, |
144 capture: If True, capture the program's stdout stream. If False, |
145 stdout will output to sys.stdout. |
145 stdout will output to sys.stdout. |
146 split_capture: If True, return the captured output as a list of |
146 split_capture: If True, return the captured output as a list of |
147 lines. Else, return as a single unaltered string. |
147 lines. Else, return as a single unaltered string. |
148 stdin: The string to feed to the program's stdin stream. |
148 stdin: The string to feed to the program's stdin stream. |
149 |
149 |
150 Returns: |
150 Returns: |
151 If capture is True, a string containing the combined |
151 If capture is True, a string containing the combined |
152 stdout/stderr output of the program. If capture is False, |
152 stdout/stderr output of the program. If capture is False, |
153 nothing is returned. |
153 nothing is returned. |
154 |
154 |
155 Raises: |
155 Raises: |
156 SubprocessFailed: The subprocess exited with a non-zero exit |
156 SubprocessFailed: The subprocess exited with a non-zero exit |
157 code. |
157 code. |
158 """ |
158 """ |
159 print colorize('# ' + ' '.join(argv), WHITE, bold=True) |
159 print colorize('# ' + ' '.join(argv), WHITE, bold=True) |
160 |
160 |
161 process = subprocess.Popen(argv, |
161 process = subprocess.Popen(argv, |
162 shell=False, |
162 shell=False, |
163 cwd=cwd, |
163 cwd=cwd, |
164 stdin=subprocess.PIPE, |
164 stdin=subprocess.PIPE, |
165 stdout=(subprocess.PIPE if capture else None), |
165 stdout=(subprocess.PIPE if capture else None), |
166 stderr=None) |
166 stderr=None) |
167 output, _ = process.communicate(input=stdin) |
167 output, _ = process.communicate(input=stdin) |
168 if process.returncode != 0: |
168 if process.returncode != 0: |
169 raise SubprocessFailed('Process %s failed with output: %s' % |
169 raise SubprocessFailed('Process %s failed with output: %s' % |
170 (argv[0], output)) |
170 (argv[0], output)) |
171 if output is not None and split_capture: |
171 if output is not None and split_capture: |
172 return output.strip().split('\n') |
172 return output.strip().split('\n') |
173 else: |
173 else: |
174 return output |
174 return output |