diff --git a/bob/devtools/changelog.py b/bob/devtools/changelog.py index 5dba4fa0894f2bfc05b9f1bb393e6b215366d44d..aec6760bde66b60e731ae8ba07a875a3e0f7515b 100644 --- a/bob/devtools/changelog.py +++ b/bob/devtools/changelog.py @@ -5,12 +5,13 @@ import io import datetime -import logging -logger = logging.getLogger(__name__) import pytz import dateutil.parser +from .log import get_logger +logger = get_logger(__name__) + def parse_date(d): '''Parses any date supported by :py:func:`dateutil.parser.parse`''' diff --git a/bob/devtools/ci.py b/bob/devtools/ci.py index e30441316bd6a532573a722e6a26889ecf44a1b3..91ae88cc923087c1ecf200fa6f9a2181fedc8da7 100644 --- a/bob/devtools/ci.py +++ b/bob/devtools/ci.py @@ -4,12 +4,12 @@ '''Tools to help CI-based builds and artifact deployment''' -import logging -logger = logging.getLogger(__name__) - import git import distutils.version +from .log import get_logger +logger = get_logger(__name__) + def is_master(refname, tag, repodir): '''Tells if we're on the master branch via ref_name or tag diff --git a/bob/devtools/constants.py b/bob/devtools/constants.py index facc0b7e43a81d6f761495660ebda60def72b272..847a0b65fb6cdd7a374343aee639eaf8e716509e 100644 --- a/bob/devtools/constants.py +++ b/bob/devtools/constants.py @@ -6,11 +6,11 @@ import os import pkg_resources -import logging -logger = logging.getLogger(__name__) - from . import bootstrap +from .log import get_logger +logger = get_logger(__name__) + BASE_CONDARC = bootstrap._BASE_CONDARC '''Default setup for conda builds''' diff --git a/bob/devtools/log.py b/bob/devtools/log.py index 3cc7c7838299e2c4b9d8ecdde85cb2888ca73aed..50842dc7cf282d672df9e4cc06e146f336af2f8e 100644 --- a/bob/devtools/log.py +++ b/bob/devtools/log.py @@ -7,6 +7,10 @@ import sys import logging +import click +import termcolor + + # get the default root logger of Bob _logger = logging.getLogger('bob') @@ -28,6 +32,87 @@ _debug_info.addFilter(_InfoFilter()) _logger.addHandler(_debug_info) +COLORMAP = dict( + debug=dict(), + info=dict(attrs=['bold']), + warn=dict(color='yellow', attrs=['bold']), + warning=dict(color='yellow', attrs=['bold']), + error=dict(color='red'), + exception=dict(color='red', attrs=['bold']), + critical=dict(color='red', attrs=['bold']), + ) +'''Default color map for homogenized color display''' + + +def _supports_color(): + """ + Returns True if the running system's terminal supports color, and False + otherwise. + """ + plat = sys.platform + supported_platform = plat != 'Pocket PC' and (plat != 'win32' or + 'ANSICON' in os.environ) + # isatty is not always implemented, #6223. + is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + if not supported_platform or not is_a_tty: + return False + return True + + +class ColorLog(object): + '''Colorizes logging colors''' + + def __init__(self, logger): + self._log = logger + + def __getattr__(self, name): + if name in ['debug', 'info', 'warn', 'warning', 'error', 'exception', + 'critical']: + if _supports_color(): + return lambda s, *args: getattr(self._log, name)( + termcolor.colored(s, **COLORMAP[name]), *args) + else: + return lambda s, *args: getattr(self._log, name)(s, *args) + + return getattr(self._log, name) + + +def get_logger(name): + """Returns the default logger as setup by this module""" + + return ColorLog(logging.getLogger(name)) + + +def _echo(text, *args, **kwargs): + """Provides a colorized version of :py:func:`click.echo` (for terminals) + + The color is stripped off if outputting to a file or piping the results of + a command using this function. + + Parameters: + + text (str): The text to be printed + args (tuple): Tuple of attributes directly passed to + :py:func:`termcolor.colored` + kwargs (dict): Dictionary of attributes directly passed to + :py:func:`termcolor.colored` + """ + + click.echo(termcolor.colored(text, *args, **kwargs)) + + +def echo_normal(text): + """Color preset for normal text output for :py:func:`click.echo`""" + + _echo(text, 'green') + + +def echo_warning(text): + """Color preset for normal warning output for :py:func:`click.echo`""" + + _echo(text, **COLORMAP['warn']) + + # helper functions to instantiate and set-up logging def setup(logger_name, format="%(levelname)s:%(name)s@%(asctime)s: %(message)s"): @@ -68,7 +153,7 @@ def setup(logger_name, for handler in _logger.handlers: handler.setFormatter(formatter) - return logger + return ColorLog(logger) def set_verbosity_level(logger, level): diff --git a/bob/devtools/release.py b/bob/devtools/release.py index ba7e0e38c460acecfd949f084a9eb768328cadac..d760e2c2837155afe3d006a4b2060ddbbebdb3ed 100644 --- a/bob/devtools/release.py +++ b/bob/devtools/release.py @@ -9,8 +9,8 @@ import time import shutil import gitlab -import logging -logger = logging.getLogger(__name__) +from .log import get_logger +logger = get_logger(__name__) from distutils.version import StrictVersion diff --git a/bob/devtools/scripts/build.py b/bob/devtools/scripts/build.py index c539d7780efee10b4470dd9f29b3dbade59f7a54..52d4841169271d6d647e398dbef2c04e5e29d138 100644 --- a/bob/devtools/scripts/build.py +++ b/bob/devtools/scripts/build.py @@ -3,8 +3,6 @@ import os import sys -import logging -logger = logging.getLogger(__name__) import yaml import click @@ -12,7 +10,6 @@ import pkg_resources import conda_build.api from . import bdt -from ..log import verbosity_option from ..build import next_build_number, conda_arch, should_skip_build, \ get_rendered_metadata, get_parsed_recipe, make_conda_config, \ get_docserver_setup, get_env_directory @@ -20,6 +17,9 @@ from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND, \ SERVER, MATPLOTLIB_RCDIR, BASE_CONDARC from ..bootstrap import set_environment, get_channels +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) + @click.command(epilog=''' Examples: diff --git a/bob/devtools/scripts/caupdate.py b/bob/devtools/scripts/caupdate.py index 575c43adf839c904f7a249f44b580e44b8871c37..b6629a5ae09a56854a3f0135c965a9a90a97958b 100644 --- a/bob/devtools/scripts/caupdate.py +++ b/bob/devtools/scripts/caupdate.py @@ -1,13 +1,13 @@ #!/usr/bin/env python import os -import logging -logger = logging.getLogger(__name__) import click from . import bdt -from ..log import verbosity_option + +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) @click.command(epilog=''' diff --git a/bob/devtools/scripts/changelog.py b/bob/devtools/scripts/changelog.py index 1ad42af895aa1d64cf635fed2f7e5f0ebeff636a..0f3c1c2ae88e4b7f3266195d88993384a0881e00 100644 --- a/bob/devtools/scripts/changelog.py +++ b/bob/devtools/scripts/changelog.py @@ -4,17 +4,16 @@ import os import sys import datetime -import logging -logger = logging.getLogger(__name__) - import click from . import bdt -from ..log import verbosity_option from ..changelog import get_last_tag_date, write_tags_with_commits from ..changelog import parse_date from ..release import get_gitlab_instance +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) + @click.command(epilog=''' Examples: diff --git a/bob/devtools/scripts/ci.py b/bob/devtools/scripts/ci.py index 763cec4072280eb16656abb60a8225c320b9ca34..3ad13b14ce5cfcb22e5735cb7b8364ba43b29ab2 100644 --- a/bob/devtools/scripts/ci.py +++ b/bob/devtools/scripts/ci.py @@ -3,8 +3,6 @@ import os import re import glob -import logging -logger = logging.getLogger(__name__) import yaml import click @@ -13,9 +11,11 @@ import conda_build.api from click_plugins import with_plugins from . import bdt -from ..log import verbosity_option from ..constants import SERVER +from ..log import verbosity_option, get_logger, echo_normal +logger = get_logger(__name__) + @with_plugins(pkg_resources.iter_entry_points('bdt.ci.cli')) @click.group(cls=bdt.AliasedGroup) @@ -363,11 +363,11 @@ def base_build(order, python, dry_run): recipes = list(itertools.product([None], recipes)) for order, (pyver, recipe) in enumerate(recipes): - click.echo('\n' + (80*'=')) + echo_normal('\n' + (80*'=')) pytext = 'for python-%s ' % pyver if pyver is not None else '' - click.echo('Building "%s" %s(%d/%d)' % \ + echo_normal('Building "%s" %s(%d/%d)' % \ (recipe, pytext, order+1, len(recipes))) - click.echo((80*'=') + '\n') + echo_normal((80*'=') + '\n') if not os.path.exists(os.path.join(recipe, 'meta.yaml')): logger.info('Ignoring directory "%s" - no meta.yaml found' % recipe) continue diff --git a/bob/devtools/scripts/commitfile.py b/bob/devtools/scripts/commitfile.py index 66303197e33647e5c542fdbec644c8fbfa59e625..b02182475af85db6cf67fc9d8f607d81da9bafe7 100644 --- a/bob/devtools/scripts/commitfile.py +++ b/bob/devtools/scripts/commitfile.py @@ -1,15 +1,15 @@ #!/usr/bin/env python import os -import logging -logger = logging.getLogger(__name__) import click from . import bdt -from ..log import verbosity_option from ..release import get_gitlab_instance, update_files_with_mr +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) + @click.command(epilog=''' Examples: diff --git a/bob/devtools/scripts/create.py b/bob/devtools/scripts/create.py index 122e7ea67eb8e087bd0f2217b21659604d16feac..0d0b6b5efe1eb83b434106246ad65dcc875eec75 100644 --- a/bob/devtools/scripts/create.py +++ b/bob/devtools/scripts/create.py @@ -3,20 +3,20 @@ import os import sys -import logging -logger = logging.getLogger(__name__) import pkg_resources import click import yaml from . import bdt -from ..log import verbosity_option from ..build import parse_dependencies, conda_create, make_conda_config from ..constants import BASE_CONDARC, CONDA_BUILD_CONFIG, \ CONDA_RECIPE_APPEND, SERVER from ..bootstrap import set_environment, get_channels +from ..log import verbosity_option, get_logger, echo_normal +logger = get_logger(__name__) + @click.command(epilog=''' Examples: @@ -138,4 +138,4 @@ def create(name, recipe_dir, python, overwrite, condarc, use_local, config, deps = parse_dependencies(recipe_dir, conda_config) status = conda_create(conda, name, overwrite, condarc_options, deps, dry_run, use_local) - click.echo('Execute on your shell: "conda activate %s"' % name) + echo_normal('Execute on your shell: "conda activate %s"' % name) diff --git a/bob/devtools/scripts/dumpsphinx.py b/bob/devtools/scripts/dumpsphinx.py index b022e8a38b02911abac1a468032b8ce0d1341a42..51b399a864b31f2202b61b932e3d2a509e978a32 100644 --- a/bob/devtools/scripts/dumpsphinx.py +++ b/bob/devtools/scripts/dumpsphinx.py @@ -2,15 +2,14 @@ # -*- coding: utf-8 -*- -import logging -logger = logging.getLogger(__name__) - from sphinx.ext import intersphinx import click from . import bdt -from ..log import verbosity_option + +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) @click.command(epilog=''' diff --git a/bob/devtools/scripts/getpath.py b/bob/devtools/scripts/getpath.py index d70be09a9a45664c8d509fb53546ece1bcea769b..b8105a8e88ca742c3cbccbd321e49949714f9b34 100644 --- a/bob/devtools/scripts/getpath.py +++ b/bob/devtools/scripts/getpath.py @@ -1,15 +1,15 @@ #!/usr/bin/env python import os -import logging -logger = logging.getLogger(__name__) import click from . import bdt -from ..log import verbosity_option from ..release import get_gitlab_instance, download_path +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) + @click.command(epilog=''' Examples: diff --git a/bob/devtools/scripts/lasttag.py b/bob/devtools/scripts/lasttag.py index 63a4d224c1a6f9709aa790db0317e0a1c2e749b2..a6fdc114e1a8dd5af7f7c93e4e4649f6875f4d06 100644 --- a/bob/devtools/scripts/lasttag.py +++ b/bob/devtools/scripts/lasttag.py @@ -1,16 +1,17 @@ #!/usr/bin/env python import os -import logging -logger = logging.getLogger(__name__) import click +import gitlab from . import bdt -from ..log import verbosity_option from ..changelog import get_last_tag, parse_date from ..release import get_gitlab_instance +from ..log import verbosity_option, get_logger, echo_normal, echo_warning +logger = get_logger(__name__) + @click.command(epilog=''' Examples: @@ -38,11 +39,16 @@ def lasttag(package): gl = get_gitlab_instance() # we lookup the gitlab package once - use_package = gl.projects.get(package) - logger.info('Found gitlab project %s (id=%d)', - use_package.attributes['path_with_namespace'], use_package.id) - - tag = get_last_tag(use_package) - date = parse_date(tag.commit['committed_date']) - click.echo('Lastest tag for %s is %s (%s)' % \ - (package, tag.name, date.strftime('%Y-%m-%d %H:%M:%S'))) + try: + use_package = gl.projects.get(package) + logger.info('Found gitlab project %s (id=%d)', + use_package.attributes['path_with_namespace'], use_package.id) + + tag = get_last_tag(use_package) + date = parse_date(tag.commit['committed_date']) + echo_normal('%s: %s (%s)' % \ + (package, tag.name, date.strftime('%Y-%m-%d %H:%M:%S'))) + except gitlab.GitlabGetError as e: + logger.warn('Gitlab access error - package %s does not exist?', + package) + echo_warning('%s: unknown' % (package,)) diff --git a/bob/devtools/scripts/new.py b/bob/devtools/scripts/new.py index c6f7b8e2097c4d78823b5fd39248ea8b294e6cc5..2c8ce0b6fc9e7f9398ce4ed39f363e6324c5b711 100644 --- a/bob/devtools/scripts/new.py +++ b/bob/devtools/scripts/new.py @@ -2,16 +2,16 @@ import os import shutil -import logging import datetime -logger = logging.getLogger(__name__) import click import jinja2 import pkg_resources from . import bdt -from ..log import verbosity_option + +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) def copy_file(template, output_dir): diff --git a/bob/devtools/scripts/release.py b/bob/devtools/scripts/release.py index 87e1ca97711be9402ee21598ec154200de49a996..0a350f65b0ad61373c10db0296607d3bf19fddb3 100644 --- a/bob/devtools/scripts/release.py +++ b/bob/devtools/scripts/release.py @@ -4,17 +4,17 @@ import os -import logging -logger = logging.getLogger(__name__) - import click from . import bdt -from ..log import verbosity_option from ..release import release_bob, parse_and_process_package_changelog from ..release import release_package, wait_for_pipeline_to_finish from ..release import get_gitlab_instance +from ..log import verbosity_option, get_logger +logger = get_logger(__name__) + + @click.command(epilog=''' Examples: diff --git a/bob/devtools/scripts/visibility.py b/bob/devtools/scripts/visibility.py index ed682f01009981290e803008fd5c73b007e47045..263fc144efe1fd4984296891a5197c48eaad633c 100644 --- a/bob/devtools/scripts/visibility.py +++ b/bob/devtools/scripts/visibility.py @@ -6,13 +6,12 @@ import sys import click import gitlab -import logging -logger = logging.getLogger(__name__) - from . import bdt -from ..log import verbosity_option from ..release import get_gitlab_instance +from ..log import verbosity_option, get_logger, echo_normal, echo_warning +logger = get_logger(__name__) + @click.command(epilog=''' Examples: @@ -48,12 +47,12 @@ def visibility(target, group): # reads package list or considers name to be a package name if os.path.exists(target) and os.path.isfile(target): - logger.info('Reading package names from file %s...', target) + logger.debug('Reading package names from file %s...', target) with open(target, 'rt') as f: packages = [k.strip() for k in f.readlines() if k.strip() and not \ k.strip().startswith('#')] else: - logger.info('Assuming %s is a package name (file does not ' \ + logger.debug('Assuming %s is a package name (file does not ' \ 'exist)...', target) packages = [target] @@ -66,11 +65,11 @@ def visibility(target, group): # retrieves the gitlab package object try: use_package = gl.projects.get(package) - logger.info('Found gitlab project %s (id=%d)', + logger.debug('Found gitlab project %s (id=%d)', use_package.attributes['path_with_namespace'], use_package.id) - click.echo('%s: %s' % (package, + echo_normal('%s: %s' % (package, use_package.attributes['visibility'].lower())) except gitlab.GitlabGetError as e: logger.warn('Gitlab access error - package %s does not exist?', package) - click.echo('%s: unknown' % (package,)) + echo_warning('%s: unknown' % (package,)) diff --git a/bob/devtools/webdav3/client.py b/bob/devtools/webdav3/client.py index b1850d705ac91c92c4ecffb01a0ed4685231bd3c..2fe7a588eb932a67987a9ac110068bb0dfc5588f 100644 --- a/bob/devtools/webdav3/client.py +++ b/bob/devtools/webdav3/client.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 import functools -import logging import os import shutil import threading @@ -15,6 +14,10 @@ from .connection import * from .exceptions import * from .urn import Urn +from ..log import get_logger +logger = get_logger(__name__) + + try: from urllib.parse import unquote, urlsplit except ImportError: @@ -22,7 +25,6 @@ except ImportError: from urlparse import urlsplit __version__ = "0.2" -log = logging.getLogger(__name__) def listdir(directory): diff --git a/conda/meta.yaml b/conda/meta.yaml index 09cd327e9f57649e6bb1aac48c8571f9b6202b02..79ea741739dbc75fcdf601896bc692ce35778892 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -47,6 +47,7 @@ requirements: - twine - lxml - jinja2 + - termcolor test: requires: diff --git a/setup.py b/setup.py index d1e0f1abbe471654a8b0f7d2f6111d7fed83764e..b37d09221538026d9bbe7e06e68b6741d84730c8 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ requires = [ 'twine', 'lxml', 'jinja2', + 'termcolor', ] setup(