3 """Misc. useful functionality used by the rest of this package.
5 This module provides common functionality used by the other modules in
15 # Whether or not to show debug messages
18 def notify(msg, *args):
19 """Print a message to stderr."""
20 print >> sys.stderr, msg % args
22 def debug (msg, *args):
23 """Print a debug message to stderr when DEBUG is enabled."""
25 print >> sys.stderr, msg % args
27 def error (msg, *args):
28 """Print an error message to stderr."""
29 print >> sys.stderr, "ERROR:", msg % args
32 """Print a warning message to stderr."""
33 print >> sys.stderr, "warning:", msg % args
36 """Print as error message to stderr and exit the program."""
41 class ProgressIndicator(object):
43 """Simple progress indicator.
45 Displayed as a spinning character by default, but can be customized
46 by passing custom messages that overrides the spinning character.
50 States = ("|", "/", "-", "\\")
52 def __init__ (self, prefix = "", f = sys.stdout):
53 """Create a new ProgressIndicator, bound to the given file object."""
54 self.n = 0 # Simple progress counter
55 self.f = f # Progress is written to this file object
56 self.prev_len = 0 # Length of previous msg (to be overwritten)
57 self.prefix = prefix # Prefix prepended to each progress message
58 self.prefix_lens = [] # Stack of prefix string lengths
60 def pushprefix (self, prefix):
61 """Append the given prefix onto the prefix stack."""
62 self.prefix_lens.append(len(self.prefix))
66 """Remove the last prefix from the prefix stack."""
67 prev_len = self.prefix_lens.pop()
68 self.prefix = self.prefix[:prev_len]
70 def __call__ (self, msg = None, lf = False):
71 """Indicate progress, possibly with a custom message."""
73 msg = self.States[self.n % len(self.States)]
74 msg = self.prefix + msg
75 print >> self.f, "\r%-*s" % (self.prev_len, msg),
76 self.prev_len = len(msg.expandtabs())
82 def finish (self, msg = "done", noprefix = False):
83 """Finalize progress indication with the given message."""
89 def start_command (args, cwd = None, shell = False, add_env = None,
90 stdin = subprocess.PIPE, stdout = subprocess.PIPE,
91 stderr = subprocess.PIPE):
92 """Start the given command, and return a subprocess object.
94 This provides a simpler interface to the subprocess module.
98 if add_env is not None:
99 env = os.environ.copy()
101 return subprocess.Popen(args, bufsize = 1, stdin = stdin, stdout = stdout,
102 stderr = stderr, cwd = cwd, shell = shell,
103 env = env, universal_newlines = True)
106 def run_command (args, cwd = None, shell = False, add_env = None,
108 """Run the given command to completion, and return its results.
110 This provides a simpler interface to the subprocess module.
112 The results are formatted as a 3-tuple: (exit_code, output, errors)
114 If flag_error is enabled, Error messages will be produced if the
115 subprocess terminated with a non-zero exit code and/or stderr
118 The other arguments are passed on to start_command().
121 process = start_command(args, cwd, shell, add_env)
122 (output, errors) = process.communicate()
123 exit_code = process.returncode
124 if flag_error and errors:
125 error("'%s' returned errors:\n---\n%s---", " ".join(args), errors)
126 if flag_error and exit_code:
127 error("'%s' returned exit code %i", " ".join(args), exit_code)
128 return (exit_code, output, errors)
131 def file_reader_method (missing_ok = False):
132 """Decorator for simplifying reading of files.
134 If missing_ok is True, a failure to open a file for reading will
135 not raise the usual IOError, but instead the wrapped method will be
136 called with f == None. The method must in this case properly
141 """Teach given method to handle both filenames and file objects.
143 The given method must take a file object as its second argument
144 (the first argument being 'self', of course). This decorator
145 will take a filename given as the second argument and promote
149 def _wrapped_method (self, filename, *args, **kwargs):
150 if isinstance(filename, file):
154 f = open(filename, 'r')
161 return method(self, f, *args, **kwargs)
163 if not isinstance(filename, file) and f:
165 return _wrapped_method
169 def file_writer_method (method):
170 """Decorator for simplifying writing of files.
172 Enables the given method to handle both filenames and file objects.
174 The given method must take a file object as its second argument
175 (the first argument being 'self', of course). This decorator will
176 take a filename given as the second argument and promote it to a
180 def _new_method (self, filename, *args, **kwargs):
181 if isinstance(filename, file):
184 # Make sure the containing directory exists
185 parent_dir = os.path.dirname(filename)
186 if not os.path.isdir(parent_dir):
187 os.makedirs(parent_dir)
188 f = open(filename, 'w')
190 return method(self, f, *args, **kwargs)
192 if not isinstance(filename, file):