Completed
Pull Request — develop (#148)
by Jace
05:26 queued 01:44
created

configure_logging()   C

Complexity

Conditions 7

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
dl 0
loc 42
ccs 34
cts 34
cp 1
crap 7
rs 5.5
c 0
b 0
f 0
1
"""Common exceptions, classes, and functions."""
2
3 1
import os
4 1
import sys
5 1
import argparse
6 1
import logging
7
8 1
from . import settings
9
10
11 1
class WideHelpFormatter(argparse.HelpFormatter):
12
    """Command-line help text formatter with wider help text."""
13
14 1
    def __init__(self, *args, **kwargs):
15 1
        super().__init__(*args, max_help_position=40, **kwargs)
16
17
18 1
class WarningFormatter(logging.Formatter):
19
    """Logging formatter that displays verbose formatting for WARNING+."""
20
21 1
    def __init__(self, default_format, verbose_format, *args, **kwargs):
22 1
        super().__init__(*args, **kwargs)
23 1
        self.default_format = default_format
24 1
        self.verbose_format = verbose_format
25
26 1
    def format(self, record):
27
        """A hack to change the formatting style dynamically."""
28
        # pylint: disable=protected-access
0 ignored issues
show
introduced by
Locally disabling protected-access (W0212)
Loading history...
29 1
        if record.levelno > logging.INFO:
30 1
            self._style._fmt = self.verbose_format
31
        else:
32 1
            self._style._fmt = self.default_format
33 1
        return super().format(record)
34
35
36 1
def positive_int(value):
37
    """Custom `int` that must be positive."""
38 1
    value = int(value)
39 1
    if value < 1:
40 1
        raise TypeError
41 1
    return value
42
43
44 1
class _Config:
45
    """Share configuration options."""
46
47 1
    MAX_VERBOSITY = 4
48
49 1
    verbosity = 0
50 1
    indent_level = 0
51
52
53 1
def configure_logging(count=0):
54
    """Configure logging using the provided verbosity count."""
55 1
    if count == -1:
56 1
        level = settings.QUIET_LOGGING_LEVEL
57 1
        default_format = settings.DEFAULT_LOGGING_FORMAT
58 1
        verbose_format = settings.LEVELED_LOGGING_FORMAT
59 1
    elif count == 0:
60 1
        level = settings.DEFAULT_LOGGING_LEVEL
61 1
        default_format = settings.DEFAULT_LOGGING_FORMAT
62 1
        verbose_format = settings.LEVELED_LOGGING_FORMAT
63 1
    elif count == 1:
64 1
        level = settings.VERBOSE_LOGGING_LEVEL
65 1
        default_format = settings.VERBOSE_LOGGING_FORMAT
66 1
        verbose_format = settings.VERBOSE_LOGGING_FORMAT
67 1
    elif count == 2:
68 1
        level = settings.VERBOSE2_LOGGING_LEVEL
69 1
        default_format = settings.VERBOSE_LOGGING_FORMAT
70 1
        verbose_format = settings.VERBOSE_LOGGING_FORMAT
71 1
    elif count == 3:
72 1
        level = settings.VERBOSE2_LOGGING_LEVEL
73 1
        default_format = settings.VERBOSE2_LOGGING_FORMAT
74 1
        verbose_format = settings.VERBOSE2_LOGGING_FORMAT
75
    else:
76 1
        level = settings.VERBOSE2_LOGGING_LEVEL - 1
77 1
        default_format = settings.VERBOSE2_LOGGING_FORMAT
78 1
        verbose_format = settings.VERBOSE2_LOGGING_FORMAT
79
80
    # Set a custom formatter
81 1
    logging.basicConfig(level=level)
82 1
    logging.captureWarnings(True)
83 1
    formatter = WarningFormatter(default_format, verbose_format,
84
                                 datefmt=settings.LOGGING_DATEFMT)
85 1
    logging.root.handlers[0].setFormatter(formatter)
86 1
    logging.getLogger('yorm').setLevel(max(level, settings.YORM_LOGGING_LEVEL))
87
88
    # Warn about excessive verbosity
89 1
    if count > _Config.MAX_VERBOSITY:
90 1
        msg = "Maximum verbosity level is {}".format(_Config.MAX_VERBOSITY)
91 1
        logging.warning(msg)
92 1
        _Config.verbosity = _Config.MAX_VERBOSITY
93
    else:
94 1
        _Config.verbosity = count
95
96
97 1
def indent():
98
    """Increase the indent of future output lines."""
99 1
    _Config.indent_level += 1
100
101
102 1
def dedent(level=None):
103
    """Decrease (or reset) the indent of future output lines."""
104 1
    if level is None:
105 1
        _Config.indent_level = max(0, _Config.indent_level - 1)
106
    else:
107 1
        _Config.indent_level = level
108
109
110 1
def newline():
111
    """Write a new line to standard output."""
112 1
    show("")
113
114
115 1
def show(*messages, color=None,
116
         file=sys.stdout, log=logging.getLogger(__name__)):
117
    """Write to standard output or error if enabled."""
118 1
    for message in messages:
119 1
        if _Config.verbosity == 0:
120 1
            text = ' ' * 2 * _Config.indent_level + style(message, color)
121 1
            print(text, file=file)
122 1
        elif _Config.verbosity >= 1:
123 1
            message = message.strip()
124 1
            if message and log:
125 1
                if color == 'error':
126 1
                    log.error(message)
127
                else:
128 1
                    log.info(message)
129
130
131 1
BOLD = '\033[1m'
132 1
RED = '\033[31m'
133 1
GREEN = '\033[32m'
134 1
YELLOW = '\033[33m'
135 1
BLUE = '\033[34m'
136 1
MAGENTA = '\033[35m'
137 1
CYAN = '\033[36m'
138 1
WHITE = '\033[37m'
139 1
RESET = '\033[0m'
140
141 1
COLORS = dict(
142
    path='',
143
    git_rev=BOLD + BLUE,
144
    git_dirty=BOLD + MAGENTA,
145
    git_changes=YELLOW,
146
    shell=BOLD + GREEN,
147
    shell_info=BOLD + MAGENTA,
148
    shell_output=CYAN,
149
    shell_error=YELLOW,
150
    message=BOLD + WHITE,
151
    success=BOLD + GREEN,
152
    error=BOLD + RED,
153
)
154
155
156 1
def style(msg, name=None, *, _color_support=False):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
157 1
    is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
158 1
    supports_ansi = sys.platform != 'win32' or 'ANSICON' in os.environ
159 1
    if not (is_tty and supports_ansi) and not _color_support:
160 1
        return msg
161
162 1
    if name == 'shell':
163 1
        return msg.replace("$ ", COLORS.get(name) + "$ " + RESET)
164
165 1
    color = COLORS.get(name)
166 1
    if color:
167 1
        return color + msg + RESET
168
169 1
    if msg:
170 1
        assert color is not None, \
171
            "Unknown style name requested: {!r}".format(name)
172
173
    return msg
174