Package commons :: Module misc
[hide private]
[frames] | no frames]

Source Code for Module commons.misc

  1  # -*- mode: python; tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4; -*- 
  2  # vim:ft=python:et:sw=4:ts=4 
  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  # Email 
 25  # 
 26   
 27  import smtplib 
28 29 -def sendmail(sender, to, subj, body):
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 # Date/Time 40 # 41 42 from datetime import timedelta
43 44 -def days(td):
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
48 # 49 # Bit fields 50 # 51 52 -def generate_bit_fields(count):
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 # Timing 71 # 72 73 from time import * 74 from contextlib import * 75 from signal import *
76 77 @contextmanager 78 -def wall_clock(output):
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
99 -class timeout_exception(Exception): pass
100
101 -def settimeout(secs):
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
110 -def cleartimeout(): alarm(0)
111
112 # 113 # Functional 114 # 115 116 -def default_if_none(x, d):
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
123 -def seq(f, g):
124 """ 125 Evaluate 0-ary functions L{f} then L{g}, returning L{g()}. 126 """ 127 f() 128 return g()
129 130 # 131 # Processes 132 # 133 134 from subprocess import CalledProcessError, PIPE, Popen
135 136 -def run(cmd):
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 # Terminal ANSI coloring 150 # 151 152 import sys, re
153 154 -class TerminalController:
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 # Cursor movement: 191 BOL = '' #: Move the cursor to the beginning of the line 192 UP = '' #: Move the cursor up one line 193 DOWN = '' #: Move the cursor down one line 194 LEFT = '' #: Move the cursor left one char 195 RIGHT = '' #: Move the cursor right one char 196 197 # Deletion: 198 CLEAR_SCREEN = '' #: Clear the screen and move to home position 199 CLEAR_EOL = '' #: Clear to the end of the line. 200 CLEAR_BOL = '' #: Clear to the beginning of the line. 201 CLEAR_EOS = '' #: Clear to the end of the screen 202 203 # Output modes: 204 BOLD = '' #: Turn on bold mode 205 BLINK = '' #: Turn on blink mode 206 DIM = '' #: Turn on half-bright mode 207 REVERSE = '' #: Turn on reverse-video mode 208 NORMAL = '' #: Turn off all modes 209 210 # Cursor display: 211 HIDE_CURSOR = '' #: Make the cursor invisible 212 SHOW_CURSOR = '' #: Make the cursor visible 213 214 # Terminal size: 215 COLS = None #: Width of the terminal (None for unknown) 216 LINES = None #: Height of the terminal (None for unknown) 217 218 # Foreground colors: 219 BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' 220 221 # Background colors: 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 # Curses isn't available on all platforms 242 try: import curses 243 except: return 244 245 # If the stream isn't a tty, then assume it has no capabilities. 246 if not force and not term_stream.isatty(): return 247 248 # Check the terminal type. If we fail, then assume that the 249 # terminal has no capabilities. 250 try: curses.setupterm() 251 except: return 252 253 # Look up numeric capabilities. 254 self.COLS = curses.tigetnum('cols') 255 self.LINES = curses.tigetnum('lines') 256 257 # Look up string capabilities. 258 for capability in self._STRING_CAPABILITIES: 259 (attrib, cap_name) = capability.split('=') 260 setattr(self, attrib, self._tigetstr(cap_name) or '') 261 262 # Colors 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
280 - def _tigetstr(self, cap_name):
281 # String capabilities can include "delays" of the form "$<2>". 282 # For any modern terminal, we should be able to just ignore 283 # these, so strip them out. 284 import curses 285 cap = curses.tigetstr(cap_name) or '' 286 return re.sub(r'\$<\d+>[/*]?', '', cap)
287
288 - def render(self, template):
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
296 - def _render_sub(self, match):
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')
308 -def remove_colors(s):
309 'Removes ANSI escape codes (e.g. those for terminal colors).' 310 return remove_colors_pat.sub('', s)
311 312 import unittest
313 314 -class color_test( unittest.TestCase ):
315 - def test_round_trip( self ):
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