OLD | NEW |
1 # Copyright 2014 Google Inc. | 1 # Copyright 2014 Google Inc. |
2 # | 2 # |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 | 6 |
7 """Module to host the VerboseSubprocess, ChangeDir, and ReSearch classes. | 7 """Miscellaneous utilities.""" |
8 """ | |
9 | |
10 import os | |
11 import re | |
12 import subprocess | |
13 | 8 |
14 | 9 |
15 def print_subprocess_args(prefix, *args, **kwargs): | 10 import re |
16 """Print out args in a human-readable manner.""" | |
17 def quote_and_escape(string): | |
18 """Quote and escape a string if necessary.""" | |
19 if ' ' in string or '\n' in string: | |
20 string = '"%s"' % string.replace('"', '\\"') | |
21 return string | |
22 if 'cwd' in kwargs: | |
23 print '%scd %s' % (prefix, kwargs['cwd']) | |
24 print prefix + ' '.join(quote_and_escape(arg) for arg in args[0]) | |
25 if 'cwd' in kwargs: | |
26 print '%scd -' % prefix | |
27 | |
28 | |
29 class VerboseSubprocess(object): | |
30 """Call subprocess methods, but print out command before executing. | |
31 | |
32 Attributes: | |
33 verbose: (boolean) should we print out the command or not. If | |
34 not, this is the same as calling the subprocess method | |
35 quiet: (boolean) suppress stdout on check_call and call. | |
36 prefix: (string) When verbose, what to print before each command. | |
37 """ | |
38 | |
39 def __init__(self, verbose): | |
40 self.verbose = verbose | |
41 self.quiet = not verbose | |
42 self.prefix = '~~$ ' | |
43 | |
44 def check_call(self, *args, **kwargs): | |
45 """Wrapper for subprocess.check_call(). | |
46 | |
47 Args: | |
48 *args: to be passed to subprocess.check_call() | |
49 **kwargs: to be passed to subprocess.check_call() | |
50 Returns: | |
51 Whatever subprocess.check_call() returns. | |
52 Raises: | |
53 OSError or subprocess.CalledProcessError: raised by check_call. | |
54 """ | |
55 if self.verbose: | |
56 print_subprocess_args(self.prefix, *args, **kwargs) | |
57 if self.quiet: | |
58 with open(os.devnull, 'w') as devnull: | |
59 return subprocess.check_call(*args, stdout=devnull, **kwargs) | |
60 else: | |
61 return subprocess.check_call(*args, **kwargs) | |
62 | |
63 def call(self, *args, **kwargs): | |
64 """Wrapper for subprocess.check(). | |
65 | |
66 Args: | |
67 *args: to be passed to subprocess.check_call() | |
68 **kwargs: to be passed to subprocess.check_call() | |
69 Returns: | |
70 Whatever subprocess.call() returns. | |
71 Raises: | |
72 OSError or subprocess.CalledProcessError: raised by call. | |
73 """ | |
74 if self.verbose: | |
75 print_subprocess_args(self.prefix, *args, **kwargs) | |
76 if self.quiet: | |
77 with open(os.devnull, 'w') as devnull: | |
78 return subprocess.call(*args, stdout=devnull, **kwargs) | |
79 else: | |
80 return subprocess.call(*args, **kwargs) | |
81 | |
82 def check_output(self, *args, **kwargs): | |
83 """Wrapper for subprocess.check_output(). | |
84 | |
85 Args: | |
86 *args: to be passed to subprocess.check_output() | |
87 **kwargs: to be passed to subprocess.check_output() | |
88 Returns: | |
89 Whatever subprocess.check_output() returns. | |
90 Raises: | |
91 OSError or subprocess.CalledProcessError: raised by check_output. | |
92 """ | |
93 if self.verbose: | |
94 print_subprocess_args(self.prefix, *args, **kwargs) | |
95 return subprocess.check_output(*args, **kwargs) | |
96 | |
97 def strip_output(self, *args, **kwargs): | |
98 """Wrap subprocess.check_output and str.strip(). | |
99 | |
100 Pass the given arguments into subprocess.check_output() and return | |
101 the results, after stripping any excess whitespace. | |
102 | |
103 Args: | |
104 *args: to be passed to subprocess.check_output() | |
105 **kwargs: to be passed to subprocess.check_output() | |
106 | |
107 Returns: | |
108 The output of the process as a string without leading or | |
109 trailing whitespace. | |
110 Raises: | |
111 OSError or subprocess.CalledProcessError: raised by check_output. | |
112 """ | |
113 if self.verbose: | |
114 print_subprocess_args(self.prefix, *args, **kwargs) | |
115 return str(subprocess.check_output(*args, **kwargs)).strip() | |
116 | |
117 def popen(self, *args, **kwargs): | |
118 """Wrapper for subprocess.Popen(). | |
119 | |
120 Args: | |
121 *args: to be passed to subprocess.Popen() | |
122 **kwargs: to be passed to subprocess.Popen() | |
123 Returns: | |
124 The output of subprocess.Popen() | |
125 Raises: | |
126 OSError or subprocess.CalledProcessError: raised by Popen. | |
127 """ | |
128 if self.verbose: | |
129 print_subprocess_args(self.prefix, *args, **kwargs) | |
130 return subprocess.Popen(*args, **kwargs) | |
131 | |
132 | |
133 class ChangeDir(object): | |
134 """Use with a with-statement to temporarily change directories.""" | |
135 # pylint: disable=I0011,R0903 | |
136 | |
137 def __init__(self, directory, verbose=False): | |
138 self._directory = directory | |
139 self._verbose = verbose | |
140 | |
141 def __enter__(self): | |
142 if self._directory != os.curdir: | |
143 if self._verbose: | |
144 print '~~$ cd %s' % self._directory | |
145 cwd = os.getcwd() | |
146 os.chdir(self._directory) | |
147 self._directory = cwd | |
148 | |
149 def __exit__(self, etype, value, traceback): | |
150 if self._directory != os.curdir: | |
151 if self._verbose: | |
152 print '~~$ cd %s' % self._directory | |
153 os.chdir(self._directory) | |
154 | 11 |
155 | 12 |
156 class ReSearch(object): | 13 class ReSearch(object): |
157 """A collection of static methods for regexing things.""" | 14 """A collection of static methods for regexing things.""" |
158 | 15 |
159 @staticmethod | 16 @staticmethod |
160 def search_within_stream(input_stream, pattern, default=None): | 17 def search_within_stream(input_stream, pattern, default=None): |
161 """Search for regular expression in a file-like object. | 18 """Search for regular expression in a file-like object. |
162 | 19 |
163 Opens a file for reading and searches line by line for a match to | 20 Opens a file for reading and searches line by line for a match to |
(...skipping 28 matching lines...) Expand all Loading... |
192 Args: | 49 Args: |
193 input_string: (string) to be searched | 50 input_string: (string) to be searched |
194 pattern: (string) to be passed to re.compile | 51 pattern: (string) to be passed to re.compile |
195 default: what to return if no match | 52 default: what to return if no match |
196 | 53 |
197 Returns: | 54 Returns: |
198 A string or whatever default is | 55 A string or whatever default is |
199 """ | 56 """ |
200 match = re.search(pattern, input_string) | 57 match = re.search(pattern, input_string) |
201 return match.group('return') if match else default | 58 return match.group('return') if match else default |
202 | |
203 @staticmethod | |
204 def search_within_output(verbose, pattern, default, *args, **kwargs): | |
205 """Search for regular expression in a process output. | |
206 | |
207 Does not search across newlines. | |
208 | |
209 Args: | |
210 verbose: (boolean) shoule we call print_subprocess_args? | |
211 pattern: (string) to be passed to re.compile | |
212 default: what to return if no match | |
213 *args: to be passed to subprocess.Popen() | |
214 **kwargs: to be passed to subprocess.Popen() | |
215 | |
216 Returns: | |
217 A string or whatever default is | |
218 """ | |
219 if verbose: | |
220 print_subprocess_args('~~$ ', *args, **kwargs) | |
221 proc = subprocess.Popen(*args, stdout=subprocess.PIPE, **kwargs) | |
222 return ReSearch.search_within_stream(proc.stdout, pattern, default) | |
223 | |
224 | |
OLD | NEW |