Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

some refactor for ConanOutput, reducing module scope to class scope #13696

Merged
merged 1 commit into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 55 additions & 46 deletions conan/api/output.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
"""
Expand Down Expand Up @@ -57,13 +48,40 @@ class Color(object):


class ConanOutput:
# Singleton
_conan_output_level = LEVEL_STATUS

def __init__(self, scope=""):
self.stream = sys.stderr
self._scope = scope
# FIXME: This is needed because in testing we are redirecting the sys.stderr to a buffer
# 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
Expand All @@ -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)
Expand 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
Expand All @@ -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):
Expand Down
3 changes: 1 addition & 2 deletions conan/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 2 additions & 25 deletions conan/cli/command.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import argparse
import textwrap

from conan.api.output import ConanOutput
from conan.errors import ConanException


Expand Down Expand Up @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions conans/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down