diff --git a/conan/api/output.py b/conan/api/output.py index 38f8c5c7257..1d4b5cc5496 100644 --- a/conan/api/output.py +++ b/conan/api/output.py @@ -1,13 +1,12 @@ -import json import sys from colorama import Fore, Style from conans.client.userio import color_enabled +from conans.errors import ConanException from conans.util.env import get_env LEVEL_QUIET = 80 # -q - LEVEL_ERROR = 70 # Errors LEVEL_WARNING = 60 # Warnings LEVEL_NOTICE = 50 # Important messages to attract user attention. @@ -17,14 +16,6 @@ LEVEL_TRACE = 10 # -vvv Fine-grained messages with very low-level implementation details -# Singletons -conan_output_level = LEVEL_STATUS - - -def log_level_allowed(level): - return conan_output_level <= level - - class Color(object): """ Wrapper around colorama colors that are undefined in importing """ @@ -57,6 +48,9 @@ class Color(object): class ConanOutput: + # Singleton + _conan_output_level = LEVEL_STATUS + def __init__(self, scope=""): self.stream = sys.stderr self._scope = scope @@ -64,6 +58,30 @@ def __init__(self, scope=""): # stream to capture it, so colorama is not there to strip the color bytes self._color = color_enabled(self.stream) + @classmethod + def define_log_level(cls, v): + levels = {"quiet": LEVEL_QUIET, # -vquiet 80 + "error": LEVEL_ERROR, # -verror 70 + "warning": LEVEL_WARNING, # -vwaring 60 + "notice": LEVEL_NOTICE, # -vnotice 50 + "status": LEVEL_STATUS, # -vstatus 40 + None: LEVEL_STATUS, # -v 40 + "verbose": LEVEL_VERBOSE, # -vverbose 30 + "debug": LEVEL_DEBUG, # -vdebug 20 + "v": LEVEL_DEBUG, # -vv 20 + "trace": LEVEL_TRACE, # -vtrace 10 + "vv": LEVEL_TRACE, # -vvv 10 + } + + level = levels.get(v) + if not level: + raise ConanException(f"Invalid argument '-v{v}'") + cls._conan_output_level = level + + @classmethod + def level_allowed(cls, level): + return cls._conan_output_level <= level + @property def color(self): return self._color @@ -84,7 +102,7 @@ def writeln(self, data, fg=None, bg=None): return self.write(data, fg, bg, newline=True) def write(self, data, fg=None, bg=None, newline=False): - if conan_output_level > LEVEL_NOTICE: + if self._conan_output_level > LEVEL_NOTICE: return self if self._color and (fg or bg): data = "%s%s%s%s" % (fg or '', bg or '', data, Style.RESET_ALL) @@ -98,24 +116,15 @@ def write(self, data, fg=None, bg=None, newline=False): def rewrite_line(self, line): tmp_color = self._color self._color = False - TOTAL_SIZE = 70 - LIMIT_SIZE = TOTAL_SIZE // 2 - 3 - if len(line) > TOTAL_SIZE: - line = line[0:LIMIT_SIZE] + " ... " + line[-LIMIT_SIZE:] - self.write("\r%s%s" % (line, " " * (TOTAL_SIZE - len(line)))) + total_size = 70 + limit_size = total_size // 2 - 3 + if len(line) > total_size: + line = line[0:limit_size] + " ... " + line[-limit_size:] + self.write("\r%s%s" % (line, " " * (total_size - len(line)))) self.stream.flush() self._color = tmp_color - def _write_message(self, msg, level_str, fg=None, bg=None): - if conan_output_level == LEVEL_QUIET: - return - - def json_encoder(_obj): - try: - return json.dumps(_obj) - except TypeError: - return repr(_obj) - + def _write_message(self, msg, fg=None, bg=None): if isinstance(msg, dict): # For traces we can receive a dict already, we try to transform then into more natural # text @@ -138,58 +147,58 @@ def json_encoder(_obj): self.stream.write("{}\n".format(ret)) def trace(self, msg): - if log_level_allowed(LEVEL_TRACE): - self._write_message(msg, "TRACE", fg=Color.BRIGHT_WHITE) + if self._conan_output_level <= LEVEL_TRACE: + self._write_message(msg, fg=Color.BRIGHT_WHITE) return self def debug(self, msg): - if log_level_allowed(LEVEL_DEBUG): - self._write_message(msg, "DEBUG") + if self._conan_output_level <= LEVEL_DEBUG: + self._write_message(msg) return self def verbose(self, msg, fg=None, bg=None): - if log_level_allowed(LEVEL_VERBOSE): - self._write_message(msg, "VERBOSE", fg=fg, bg=bg) + if self._conan_output_level <= LEVEL_VERBOSE: + self._write_message(msg, fg=fg, bg=bg) return self def status(self, msg, fg=None, bg=None): - if log_level_allowed(LEVEL_STATUS): - self._write_message(msg, "STATUS", fg=fg, bg=bg) + if self._conan_output_level <= LEVEL_STATUS: + self._write_message(msg, fg=fg, bg=bg) return self # Remove in a later refactor of all the output.info calls info = status def title(self, msg): - if log_level_allowed(LEVEL_NOTICE): - self._write_message("\n======== {} ========".format(msg), "NOTICE", + if self._conan_output_level <= LEVEL_NOTICE: + self._write_message("\n======== {} ========".format(msg), fg=Color.BRIGHT_MAGENTA) return self def subtitle(self, msg): - if log_level_allowed(LEVEL_NOTICE): - self._write_message("\n-------- {} --------".format(msg), "NOTICE", + if self._conan_output_level <= LEVEL_NOTICE: + self._write_message("\n-------- {} --------".format(msg), fg=Color.BRIGHT_MAGENTA) return self def highlight(self, msg): - if log_level_allowed(LEVEL_NOTICE): - self._write_message(msg, "NOTICE", fg=Color.BRIGHT_MAGENTA) + if self._conan_output_level <= LEVEL_NOTICE: + self._write_message(msg, fg=Color.BRIGHT_MAGENTA) return self def success(self, msg): - if log_level_allowed(LEVEL_NOTICE): - self._write_message(msg, "NOTICE", fg=Color.BRIGHT_GREEN) + if self._conan_output_level <= LEVEL_NOTICE: + self._write_message(msg, fg=Color.BRIGHT_GREEN) return self def warning(self, msg): - if log_level_allowed(LEVEL_WARNING): - self._write_message("WARN: {}".format(msg), "WARN", Color.YELLOW) + if self._conan_output_level <= LEVEL_WARNING: + self._write_message("WARN: {}".format(msg), Color.YELLOW) return self def error(self, msg): - if log_level_allowed(LEVEL_ERROR): - self._write_message("ERROR: {}".format(msg), "ERROR", Color.RED) + if self._conan_output_level <= LEVEL_ERROR: + self._write_message("ERROR: {}".format(msg), Color.RED) return self def flush(self): diff --git a/conan/cli/cli.py b/conan/cli/cli.py index add48fa414c..d1a39d39312 100644 --- a/conan/cli/cli.py +++ b/conan/cli/cli.py @@ -171,8 +171,7 @@ def run(self, *args): command.run(self._conan_api, self._commands[command_argument].parser, args[0][1:]) except Exception as e: # must be a local-import to get updated value - from conan.api.output import conan_output_level - if conan_output_level <= LEVEL_TRACE: + if ConanOutput.level_allowed(LEVEL_TRACE): print(traceback.format_exc()) self._conan2_migrate_recipe_msg(e) raise diff --git a/conan/cli/command.py b/conan/cli/command.py index 0af629ce883..db6e7661e5f 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -1,6 +1,7 @@ import argparse import textwrap +from conan.api.output import ConanOutput from conan.errors import ConanException @@ -103,33 +104,9 @@ def __init__(self, *args, **kwargs): def parse_args(self, args=None, namespace=None): args = super().parse_args(args) - self._process_log_level_args(args) + ConanOutput.define_log_level(args.v) return args - @staticmethod - def _process_log_level_args(args): - from conan.api import output - from conan.api.output import LEVEL_QUIET, LEVEL_ERROR, LEVEL_WARNING, LEVEL_NOTICE, \ - LEVEL_STATUS, LEVEL_VERBOSE, LEVEL_DEBUG, LEVEL_TRACE - - levels = {"quiet": LEVEL_QUIET, # -vquiet 80 - "error": LEVEL_ERROR, # -verror 70 - "warning": LEVEL_WARNING, # -vwaring 60 - "notice": LEVEL_NOTICE, # -vnotice 50 - "status": LEVEL_STATUS, # -vstatus 40 - None: LEVEL_STATUS, # -v 40 - "verbose": LEVEL_VERBOSE, # -vverbose 30 - "debug": LEVEL_DEBUG, # -vdebug 20 - "v": LEVEL_DEBUG, # -vv 20 - "trace": LEVEL_TRACE, # -vtrace 10 - "vv": LEVEL_TRACE, # -vvv 10 - } - - level = levels.get(args.v) - if not level: - raise ConanException(f"Invalid argument '-v{args.v}'") - output.conan_output_level = level - class ConanCommand(BaseConanCommand): def __init__(self, method, group=None, formatters=None): diff --git a/conans/errors.py b/conans/errors.py index 31957028f32..90add7ad41d 100644 --- a/conans/errors.py +++ b/conans/errors.py @@ -40,8 +40,8 @@ def conanfile_exception_formatter(conanfile_name, func_name): """ def _raise_conanfile_exc(e): - from conan.api.output import LEVEL_DEBUG, conan_output_level - if conan_output_level <= LEVEL_DEBUG: + from conan.api.output import LEVEL_DEBUG, ConanOutput + if ConanOutput.level_allowed(LEVEL_DEBUG): import traceback raise ConanExceptionInUserConanfileMethod(traceback.format_exc()) m = _format_conanfile_exception(conanfile_name, func_name, e)