Package flumotion :: Package extern :: Package log :: Module termcolor
[hide private]

Source Code for Module flumotion.extern.log.termcolor

  1  # Copyright 2006 Edward Loper. May be distributed under the same terms 
  2  # as Python itself. 
  3  # 
  4  # From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116 
  5   
  6  import re 
  7  import sys 
  8   
  9   
10 -class TerminalController:
11 """ 12 A class that can be used to portably generate formatted output to 13 a terminal. 14 15 `TerminalController` defines a set of instance variables whose 16 values are initialized to the control sequence necessary to 17 perform a given action. These can be simply included in normal 18 output to the terminal: 19 20 >>> term = TerminalController() 21 >>> print 'This is '+term.GREEN+'green'+term.NORMAL 22 23 Alternatively, the `render()` method can used, which replaces 24 '${action}' with the string required to perform 'action': 25 26 >>> term = TerminalController() 27 >>> print term.render('This is ${GREEN}green${NORMAL}') 28 29 If the terminal doesn't support a given action, then the value of 30 the corresponding instance variable will be set to ''. As a 31 result, the above code will still work on terminals that do not 32 support color, except that their output will not be colored. 33 Also, this means that you can test whether the terminal supports a 34 given action by simply testing the truth value of the 35 corresponding instance variable: 36 37 >>> term = TerminalController() 38 >>> if term.CLEAR_SCREEN: 39 ... print 'This terminal supports clearning the screen.' 40 41 Finally, if the width and height of the terminal are known, then 42 they will be stored in the `COLS` and `LINES` attributes. 43 """ 44 # Cursor movement: 45 BOL = '' #: Move the cursor to the beginning of the line 46 UP = '' #: Move the cursor up one line 47 DOWN = '' #: Move the cursor down one line 48 LEFT = '' #: Move the cursor left one char 49 RIGHT = '' #: Move the cursor right one char 50 51 # Deletion: 52 CLEAR_SCREEN = '' #: Clear the screen and move to home position 53 CLEAR_EOL = '' #: Clear to the end of the line. 54 CLEAR_BOL = '' #: Clear to the beginning of the line. 55 CLEAR_EOS = '' #: Clear to the end of the screen 56 57 # Output modes: 58 BOLD = '' #: Turn on bold mode 59 BLINK = '' #: Turn on blink mode 60 DIM = '' #: Turn on half-bright mode 61 REVERSE = '' #: Turn on reverse-video mode 62 NORMAL = '' #: Turn off all modes 63 64 # Cursor display: 65 HIDE_CURSOR = '' #: Make the cursor invisible 66 SHOW_CURSOR = '' #: Make the cursor visible 67 68 # Terminal size: 69 COLS = None #: Width of the terminal (None for unknown) 70 LINES = None #: Height of the terminal (None for unknown) 71 72 # Foreground colors: 73 BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' 74 75 # Background colors: 76 BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' 77 BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' 78 79 _STRING_CAPABILITIES = """ 80 BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 81 CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold 82 BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 83 HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() 84 _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() 85 _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() 86
87 - def __init__(self, term_stream=sys.stdout):
88 """ 89 Create a `TerminalController` and initialize its attributes 90 with appropriate values for the current terminal. 91 `term_stream` is the stream that will be used for terminal 92 output; if this stream is not a tty, then the terminal is 93 assumed to be a dumb terminal (i.e., have no capabilities). 94 """ 95 # Curses isn't available on all platforms 96 try: 97 import curses 98 except ImportError: 99 return 100 101 # If the stream isn't a tty, then assume it has no capabilities. 102 if not term_stream.isatty(): 103 return 104 105 # Check the terminal type. If we fail, then assume that the 106 # terminal has no capabilities. 107 try: 108 curses.setupterm() 109 except: 110 return 111 112 # Look up numeric capabilities. 113 self.COLS = curses.tigetnum('cols') 114 self.LINES = curses.tigetnum('lines') 115 116 # Look up string capabilities. 117 for capability in self._STRING_CAPABILITIES: 118 (attrib, cap_name) = capability.split('=') 119 setattr(self, attrib, self._tigetstr(cap_name) or '') 120 121 # Colors 122 set_fg = self._tigetstr('setf') 123 if set_fg: 124 for i, color in zip(range(len(self._COLORS)), self._COLORS): 125 setattr(self, color, curses.tparm(set_fg, i) or '') 126 set_fg_ansi = self._tigetstr('setaf') 127 if set_fg_ansi: 128 for i, color in zip(range(len(self._ANSICOLORS)), 129 self._ANSICOLORS): 130 setattr(self, color, curses.tparm(set_fg_ansi, i) or '') 131 set_bg = self._tigetstr('setb') 132 if set_bg: 133 for i, color in zip(range(len(self._COLORS)), self._COLORS): 134 setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') 135 set_bg_ansi = self._tigetstr('setab') 136 if set_bg_ansi: 137 for i, color in zip(range(len(self._ANSICOLORS)), 138 self._ANSICOLORS): 139 setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
140
141 - def _tigetstr(self, cap_name):
142 # String capabilities can include "delays" of the form "$<2>". 143 # For any modern terminal, we should be able to just ignore 144 # these, so strip them out. 145 import curses 146 cap = curses.tigetstr(cap_name) or '' 147 return re.sub(r'\$<\d+>[/*]?', '', cap)
148
149 - def render(self, template):
150 """ 151 Replace each $-substitutions in the given template string with 152 the corresponding terminal control string (if it's defined) or 153 '' (if it's not). 154 """ 155 return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
156
157 - def _render_sub(self, match):
158 s = match.group() 159 if s == '$$': 160 return s 161 else: 162 return getattr(self, s[2:-1])
163 164 ####################################################################### 165 # Example use case: progress bar 166 ####################################################################### 167 168
169 -class ProgressBar:
170 """ 171 A 3-line progress bar, which looks like:: 172 173 Header 174 20% [===========----------------------------------] 175 progress message 176 177 The progress bar is colored, if the terminal supports color 178 output; and adjusts to the width of the terminal. 179 """ 180 BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n' 181 HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' 182
183 - def __init__(self, term, header):
184 self.term = term 185 if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): 186 raise ValueError("Terminal isn't capable enough -- you " 187 "should use a simpler progress dispaly.") 188 self.width = self.term.COLS or 75 189 self.bar = term.render(self.BAR) 190 self.header = self.term.render(self.HEADER % header.center(self.width)) 191 self.cleared = 1 #: true if we haven't drawn the bar yet. 192 self.update(0, '')
193
194 - def update(self, percent, message):
195 if self.cleared: 196 sys.stdout.write(self.header) 197 self.cleared = 0 198 n = int((self.width-10)*percent) 199 sys.stdout.write( 200 self.term.BOL + self.term.UP + self.term.CLEAR_EOL + 201 (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) + 202 self.term.CLEAR_EOL + message.center(self.width))
203
204 - def clear(self):
205 if not self.cleared: 206 sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + 207 self.term.UP + self.term.CLEAR_EOL + 208 self.term.UP + self.term.CLEAR_EOL) 209 self.cleared = 1
210 211 if __name__ == '__main__': 212 term = TerminalController() 213 print term.render('${BOLD}${RED}Error:${NORMAL}'), 'paper is ripped' 214