Package entropy :: Module output

Source Code for Module entropy.output

  1  #!/usr/bin/python 
  2  ''' 
  3      # DESCRIPTION: 
  4      # Text formatting and colouring tools 
  5   
  6      Copyright 1998-2004 Gentoo Foundation 
  7      # $Id: output.py 4906 2006-11-01 23:55:29Z zmedico $ 
  8      Copyright (C) 2007-2008 Fabio Erculiani 
  9   
 10      This program is free software; you can redistribute it and/or modify 
 11      it under the terms of the GNU General Public License as published by 
 12      the Free Software Foundation; either version 2 of the License, or 
 13      (at your option) any later version. 
 14   
 15      This program is distributed in the hope that it will be useful, 
 16      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18      GNU General Public License for more details. 
 19   
 20      You should have received a copy of the GNU General Public License 
 21      along with this program; if not, write to the Free Software 
 22      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 23  ''' 
 24   
 25  import sys, os 
 26  import curses 
 27  from entropy.const import etpUi 
 28  from entropy.i18n import _ 
 29  stuff = {} 
 30  stuff['cols'] = 30 
 31  try: 
 32      curses.setupterm() 
 33      stuff['cols'] = curses.tigetnum('cols') 
 34  except: 
 35      pass 
 36  stuff['cleanline'] = "" 
37 -def setcols():
38 stuff['cleanline'] = "" 39 count = stuff['cols'] 40 while count: 41 stuff['cleanline'] += ' ' 42 count -= 1
43 setcols() 44 stuff['cursor'] = False 45 stuff['ESC'] = chr(27) 46 47 havecolor=1 48 global dotitles 49 dotitles=1 50 51 esc_seq = "\x1b[" 52 53 g_attr = {} 54 g_attr["normal"] = 0 55 56 g_attr["bold"] = 1 57 g_attr["faint"] = 2 58 g_attr["standout"] = 3 59 g_attr["underline"] = 4 60 g_attr["blink"] = 5 61 g_attr["overline"] = 6 # Why is overline actually useful? 62 g_attr["reverse"] = 7 63 g_attr["invisible"] = 8 64 65 g_attr["no-attr"] = 22 66 g_attr["no-standout"] = 23 67 g_attr["no-underline"] = 24 68 g_attr["no-blink"] = 25 69 g_attr["no-overline"] = 26 70 g_attr["no-reverse"] = 27 71 # 28 isn't defined? 72 # 29 isn't defined? 73 g_attr["black"] = 30 74 g_attr["red"] = 31 75 g_attr["green"] = 32 76 g_attr["yellow"] = 33 77 g_attr["blue"] = 34 78 g_attr["magenta"] = 35 79 g_attr["cyan"] = 36 80 g_attr["white"] = 37 81 # 38 isn't defined? 82 g_attr["default"] = 39 83 g_attr["bg_black"] = 40 84 g_attr["bg_red"] = 41 85 g_attr["bg_green"] = 42 86 g_attr["bg_yellow"] = 43 87 g_attr["bg_blue"] = 44 88 g_attr["bg_magenta"] = 45 89 g_attr["bg_cyan"] = 46 90 g_attr["bg_white"] = 47 91 g_attr["bg_default"] = 49 92 93 94 # make_seq("blue", "black", "normal")
95 -def color(fg, bg="default", attr=["normal"]):
96 mystr = esc_seq[:] + "%02d" % g_attr[fg] 97 for x in [bg]+attr: 98 mystr += ";%02d" % g_attr[x] 99 return mystr+"m"
100 101 102 103 codes={} 104 codes["reset"] = esc_seq + "39;49;00m" 105 106 codes["bold"] = esc_seq + "01m" 107 codes["faint"] = esc_seq + "02m" 108 codes["standout"] = esc_seq + "03m" 109 codes["underline"] = esc_seq + "04m" 110 codes["blink"] = esc_seq + "05m" 111 codes["overline"] = esc_seq + "06m" # Who made this up? Seriously. 112 113 ansi_color_codes = [] 114 for x in xrange(30, 38): 115 ansi_color_codes.append("%im" % x) 116 ansi_color_codes.append("%i;01m" % x) 117 118 rgb_ansi_colors = ['0x000000', '0x555555', '0xAA0000', '0xFF5555', '0x00AA00', 119 '0x55FF55', '0xAA5500', '0xFFFF55', '0x0000AA', '0x5555FF', '0xAA00AA', 120 '0xFF55FF', '0x00AAAA', '0x55FFFF', '0xAAAAAA', '0xFFFFFF'] 121 122 for x in xrange(len(rgb_ansi_colors)): 123 codes[rgb_ansi_colors[x]] = esc_seq + ansi_color_codes[x] 124 125 del x 126 127 codes["black"] = codes["0x000000"] 128 codes["darkgray"] = codes["0x555555"] 129 130 codes["red"] = codes["0xFF5555"] 131 codes["darkred"] = codes["0xAA0000"] 132 133 codes["green"] = codes["0x55FF55"] 134 codes["darkgreen"] = codes["0x00AA00"] 135 136 codes["yellow"] = codes["0xFFFF55"] 137 codes["brown"] = codes["0xAA5500"] 138 139 codes["blue"] = codes["0x5555FF"] 140 codes["darkblue"] = codes["0x0000AA"] 141 142 codes["fuchsia"] = codes["0xFF55FF"] 143 codes["purple"] = codes["0xAA00AA"] 144 145 codes["turquoise"] = codes["0x55FFFF"] 146 codes["teal"] = codes["0x00AAAA"] 147 148 codes["white"] = codes["0xFFFFFF"] 149 codes["lightgray"] = codes["0xAAAAAA"] 150 151 codes["darkteal"] = codes["turquoise"] 152 codes["darkyellow"] = codes["brown"] 153 codes["fuscia"] = codes["fuchsia"] 154 codes["white"] = codes["bold"] 155 156 # Colors from /sbin/functions.sh 157 codes["GOOD"] = codes["green"] 158 codes["WARN"] = codes["yellow"] 159 codes["BAD"] = codes["red"] 160 codes["HILITE"] = codes["teal"] 161 codes["BRACKET"] = codes["blue"] 162 163 # Portage functions 164 codes["INFORM"] = codes["darkgreen"] 165 codes["UNMERGE_WARN"] = codes["red"] 166 codes["MERGE_LIST_PROGRESS"] = codes["yellow"] 167
168 -def xtermTitle(mystr, raw=False):
169 if dotitles and "TERM" in os.environ and sys.stderr.isatty(): 170 myt=os.environ["TERM"] 171 legal_terms = ["xterm","Eterm","aterm","rxvt","screen","kterm","rxvt-unicode","gnome"] 172 if myt in legal_terms: 173 if not raw: 174 mystr = "\x1b]0;%s\x07" % mystr 175 try: 176 sys.stderr.write(mystr) 177 except UnicodeEncodeError: 178 sys.stderr.write(mystr.encode('utf-8')) 179 sys.stderr.flush()
180 181 default_xterm_title = None 182
183 -def xtermTitleReset():
184 global default_xterm_title 185 if default_xterm_title is None: 186 prompt_command = os.getenv('PROMPT_COMMAND') 187 if prompt_command == "": 188 default_xterm_title = "" 189 elif prompt_command is not None: 190 from entropy.tools import getstatusoutput 191 default_xterm_title = getstatusoutput(prompt_command)[1] 192 else: 193 pwd = os.getenv('PWD','') 194 home = os.getenv('HOME', '') 195 if home != '' and pwd.startswith(home): 196 pwd = '~' + pwd[len(home):] 197 default_xterm_title = '\x1b]0;%s@%s:%s\x07' % ( 198 os.getenv('LOGNAME', ''), 199 os.getenv('HOSTNAME', '').split('.', 1)[0], 200 pwd) 201 xtermTitle(default_xterm_title, raw=True)
202
203 -def notitles():
204 "turn off title setting" 205 global dotitles 206 dotitles=0
207
208 -def nocolor():
209 "turn off colorization" 210 os.environ['ETP_NO_COLOR'] = "1" 211 global havecolor 212 havecolor=0
213 214 nc = os.getenv("ETP_NO_COLOR") 215 if nc: 216 nocolor() 217
218 -def resetColor():
219 return codes["reset"]
220
221 -def colorize(color_key, text):
222 if etpUi['mute']: 223 return text 224 global havecolor 225 if havecolor: 226 return codes[color_key] + text + codes["reset"] 227 return text
228
229 -def bold(text):
230 return colorize("bold", text)
231
232 -def white(text):
233 return colorize("white", text)
234
235 -def teal(text):
236 return colorize("teal", text)
237
238 -def turquoise(text):
239 return colorize("turquoise", text)
240
241 -def darkteal(text):
242 return colorize("darkteal", text)
243
244 -def fuscia(text):
245 return colorize("fuscia", text)
246
247 -def fuchsia(text):
248 return colorize("fuchsia", text)
249
250 -def purple(text):
251 return colorize("purple", text)
252
253 -def blue(text):
254 return colorize("blue", text)
255
256 -def darkblue(text):
257 return colorize("darkblue", text)
258
259 -def green(text):
260 return colorize("green", text)
261
262 -def darkgreen(text):
263 return colorize("darkgreen", text)
264
265 -def yellow(text):
266 return colorize("yellow", text)
267
268 -def brown(text):
269 return colorize("brown", text)
270
271 -def darkyellow(text):
272 return colorize("darkyellow", text)
273
274 -def red(text):
275 return colorize("red", text)
276
277 -def darkred(text):
278 return colorize("darkred", text)
279
280 -def create_color_func(color_key):
281 def derived_func(*args): 282 newargs = list(args) 283 newargs.insert(0, color_key) 284 return colorize(*newargs)
285 return derived_func 286
287 -def enlightenatom(atom):
288 out = atom.split("/") 289 return blue(out[0])+"/"+red(out[1])
290 295 def orig_myfunc_desc(x): 296 return x 297 298 for item in data: 299 300 myfunc = orig_myfunc 301 myfunc_desc = orig_myfunc_desc 302 303 if not item: 304 writechar("\n") 305 else: 306 n_ident = item[0] 307 name = item[1] 308 n_d_ident = item[2] 309 desc = item[3] 310 311 if n_ident == 0: 312 writechar(" ") 313 # setup identation 314 while n_ident > 0: 315 n_ident -= 1 316 writechar("\t") 317 n_ident = item[0] 318 319 # write name 320 if n_ident == 0: 321 myfunc = darkgreen 322 elif n_ident == 1: 323 myfunc = blue 324 myfunc_desc = darkgreen 325 elif n_ident == 2: 326 if not name.startswith("--"): 327 myfunc = red 328 myfunc_desc = brown 329 elif n_ident == 3: 330 myfunc = darkblue 331 myfunc_desc = purple 332 try: 333 print myfunc(name), 334 except UnicodeEncodeError: 335 print myfunc(name.encode('utf-8')), 336 337 # write desc 338 if desc: 339 while n_d_ident > 0: 340 n_d_ident -= 1 341 writechar("\t") 342 try: 343 print myfunc_desc(desc), 344 except UnicodeEncodeError: 345 print myfunc_desc(desc.encode('utf-8')), 346 writechar("\n") 347
348 -def reset_cursor():
349 if havecolor: 350 sys.stdout.write(stuff['ESC'] + '[2K') 351 flush_stdouterr()
352
353 -def flush_stdouterr():
354 sys.stdout.flush() 355 sys.stderr.flush()
356 376 396 416 418 if etpUi['mute']: 419 return 420 writechar("\r") 421 try: 422 print msg 423 except UnicodeEncodeError: 424 print msg.encode('utf-8') 425 flush_stdouterr() 426
427 -def writechar(char):
428 if etpUi['mute']: 429 return 430 try: 431 sys.stdout.write(char) 432 sys.stdout.flush() 433 except IOError, e: 434 if e.errno == 32: 435 return 436 raise
437
438 -def readtext(request, password = False):
439 xtermTitle(_("Entropy needs your attention")) 440 if password: 441 from getpass import getpass 442 try: 443 text = getpass(request+" ") 444 except UnicodeEncodeError: 445 text = getpass(request.encode('utf-8')+" ") 446 else: 447 try: 448 print request,"", 449 except UnicodeEncodeError: 450 print request.encode('utf-8'),"", 451 flush_stdouterr() 452 text = my_raw_input() 453 return text
454
455 -def my_raw_input(txt = ''):
456 if txt: 457 try: 458 print darkgreen(txt), 459 except UnicodeEncodeError: 460 print darkgreen(txt.encode('utf-8')), 461 flush_stdouterr() 462 response = '' 463 while 1: 464 y = sys.stdin.read(1) 465 if y in ('\n','\r',): break 466 response += y 467 flush_stdouterr() 468 return response
469
470 -class TextInterface:
471 472 # @input text: text to write 473 # @input back: write on on the same line? 474 # @input importance: 475 # values: 0,1,2,3 (latter is a blocker - popup menu on a GUI env) 476 # used to specify information importance, 0<important<2 477 # @input type: 478 # values: "info, warning, error" 479 # 480 # @input count: 481 # if you need to print an incremental count ( 100/250...101/251..) 482 # just pass count = [first integer,second integer] or even a tuple! 483 # @input header: 484 # text header (decoration?), that's it 485 # 486 # @input footer: 487 # text footer (decoration?), that's it 488 # 489 # @input percent: 490 # if percent is True: count will be treating as a percentual count[0]/count[1]*100 491 # 492 # feel free to reimplement this
493 - def updateProgress(self, text, header = "", footer = "", back = False, importance = 0, type = "info", count = [], percent = False):
494 if (etpUi['quiet']) or (etpUi['mute']): 495 return 496 497 flush_stdouterr() 498 499 myfunc = print_info 500 if type == "warning": 501 myfunc = print_warning 502 elif type == "error": 503 myfunc = print_error 504 505 count_str = "" 506 if count: 507 if len(count) > 1: 508 if percent: 509 count_str = " ("+str(round((float(count[0])/count[1])*100,1))+"%) " 510 else: 511 count_str = " (%s/%s) " % (red(str(count[0])),blue(str(count[1])),) 512 if importance == 0: 513 myfunc(header+count_str+text+footer, back = back, flush = False) 514 elif importance == 1: 515 myfunc(header+count_str+text+footer, back = back, flush = False) 516 elif importance in (2,3): 517 myfunc(header+count_str+text+footer, back = back, flush = False) 518 519 flush_stdouterr()
520 521 # @input question: question to do 522 # 523 # @input importance: 524 # values: 0,1,2 (latter is a blocker - popup menu on a GUI env) 525 # used to specify information importance, 0<important<2 526 # 527 # @input responses: 528 # list of options whose users can choose between 529 # 530 # feel free to reimplement this
531 - def askQuestion(self, question, importance = 0, responses = ["Yes","No"]):
532 533 colours = [green, red, blue, darkgreen, darkred, darkblue, brown, purple] 534 colours += colours[:] 535 if len(responses) > len(colours): 536 try: 537 from entropy.exceptions import IncorrectParameter 538 except ImportError: 539 from exceptionTools import IncorrectParameter 540 raise IncorrectParameter("IncorrectParameter: %s = %s" % (_("maximum responses length"),len(colours),)) 541 try: 542 print darkgreen(question), 543 except UnicodeEncodeError: 544 print darkgreen(question.encode('utf-8')), 545 flush_stdouterr() 546 try: 547 while True: 548 xtermTitle(_("Entropy got a question for you")) 549 flush_stdouterr() 550 response = my_raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") 551 flush_stdouterr() 552 for key in responses: 553 # An empty response will match the first value in responses. 554 if response.upper() == key[:len(response)].upper(): 555 xtermTitleReset() 556 return key 557 ''' 558 try: 559 print "%s '%s'" % (_("I cannot understand"),response,), 560 except UnicodeEncodeError: 561 print "%s '%s'" % (_("I cannot understand"),response.encode('utf-8'),), 562 ''' 563 flush_stdouterr() 564 except (EOFError, KeyboardInterrupt): 565 print "%s." % (_("Interrupted"),) 566 xtermTitleReset() 567 raise SystemExit(100) 568 xtermTitleReset() 569 flush_stdouterr()
570 571 ''' 572 @ title: title of the input box 573 @ input_parameters: [ 574 ('identifier 1','input text 1',input_verification_callback,False), 575 ('password','Password',input_verification_callback,True), 576 ('item_3',('checkbox','Checkbox option (boolean request) - please choose',),input_verification_callback,True), 577 ('item_4',('combo',('Select your favorite option',['option 1', 'option 2', 'option 3']),),input_verification_callback,True), 578 ('item_4',('list',('Setup your list',['default list item 1', 'default list item 2']),),input_verification_callback,True) 579 ] 580 @ cancel_button: show cancel button ? 581 @ output: dictionary as follows: 582 {'identifier 1': result, 'identifier 2': result} 583 '''
584 - def inputBox(self, title, input_parameters, cancel_button = True):
585 results = {} 586 if title: 587 try: 588 print title 589 except UnicodeEncodeError: 590 print title.encode('utf-8') 591 flush_stdouterr() 592 593 def option_chooser(option_data): 594 mydict = {} 595 counter = 1 596 option_text, option_list = option_data 597 self.updateProgress(option_text) 598 for item in option_list: 599 mydict[counter] = item 600 txt = "[%s] %s" % (darkgreen(str(counter)), blue(item),) 601 self.updateProgress(txt) 602 counter += 1 603 while 1: 604 myresult = readtext("%s:" % (_('Selected number'),)).decode('utf-8') 605 try: 606 myresult = int(myresult) 607 except ValueError: 608 continue 609 selected = mydict.get(myresult) 610 if selected != None: 611 return myresult, selected
612 613 def list_editor(option_data, can_cancel, callback): 614 615 def selaction(): 616 self.updateProgress('') 617 self.updateProgress(darkred(_("Please select an option"))) 618 if can_cancel: 619 self.updateProgress(" ("+blue("-1")+") "+darkred(_("Discard all"))) 620 self.updateProgress(" ("+blue("0")+") "+darkgreen(_("Confirm"))) 621 self.updateProgress(" ("+blue("1")+") "+brown(_("Add item"))) 622 self.updateProgress(" ("+blue("2")+") "+darkblue(_("Remove item"))) 623 self.updateProgress(" ("+blue("3")+") "+darkgreen(_("Show current list"))) 624 # wait user interaction 625 self.updateProgress('') 626 action = readtext(darkgreen(_("Your choice (type a number and press enter):"))+" ") 627 return action
628 629 mydict = {} 630 counter = 1 631 valid_actions = [0,1,2,3] 632 if can_cancel: valid_actions.insert(0,-1) 633 option_text, option_list = option_data 634 txt = "%s:" % (blue(option_text),) 635 self.updateProgress(txt) 636 637 for item in option_list: 638 mydict[counter] = item 639 txt = "[%s] %s" % (darkgreen(str(counter)), blue(item),) 640 self.updateProgress(txt) 641 counter += 1 642 643 def show_current_list(): 644 for key in sorted(mydict): 645 txt = "[%s] %s" % (darkgreen(str(key)), blue(mydict[key]),) 646 self.updateProgress(txt) 647 648 while 1: 649 try: 650 action = int(selaction()) 651 except (ValueError,TypeError,): 652 self.updateProgress(_("You don't have typed a number."), type = "warning") 653 continue 654 if action not in valid_actions: 655 self.updateProgress(_("Invalid action."), type = "warning") 656 continue 657 if action == -1: 658 raise KeyboardInterrupt 659 elif action == 0: 660 break 661 elif action == 1: 662 while 1: 663 try: 664 s_el = readtext(darkred(_("String to add:"))+" ") 665 if not callback(s_el): 666 raise ValueError 667 mydict[counter] = s_el 668 counter += 1 669 except (ValueError,): 670 self.updateProgress(_("Invalid string."), type = "warning") 671 continue 672 break 673 show_current_list() 674 continue 675 elif action == 2: 676 while 1: 677 try: 678 s_el = int(readtext(darkred(_("Element number to remove:"))+" ")) 679 if s_el not in mydict: 680 raise ValueError 681 del mydict[s_el] 682 except (ValueError,TypeError,): 683 self.updateProgress(_("Invalid element."), type = "warning") 684 continue 685 break 686 show_current_list() 687 continue 688 elif action == 3: 689 show_current_list() 690 continue 691 break 692 693 mylist = [mydict[x] for x in sorted(mydict)] 694 return mylist 695 696 for identifier, input_text, callback, password in input_parameters: 697 while 1: 698 use_cb = True 699 try: 700 if isinstance(input_text,tuple): 701 myresult = False 702 input_type, data = input_text 703 if input_type == "checkbox": 704 answer = self.askQuestion(data) 705 if answer == "Yes": myresult = True 706 elif input_type == "combo": 707 myresult = option_chooser(data) 708 elif input_type == "list": 709 use_cb = False 710 myresult = list_editor(data, cancel_button, callback) 711 else: 712 myresult = readtext(input_text+":", password = password).decode('utf-8') 713 except (KeyboardInterrupt,EOFError,): 714 if not cancel_button: # use with care 715 continue 716 return None 717 valid = True 718 if use_cb: 719 valid = callback(myresult) 720 if valid: 721 results[identifier] = myresult 722 break 723 return results 724 725 # useful for reimplementation 726 # in this wait you can send a signal to a widget (total progress bar?)
727 - def cycleDone(self):
728 return
729
730 - def setTitle(self, title):
731 xtermTitle(title)
732
733 - def setTotalCycles(self, total):
734 return
735
736 - def outputInstanceTest(self):
737 return
738
739 - def nocolor(self):
740 nocolor()
741
742 - def notitles(self):
743 notitles()
744