1# SPDX-FileCopyrightText: 2020 GNOME Foundation 2# SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later 3 4import os 5import platform 6import sys 7import time 8 9 10def setup_output(): 11 try: 12 if platform.system().lower() == 'windows': 13 return os.isatty(sys.stdout.fileno()) 14 return os.isatty(sys.stdout.fileno()) and os.environ.get('TERM') != 'dumb' 15 except Exception: 16 return False 17 18 19def setup_debug(): 20 if os.environ.get('GIDOCGEN_DEBUG', '0') == '1': 21 return True 22 return False 23 24 25log_colorize_output = setup_output() 26log_debug = setup_debug() 27log_quiet = False 28log_fatal_warnings = False 29log_warnings_counter = 0 30log_epoch = 0 31 32colors = { 33 'NONE': "[0m", 34 'RED': "[1;31m", 35 'GREEN': "[1;32m", 36 'YELLOW': "[1;33m", 37 'BLUE': "[1;34m", 38 'LIGHT_GREY': "[1;37m", 39 'DARK_GREY': "[1;90m", 40} 41 42modifiers = { 43 'NONE': "[0m", 44 'DEFAULT': "[4;39m", 45 'BOLD_DEFAULT': "[1;39m", 46 'DIM_DEFAULT': "[2;39m", 47} 48 49 50logged_once = set() 51 52 53class AnsiEscape(object): 54 ''' 55 A string-like object that contains an ANSI escaped string. 56 ''' 57 char = '\033' 58 59 def __init__(self, *args, **kwargs): 60 self.text = kwargs.get('text', '') 61 self.color = kwargs.get('color', 'NONE') 62 self.mods = kwargs.get('mods', 'DEFAULT') 63 64 def __str__(self): 65 if self.mods != 'DEFAULT': 66 return f'{AnsiEscape.char}{modifiers[self.mods]}{self.text}{AnsiEscape.char}{modifiers["NONE"]}' 67 return f'{AnsiEscape.char}{colors[self.color]}{self.text}{AnsiEscape.char}{colors["NONE"]}' 68 69 70def color(text, color_id): 71 return f'\u001b[38;5;{color_id}m{text}\u001b[0m' 72 73 74def red(text): 75 return AnsiEscape(text=text, color='RED') 76 77 78def green(text): 79 return AnsiEscape(text=text, color='GREEN') 80 81 82def yellow(text): 83 return AnsiEscape(text=text, color='YELLOW') 84 85 86def blue(text): 87 return AnsiEscape(text=text, color='BLUE') 88 89 90def bold(text): 91 return AnsiEscape(text=text, mods='BOLD_DEFAULT') 92 93 94def dim(text): 95 return AnsiEscape(text=text, mods='DIM_DEFAULT') 96 97 98class Location(object): 99 ''' 100 A location object, pointing to a filename and a line. 101 ''' 102 def __init__(self, **kwargs): 103 self.filename = kwargs.get('filename', 'input') 104 self.line = kwargs.get('line', 0) 105 106 def __str__(self): 107 return f'{self.filename}:{self.line}:' 108 109 110def log_once(text, prefix=None, location=None): 111 ''' 112 Prints a line of text only once. 113 ''' 114 t = tuple(text, prefix, location) 115 if t in logged_once: 116 return 117 log(text, prefix, location) 118 logged_once.add(t) 119 120 121def set_quiet(quiet): 122 global log_quiet 123 log_quiet = quiet 124 125 126def set_fatal_warnings(fatal_warnings): 127 global log_fatal_warnings 128 log_fatal_warnings = fatal_warnings 129 130 131def set_log_epoch(epoch=0): 132 global log_epoch 133 if epoch == 0: 134 log_epoch = time.monotonic() 135 else: 136 log_epoch = epoch 137 138 139def log(text, prefix=None, location=None, out=None): 140 ''' 141 Prints a line of text using the given prefix and location. 142 143 @prefix: (optional): a prefix string, or an AnsiEscape object 144 @location: (optional): a location string, or a Location object 145 @out: (optional): a File object 146 ''' 147 res = [] 148 if prefix: 149 res += [str(prefix), ': '] 150 if location: 151 res += [str(location), ' '] 152 res += [text] 153 print(''.join(res), file=out) 154 155 156def error(text, location=None): 157 '''Prints an error message''' 158 log(text, prefix=red('ERROR'), location=location, out=sys.stderr) 159 sys.exit(1) 160 161 162def warning(text, location=None): 163 '''Prints a warning message''' 164 log(text, prefix=yellow('WARNING'), location=location, out=sys.stderr) 165 166 global log_warnings_counter 167 log_warnings_counter += 1 168 169 if log_fatal_warnings: 170 sys.exit(1) 171 172 173def info(text, location=None): 174 '''Prints an information message''' 175 if not log_quiet: 176 log(text, prefix=green('INFO'), location=location) 177 178 179def debug(text, location=None): 180 '''Prints a debug message''' 181 if log_debug: 182 log(text, prefix=dim('DEBUG'), location=location) 183 184 185def deprecation(text, location=None): 186 '''Prints a deprecation warning''' 187 log(text, prefix=blue('DEPRECATED'), location=location, out=sys.stderr) 188 global log_warnings_counter 189 log_warnings_counter += 1 190 191 192def checkpoint(prefix=None): 193 if log_quiet: 194 return 195 elapsed = (time.monotonic() - log_epoch) 196 msg = f"Elapsed time {elapsed:.3f} seconds" 197 if prefix is None: 198 prefix = green('INFO') 199 log(msg, prefix) 200 201 202def report(): 203 global log_warnings_counter 204 if log_quiet: 205 return 206 elapsed = (time.monotonic() - log_epoch) 207 report = [""] 208 report += [f"Elapsed time: {elapsed:.3f} seconds"] 209 report += [f"Total warnings: {log_warnings_counter}"] 210 if log_warnings_counter == 0: 211 return 0 212 return 1 213