1*4882a593Smuzhiyun# Copyright (c) 2011 The Chromium OS Authors. 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun 6*4882a593Smuzhiyun"""Terminal utilities 7*4882a593Smuzhiyun 8*4882a593SmuzhiyunThis module handles terminal interaction including ANSI color codes. 9*4882a593Smuzhiyun""" 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunfrom __future__ import print_function 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport os 14*4882a593Smuzhiyunimport sys 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun# Selection of when we want our output to be colored 17*4882a593SmuzhiyunCOLOR_IF_TERMINAL, COLOR_ALWAYS, COLOR_NEVER = range(3) 18*4882a593Smuzhiyun 19*4882a593Smuzhiyun# Initially, we are set up to print to the terminal 20*4882a593Smuzhiyunprint_test_mode = False 21*4882a593Smuzhiyunprint_test_list = [] 22*4882a593Smuzhiyun 23*4882a593Smuzhiyunclass PrintLine: 24*4882a593Smuzhiyun """A line of text output 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun Members: 27*4882a593Smuzhiyun text: Text line that was printed 28*4882a593Smuzhiyun newline: True to output a newline after the text 29*4882a593Smuzhiyun colour: Text colour to use 30*4882a593Smuzhiyun """ 31*4882a593Smuzhiyun def __init__(self, text, newline, colour): 32*4882a593Smuzhiyun self.text = text 33*4882a593Smuzhiyun self.newline = newline 34*4882a593Smuzhiyun self.colour = colour 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun def __str__(self): 37*4882a593Smuzhiyun return 'newline=%s, colour=%s, text=%s' % (self.newline, self.colour, 38*4882a593Smuzhiyun self.text) 39*4882a593Smuzhiyun 40*4882a593Smuzhiyundef Print(text='', newline=True, colour=None): 41*4882a593Smuzhiyun """Handle a line of output to the terminal. 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun In test mode this is recorded in a list. Otherwise it is output to the 44*4882a593Smuzhiyun terminal. 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun Args: 47*4882a593Smuzhiyun text: Text to print 48*4882a593Smuzhiyun newline: True to add a new line at the end of the text 49*4882a593Smuzhiyun colour: Colour to use for the text 50*4882a593Smuzhiyun """ 51*4882a593Smuzhiyun if print_test_mode: 52*4882a593Smuzhiyun print_test_list.append(PrintLine(text, newline, colour)) 53*4882a593Smuzhiyun else: 54*4882a593Smuzhiyun if colour: 55*4882a593Smuzhiyun col = Color() 56*4882a593Smuzhiyun text = col.Color(colour, text) 57*4882a593Smuzhiyun print(text, end='') 58*4882a593Smuzhiyun if newline: 59*4882a593Smuzhiyun print() 60*4882a593Smuzhiyun else: 61*4882a593Smuzhiyun sys.stdout.flush() 62*4882a593Smuzhiyun 63*4882a593Smuzhiyundef SetPrintTestMode(): 64*4882a593Smuzhiyun """Go into test mode, where all printing is recorded""" 65*4882a593Smuzhiyun global print_test_mode 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun print_test_mode = True 68*4882a593Smuzhiyun 69*4882a593Smuzhiyundef GetPrintTestLines(): 70*4882a593Smuzhiyun """Get a list of all lines output through Print() 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun Returns: 73*4882a593Smuzhiyun A list of PrintLine objects 74*4882a593Smuzhiyun """ 75*4882a593Smuzhiyun global print_test_list 76*4882a593Smuzhiyun 77*4882a593Smuzhiyun ret = print_test_list 78*4882a593Smuzhiyun print_test_list = [] 79*4882a593Smuzhiyun return ret 80*4882a593Smuzhiyun 81*4882a593Smuzhiyundef EchoPrintTestLines(): 82*4882a593Smuzhiyun """Print out the text lines collected""" 83*4882a593Smuzhiyun for line in print_test_list: 84*4882a593Smuzhiyun if line.colour: 85*4882a593Smuzhiyun col = Color() 86*4882a593Smuzhiyun print(col.Color(line.colour, line.text), end='') 87*4882a593Smuzhiyun else: 88*4882a593Smuzhiyun print(line.text, end='') 89*4882a593Smuzhiyun if line.newline: 90*4882a593Smuzhiyun print() 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun 93*4882a593Smuzhiyunclass Color(object): 94*4882a593Smuzhiyun """Conditionally wraps text in ANSI color escape sequences.""" 95*4882a593Smuzhiyun BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) 96*4882a593Smuzhiyun BOLD = -1 97*4882a593Smuzhiyun BRIGHT_START = '\033[1;%dm' 98*4882a593Smuzhiyun NORMAL_START = '\033[22;%dm' 99*4882a593Smuzhiyun BOLD_START = '\033[1m' 100*4882a593Smuzhiyun RESET = '\033[0m' 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun def __init__(self, colored=COLOR_IF_TERMINAL): 103*4882a593Smuzhiyun """Create a new Color object, optionally disabling color output. 104*4882a593Smuzhiyun 105*4882a593Smuzhiyun Args: 106*4882a593Smuzhiyun enabled: True if color output should be enabled. If False then this 107*4882a593Smuzhiyun class will not add color codes at all. 108*4882a593Smuzhiyun """ 109*4882a593Smuzhiyun try: 110*4882a593Smuzhiyun self._enabled = (colored == COLOR_ALWAYS or 111*4882a593Smuzhiyun (colored == COLOR_IF_TERMINAL and 112*4882a593Smuzhiyun os.isatty(sys.stdout.fileno()))) 113*4882a593Smuzhiyun except: 114*4882a593Smuzhiyun self._enabled = False 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun def Start(self, color, bright=True): 117*4882a593Smuzhiyun """Returns a start color code. 118*4882a593Smuzhiyun 119*4882a593Smuzhiyun Args: 120*4882a593Smuzhiyun color: Color to use, .e.g BLACK, RED, etc. 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun Returns: 123*4882a593Smuzhiyun If color is enabled, returns an ANSI sequence to start the given 124*4882a593Smuzhiyun color, otherwise returns empty string 125*4882a593Smuzhiyun """ 126*4882a593Smuzhiyun if self._enabled: 127*4882a593Smuzhiyun base = self.BRIGHT_START if bright else self.NORMAL_START 128*4882a593Smuzhiyun return base % (color + 30) 129*4882a593Smuzhiyun return '' 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun def Stop(self): 132*4882a593Smuzhiyun """Retruns a stop color code. 133*4882a593Smuzhiyun 134*4882a593Smuzhiyun Returns: 135*4882a593Smuzhiyun If color is enabled, returns an ANSI color reset sequence, 136*4882a593Smuzhiyun otherwise returns empty string 137*4882a593Smuzhiyun """ 138*4882a593Smuzhiyun if self._enabled: 139*4882a593Smuzhiyun return self.RESET 140*4882a593Smuzhiyun return '' 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun def Color(self, color, text, bright=True): 143*4882a593Smuzhiyun """Returns text with conditionally added color escape sequences. 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun Keyword arguments: 146*4882a593Smuzhiyun color: Text color -- one of the color constants defined in this 147*4882a593Smuzhiyun class. 148*4882a593Smuzhiyun text: The text to color. 149*4882a593Smuzhiyun 150*4882a593Smuzhiyun Returns: 151*4882a593Smuzhiyun If self._enabled is False, returns the original text. If it's True, 152*4882a593Smuzhiyun returns text with color escape sequences based on the value of 153*4882a593Smuzhiyun color. 154*4882a593Smuzhiyun """ 155*4882a593Smuzhiyun if not self._enabled: 156*4882a593Smuzhiyun return text 157*4882a593Smuzhiyun if color == self.BOLD: 158*4882a593Smuzhiyun start = self.BOLD_START 159*4882a593Smuzhiyun else: 160*4882a593Smuzhiyun base = self.BRIGHT_START if bright else self.NORMAL_START 161*4882a593Smuzhiyun start = base % (color + 30) 162*4882a593Smuzhiyun return start + text + self.RESET 163