1
2
3
4 """
5 Miscellanea.
6 """
7
8 __all__ = '''
9 TerminalController
10 cleartimeout
11 days
12 default_if_none
13 generate_bit_fields
14 remove_colors
15 run
16 sendmail
17 seq
18 settimeout
19 timeout_exception
20 wall_clock
21 '''.split()
22
23
24
25
26
27 import smtplib
30 msg = "From: %s\r\nTo: %s\r\nSubject:%s\r\n\r\n%s" % (sender, to, subj, body)
31 server = smtplib.SMTP('localhost')
32 try:
33 server.set_debuglevel(False)
34 server.sendmail(sender, to, msg)
35 finally:
36 server.quit()
37
38
39
40
41
42 from datetime import timedelta
45 """Returns the ceil(days in the timedelta L{td})."""
46 return td.days + (1 if td - timedelta(days = td.days) > timedelta() else 0)
47
53 """
54 A generator of [2^i] for i from 0 to (count - 1). Useful for,
55 e.g., enumerating bitmask flags::
56
57 red, yellow, green, blue = generate_bit_fields(4)
58 color1 = blue
59 color2 = red | yellow
60
61 @param count: The number of times to perform the left-shift.
62 @type count: int
63 """
64 j = 1
65 for i in xrange( count ):
66 yield j
67 j <<= 1
68
69
70
71
72
73 from time import *
74 from contextlib import *
75 from signal import *
79 """
80 A simple timer for code sections.
81
82 Example::
83
84 t = [0]
85 with wall_clock(t):
86 sleep(1)
87 print "the sleep operation took %d seconds" % t[0]
88
89 @param output: The resulting time is put into index 0 of C{output}.
90 @type output: index-writeable
91 """
92 start = time()
93 try:
94 yield
95 finally:
96 end = time()
97 output[0] = end - start
98
100
102 """
103 Set the signal handler for SIGALRM to raise timeout_exception, and call
104 alarm(secs).
105 """
106 def handle(sig, frame): raise timeout_exception()
107 signal(SIGALRM, handle)
108 alarm(secs)
109
111
117 """
118 Returns L{x} if it's not None, otherwise returns L{d}.
119 """
120 if x is None: return d
121 else: return x
122
124 """
125 Evaluate 0-ary functions L{f} then L{g}, returning L{g()}.
126 """
127 f()
128 return g()
129
130
131
132
133
134 from subprocess import CalledProcessError, PIPE, Popen
137 """
138 Run the given command (a list of program and argument strings) and return the
139 stdout as a string, raising a L{CalledProcessError} if the program exited
140 with a non-zero status. Different from check_call because I return the
141 (piped) stdout.
142 """
143 p = Popen(cmd, stdout=PIPE)
144 stdout = p.communicate()[0]
145 if p.returncode != 0: raise CalledProcessError(p.returncode, cmd)
146 return stdout
147
148
149
150
151
152 import sys, re
155 """
156 From U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116}.
157
158 A class that can be used to portably generate formatted output to
159 a terminal.
160
161 `TerminalController` defines a set of instance variables whose
162 values are initialized to the control sequence necessary to
163 perform a given action. These can be simply included in normal
164 output to the terminal:
165
166 >>> term = TerminalController()
167 >>> print 'This is '+term.GREEN+'green'+term.NORMAL
168
169 Alternatively, the `render()` method can used, which replaces
170 '${action}' with the string required to perform 'action':
171
172 >>> term = TerminalController()
173 >>> print term.render('This is ${GREEN}green${NORMAL}')
174
175 If the terminal doesn't support a given action, then the value of
176 the corresponding instance variable will be set to ''. As a
177 result, the above code will still work on terminals that do not
178 support color, except that their output will not be colored.
179 Also, this means that you can test whether the terminal supports a
180 given action by simply testing the truth value of the
181 corresponding instance variable:
182
183 >>> term = TerminalController()
184 >>> if term.CLEAR_SCREEN:
185 ... print 'This terminal supports clearning the screen.'
186
187 Finally, if the width and height of the terminal are known, then
188 they will be stored in the `COLS` and `LINES` attributes.
189 """
190
191 BOL = ''
192 UP = ''
193 DOWN = ''
194 LEFT = ''
195 RIGHT = ''
196
197
198 CLEAR_SCREEN = ''
199 CLEAR_EOL = ''
200 CLEAR_BOL = ''
201 CLEAR_EOS = ''
202
203
204 BOLD = ''
205 BLINK = ''
206 DIM = ''
207 REVERSE = ''
208 NORMAL = ''
209
210
211 HIDE_CURSOR = ''
212 SHOW_CURSOR = ''
213
214
215 COLS = None
216 LINES = None
217
218
219 BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
220
221
222 BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
223 BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
224
225 _STRING_CAPABILITIES = """
226 BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
227 CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
228 BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
229 HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
230 _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
231 _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
232
233 - def __init__(self, term_stream=sys.stdout, force=False):
234 """
235 Create a `TerminalController` and initialize its attributes
236 with appropriate values for the current terminal.
237 `term_stream` is the stream that will be used for terminal
238 output; if this stream is not a tty, then the terminal is
239 assumed to be a dumb terminal (i.e., have no capabilities).
240 """
241
242 try: import curses
243 except: return
244
245
246 if not force and not term_stream.isatty(): return
247
248
249
250 try: curses.setupterm()
251 except: return
252
253
254 self.COLS = curses.tigetnum('cols')
255 self.LINES = curses.tigetnum('lines')
256
257
258 for capability in self._STRING_CAPABILITIES:
259 (attrib, cap_name) = capability.split('=')
260 setattr(self, attrib, self._tigetstr(cap_name) or '')
261
262
263 set_fg = self._tigetstr('setf')
264 if set_fg:
265 for i,color in zip(range(len(self._COLORS)), self._COLORS):
266 setattr(self, color, curses.tparm(set_fg, i) or '')
267 set_fg_ansi = self._tigetstr('setaf')
268 if set_fg_ansi:
269 for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
270 setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
271 set_bg = self._tigetstr('setb')
272 if set_bg:
273 for i,color in zip(range(len(self._COLORS)), self._COLORS):
274 setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
275 set_bg_ansi = self._tigetstr('setab')
276 if set_bg_ansi:
277 for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
278 setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
279
281
282
283
284 import curses
285 cap = curses.tigetstr(cap_name) or ''
286 return re.sub(r'\$<\d+>[/*]?', '', cap)
287
289 """
290 Replace each $-substitutions in the given template string with
291 the corresponding terminal control string (if it's defined) or
292 '' (if it's not).
293 """
294 return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
295
297 s = match.group()
298 if s == '$$': return s
299 else: return getattr(self, s[2:-1])
300
301 - def wrap(self, s, color):
302 """
303 Wraps L{s} in L{color} (resets color to NORMAL at the end).
304 """
305 return self.render( '${%s}%s${NORMAL}' % (color, s) )
306
307 remove_colors_pat = re.compile('\033\\[[0-9;]*m')
309 'Removes ANSI escape codes (e.g. those for terminal colors).'
310 return remove_colors_pat.sub('', s)
311
312 import unittest
316 tc = TerminalController()
317 template = '${GREEN}green${BLUE}blue${NORMAL}normal'
318 rendered = tc.render( template )
319 removed = remove_colors( rendered )
320 self.assertEqual( removed, 'greenbluenormal' )
321
322 if __name__ == '__main__':
323 unittest.main()
324