diff --git a/avocado/core/loader.py b/avocado/core/loader.py index da400a10d7e20fd3674c1a9db2ee3a4f1754e870..63635fe6868db80490d1bb363b7ffd4abf11e4c9 100644 --- a/avocado/core/loader.py +++ b/avocado/core/loader.py @@ -98,7 +98,7 @@ class TestLoaderProxy(object): name = plugin.name mapping = plugin.get_type_label_mapping() # Using __func__ to avoid problem with different term_supp instances - healthy_func = getattr(output.term_support.healthy_str, '__func__') + healthy_func = getattr(output.TERM_SUPPORT.healthy_str, '__func__') types = [mapping[_[0]] for _ in plugin.get_decorator_mapping().iteritems() if _[1].__func__ is healthy_func] @@ -416,13 +416,13 @@ class FileLoader(TestLoader): @staticmethod def get_decorator_mapping(): - return {test.SimpleTest: output.term_support.healthy_str, - test.NotATest: output.term_support.warn_header_str, - test.MissingTest: output.term_support.fail_header_str, - BrokenSymlink: output.term_support.fail_header_str, - AccessDeniedPath: output.term_support.fail_header_str, - test.Test: output.term_support.healthy_str, - FilteredOut: output.term_support.warn_header_str} + return {test.SimpleTest: output.TERM_SUPPORT.healthy_str, + test.NotATest: output.TERM_SUPPORT.warn_header_str, + test.MissingTest: output.TERM_SUPPORT.fail_header_str, + BrokenSymlink: output.TERM_SUPPORT.fail_header_str, + AccessDeniedPath: output.TERM_SUPPORT.fail_header_str, + test.Test: output.TERM_SUPPORT.healthy_str, + FilteredOut: output.TERM_SUPPORT.warn_header_str} def discover(self, url, which_tests=DEFAULT): """ @@ -780,7 +780,7 @@ class ExternalLoader(TestLoader): @staticmethod def get_decorator_mapping(): - return {test.ExternalRunnerTest: output.term_support.healthy_str} + return {test.ExternalRunnerTest: output.TERM_SUPPORT.healthy_str} loader = TestLoaderProxy() diff --git a/avocado/core/output.py b/avocado/core/output.py index 590de38494aadabeee6959b74b0f533520e17e5e..10dec9861ab285ec15a6663124ad26ee40a8c944 100644 --- a/avocado/core/output.py +++ b/avocado/core/output.py @@ -49,6 +49,163 @@ BUILTIN_STREAM_SETS = {'all': 'all builtin streams', 'none': 'disable console logging completely'} +#: Transparently handles colored terminal, when one is used +TERM_SUPPORT = None + + +class TermSupport(object): + + """ + Class to help applications to colorize their outputs for terminals. + + This will probe the current terminal and colorize ouput only if the + stdout is in a tty or the terminal type is recognized. + """ + + COLOR_BLUE = '\033[94m' + COLOR_GREEN = '\033[92m' + COLOR_YELLOW = '\033[93m' + COLOR_RED = '\033[91m' + COLOR_DARKGREY = '\033[90m' + + CONTROL_END = '\033[0m' + + MOVE_BACK = '\033[1D' + MOVE_FORWARD = '\033[1C' + + ESCAPE_CODES = [COLOR_BLUE, COLOR_GREEN, COLOR_YELLOW, COLOR_RED, + COLOR_DARKGREY, CONTROL_END, MOVE_BACK, MOVE_FORWARD] + + def __init__(self): + self.HEADER = self.COLOR_BLUE + self.PASS = self.COLOR_GREEN + self.SKIP = self.COLOR_YELLOW + self.FAIL = self.COLOR_RED + self.INTERRUPT = self.COLOR_RED + self.ERROR = self.COLOR_RED + self.WARN = self.COLOR_YELLOW + self.PARTIAL = self.COLOR_YELLOW + self.ENDC = self.CONTROL_END + self.LOWLIGHT = self.COLOR_DARKGREY + self.enabled = True + allowed_terms = ['linux', 'xterm', 'xterm-256color', 'vt100', 'screen', + 'screen-256color'] + term = os.environ.get("TERM") + colored = settings.get_value('runner.output', 'colored', + key_type='bool') + if not colored or not os.isatty(1) or term not in allowed_terms: + self.disable() + + def disable(self): + """ + Disable colors from the strings output by this class. + """ + self.enabled = False + self.HEADER = '' + self.PASS = '' + self.SKIP = '' + self.FAIL = '' + self.INTERRUPT = '' + self.ERROR = '' + self.WARN = '' + self.PARTIAL = '' + self.ENDC = '' + self.LOWLIGHT = '' + self.MOVE_BACK = '' + self.MOVE_FORWARD = '' + + def header_str(self, msg): + """ + Print a header string (blue colored). + + If the output does not support colors, just return the original string. + """ + return self.HEADER + msg + self.ENDC + + def fail_header_str(self, msg): + """ + Print a fail header string (red colored). + + If the output does not support colors, just return the original string. + """ + return self.FAIL + msg + self.ENDC + + def warn_header_str(self, msg): + """ + Print a warning header string (yellow colored). + + If the output does not support colors, just return the original string. + """ + return self.WARN + msg + self.ENDC + + def healthy_str(self, msg): + """ + Print a healthy string (green colored). + + If the output does not support colors, just return the original string. + """ + return self.PASS + msg + self.ENDC + + def partial_str(self, msg): + """ + Print a string that denotes partial progress (yellow colored). + + If the output does not support colors, just return the original string. + """ + return self.PARTIAL + msg + self.ENDC + + def pass_str(self): + """ + Print a pass string (green colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.PASS + 'PASS' + self.ENDC + + def skip_str(self): + """ + Print a skip string (yellow colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.SKIP + 'SKIP' + self.ENDC + + def fail_str(self): + """ + Print a fail string (red colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.FAIL + 'FAIL' + self.ENDC + + def error_str(self): + """ + Print a error string (red colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.ERROR + 'ERROR' + self.ENDC + + def interrupt_str(self): + """ + Print an interrupt string (red colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.INTERRUPT + 'INTERRUPT' + self.ENDC + + def warn_str(self): + """ + Print an warning string (yellow colored). + + If the output does not support colors, just return the original string. + """ + return self.MOVE_BACK + self.WARN + 'WARN' + self.ENDC + + +TERM_SUPPORT = TermSupport() + + def early_start(): """ Replace all outputs with in-memory handlers @@ -84,7 +241,7 @@ def reconfigure(args): # Reconfigure stream loggers global STDOUT global STDERR - if getattr(args, "paginator", False) == "on" and is_colored_term(): + if getattr(args, "paginator", False) == "on" and TERM_SUPPORT.enabled: STDOUT = Paginator() STDERR = STDOUT enabled = getattr(args, "show", None) @@ -205,11 +362,11 @@ class ProgressStreamHandler(logging.StreamHandler): if record.levelno < logging.INFO: # Most messages are INFO pass elif record.levelno < logging.WARNING: - msg = term_support.header_str(msg) + msg = TERM_SUPPORT.header_str(msg) elif record.levelno < logging.ERROR: - msg = term_support.warn_header_str(msg) + msg = TERM_SUPPORT.warn_header_str(msg) else: - msg = term_support.fail_header_str(msg) + msg = TERM_SUPPORT.fail_header_str(msg) stream = self.stream skip_newline = False if hasattr(record, 'skip_newline'): @@ -315,166 +472,6 @@ def disable_log_handler(logger): logger.propagate = False -def is_colored_term(): - allowed_terms = ['linux', 'xterm', 'xterm-256color', 'vt100', 'screen', - 'screen-256color'] - term = os.environ.get("TERM") - colored = settings.get_value('runner.output', 'colored', - key_type='bool') - if ((not colored) or (not os.isatty(1)) or (term not in allowed_terms)): - return False - else: - return True - - -class TermSupport(object): - - COLOR_BLUE = '\033[94m' - COLOR_GREEN = '\033[92m' - COLOR_YELLOW = '\033[93m' - COLOR_RED = '\033[91m' - COLOR_DARKGREY = '\033[90m' - - CONTROL_END = '\033[0m' - - MOVE_BACK = '\033[1D' - MOVE_FORWARD = '\033[1C' - - ESCAPE_CODES = [COLOR_BLUE, COLOR_GREEN, COLOR_YELLOW, COLOR_RED, - COLOR_DARKGREY, CONTROL_END, MOVE_BACK, MOVE_FORWARD] - - """ - Class to help applications to colorize their outputs for terminals. - - This will probe the current terminal and colorize ouput only if the - stdout is in a tty or the terminal type is recognized. - """ - - def __init__(self): - self.HEADER = self.COLOR_BLUE - self.PASS = self.COLOR_GREEN - self.SKIP = self.COLOR_YELLOW - self.FAIL = self.COLOR_RED - self.INTERRUPT = self.COLOR_RED - self.ERROR = self.COLOR_RED - self.WARN = self.COLOR_YELLOW - self.PARTIAL = self.COLOR_YELLOW - self.ENDC = self.CONTROL_END - self.LOWLIGHT = self.COLOR_DARKGREY - self.enabled = True - if not is_colored_term(): - self.disable() - - def disable(self): - """ - Disable colors from the strings output by this class. - """ - self.enabled = False - self.HEADER = '' - self.PASS = '' - self.SKIP = '' - self.FAIL = '' - self.INTERRUPT = '' - self.ERROR = '' - self.WARN = '' - self.PARTIAL = '' - self.ENDC = '' - self.LOWLIGHT = '' - self.MOVE_BACK = '' - self.MOVE_FORWARD = '' - - def header_str(self, msg): - """ - Print a header string (blue colored). - - If the output does not support colors, just return the original string. - """ - return self.HEADER + msg + self.ENDC - - def fail_header_str(self, msg): - """ - Print a fail header string (red colored). - - If the output does not support colors, just return the original string. - """ - return self.FAIL + msg + self.ENDC - - def warn_header_str(self, msg): - """ - Print a warning header string (yellow colored). - - If the output does not support colors, just return the original string. - """ - return self.WARN + msg + self.ENDC - - def healthy_str(self, msg): - """ - Print a healthy string (green colored). - - If the output does not support colors, just return the original string. - """ - return self.PASS + msg + self.ENDC - - def partial_str(self, msg): - """ - Print a string that denotes partial progress (yellow colored). - - If the output does not support colors, just return the original string. - """ - return self.PARTIAL + msg + self.ENDC - - def pass_str(self): - """ - Print a pass string (green colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.PASS + 'PASS' + self.ENDC - - def skip_str(self): - """ - Print a skip string (yellow colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.SKIP + 'SKIP' + self.ENDC - - def fail_str(self): - """ - Print a fail string (red colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.FAIL + 'FAIL' + self.ENDC - - def error_str(self): - """ - Print a error string (red colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.ERROR + 'ERROR' + self.ENDC - - def interrupt_str(self): - """ - Print an interrupt string (red colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.INTERRUPT + 'INTERRUPT' + self.ENDC - - def warn_str(self): - """ - Print an warning string (yellow colored). - - If the output does not support colors, just return the original string. - """ - return self.MOVE_BACK + self.WARN + 'WARN' + self.ENDC - - -term_support = TermSupport() - - class LoggingFile(object): """ @@ -547,11 +544,11 @@ class Throbber(object): """ STEPS = ['-', '\\', '|', '/'] # Only print a throbber when we're on a terminal - if term_support.enabled: - MOVES = [term_support.MOVE_BACK + STEPS[0], - term_support.MOVE_BACK + STEPS[1], - term_support.MOVE_BACK + STEPS[2], - term_support.MOVE_BACK + STEPS[3]] + if TERM_SUPPORT.enabled: + MOVES = [TERM_SUPPORT.MOVE_BACK + STEPS[0], + TERM_SUPPORT.MOVE_BACK + STEPS[1], + TERM_SUPPORT.MOVE_BACK + STEPS[2], + TERM_SUPPORT.MOVE_BACK + STEPS[3]] else: MOVES = ['', '', '', ''] diff --git a/avocado/core/result.py b/avocado/core/result.py index f76b042637c8e0de1bf8858699fd47f03124ba36..b896eac56935628d40ad256cbb79d2fb5116654c 100644 --- a/avocado/core/result.py +++ b/avocado/core/result.py @@ -310,19 +310,19 @@ class HumanTestResult(TestResult): status = state["status"] if status == "TEST_NA": status = "SKIP" - mapping = {'PASS': output.term_support.PASS, - 'ERROR': output.term_support.ERROR, - 'FAIL': output.term_support.FAIL, - 'SKIP': output.term_support.SKIP, - 'WARN': output.term_support.WARN, - 'INTERRUPTED': output.term_support.INTERRUPT} - self.log.debug(output.term_support.MOVE_BACK + mapping[status] + - status + output.term_support.ENDC) + mapping = {'PASS': output.TERM_SUPPORT.PASS, + 'ERROR': output.TERM_SUPPORT.ERROR, + 'FAIL': output.TERM_SUPPORT.FAIL, + 'SKIP': output.TERM_SUPPORT.SKIP, + 'WARN': output.TERM_SUPPORT.WARN, + 'INTERRUPTED': output.TERM_SUPPORT.INTERRUPT} + self.log.debug(output.TERM_SUPPORT.MOVE_BACK + mapping[status] + + status + output.TERM_SUPPORT.ENDC) def notify_progress(self, progress=False): if progress: - color = output.term_support.PASS + color = output.TERM_SUPPORT.PASS else: - color = output.term_support.PARTIAL + color = output.TERM_SUPPORT.PARTIAL self.log.debug(color + self.__throbber.render() + - output.term_support.ENDC, extra={"skip_newline": True}) + output.TERM_SUPPORT.ENDC, extra={"skip_newline": True}) diff --git a/avocado/core/tree.py b/avocado/core/tree.py index cd0c0eb0b05863cda4a0c1633cbc1a6a5ac4dc20..c39ea0710390fc1dbfaac3c15470a7e08d612308 100644 --- a/avocado/core/tree.py +++ b/avocado/core/tree.py @@ -544,9 +544,9 @@ class OutputValue(object): # only container pylint: disable=R0903 def __str__(self): return "%s%s@%s:%s%s" % (self.value, - output.term_support.LOWLIGHT, + output.TERM_SUPPORT.LOWLIGHT, self.yaml, self.node.path, - output.term_support.ENDC) + output.TERM_SUPPORT.ENDC) class OutputList(list): # only container pylint: disable=R0903 @@ -566,8 +566,8 @@ class OutputList(list): # only container pylint: disable=R0903 self.yamls + other.yamls) def __str__(self): - color = output.term_support.LOWLIGHT - cend = output.term_support.ENDC + color = output.TERM_SUPPORT.LOWLIGHT + cend = output.TERM_SUPPORT.ENDC return ' + '.join("%s%s@%s:%s%s" % (_[0], color, _[1], _[2].path, cend) for _ in itertools.izip(self, self.yamls, diff --git a/avocado/plugins/list.py b/avocado/plugins/list.py index 5d161f147fc89d746c1608ce4de4ba17f5c288b2..ad972253f47c4264a17fd616170ab07d60e67853 100644 --- a/avocado/plugins/list.py +++ b/avocado/plugins/list.py @@ -104,8 +104,8 @@ class TestLister(object): def _display(self, test_matrix, stats): header = None if self.args.verbose: - header = (output.term_support.header_str('Type'), - output.term_support.header_str('Test')) + header = (output.TERM_SUPPORT.header_str('Type'), + output.TERM_SUPPORT.header_str('Test')) for line in astring.iter_tabular_output(test_matrix, header=header): self.log.debug(line) diff --git a/avocado/plugins/multiplex.py b/avocado/plugins/multiplex.py index 927a361476c3de54240c35d3ace359b6a2888411..7527887969a890001fe699bb765665e63480bfdc 100644 --- a/avocado/plugins/multiplex.py +++ b/avocado/plugins/multiplex.py @@ -121,8 +121,8 @@ class Multiplex(CLICmd): if not args.debug: paths = ', '.join([x.path for x in tpl]) else: - color = output.term_support.LOWLIGHT - cend = output.term_support.ENDC + color = output.TERM_SUPPORT.LOWLIGHT + cend = output.TERM_SUPPORT.ENDC paths = ', '.join(["%s%s@%s%s" % (_.name, color, getattr(_, 'yaml', "Unknown"),