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