Get rid of the colorlog dependency
colorlog was problematic for various reasons: - Not commonly packaged for Linux distributions - Calling colorama.init() automatically on import - Not supporting {foo} log formatting - Not supporting an easy way to turn colors off Instead we now do the log coloring by hand, which is simpler and means everyone will have colored logs.
This commit is contained in:
parent
a9a853baf0
commit
c64e5c9bd5
@ -46,6 +46,8 @@ Changed
|
|||||||
not be.
|
not be.
|
||||||
- Show favicons as window icon with `tabs-are-windows` set.
|
- Show favicons as window icon with `tabs-are-windows` set.
|
||||||
- `:bind <key>` without a command now shows the existing binding
|
- `:bind <key>` without a command now shows the existing binding
|
||||||
|
- The optional `colorlog` dependency got removed, as qutebrowser now displays
|
||||||
|
colored logs without it.
|
||||||
|
|
||||||
Fixed
|
Fixed
|
||||||
-----
|
-----
|
||||||
|
@ -212,7 +212,6 @@ Documentation of used Python libraries:
|
|||||||
* http://pythonhosted.org/setuptools/[setuptools]
|
* http://pythonhosted.org/setuptools/[setuptools]
|
||||||
* http://cx-freeze.readthedocs.org/en/latest/overview.html[cx_Freeze]
|
* http://cx-freeze.readthedocs.org/en/latest/overview.html[cx_Freeze]
|
||||||
* https://pypi.python.org/pypi/colorama[colorama]
|
* https://pypi.python.org/pypi/colorama[colorama]
|
||||||
* https://pypi.python.org/pypi/colorlog[colorlog]
|
|
||||||
|
|
||||||
Related RFCs and standards:
|
Related RFCs and standards:
|
||||||
|
|
||||||
|
@ -109,11 +109,8 @@ The following libraries are optional and provide a better user experience:
|
|||||||
To generate the documentation for the `:help` command, when using the git
|
To generate the documentation for the `:help` command, when using the git
|
||||||
repository (rather than a release), http://asciidoc.org/[asciidoc] is needed.
|
repository (rather than a release), http://asciidoc.org/[asciidoc] is needed.
|
||||||
|
|
||||||
The following libraries are optional and provide colored logging in the
|
On Windows, https://pypi.python.org/pypi/colorama/[colorama] is needed to
|
||||||
console:
|
display colored log output.
|
||||||
|
|
||||||
* https://pypi.python.org/pypi/colorlog/[colorlog]
|
|
||||||
* On Windows: https://pypi.python.org/pypi/colorama/[colorama]
|
|
||||||
|
|
||||||
See link:INSTALL.asciidoc[INSTALL] for directions on how to install qutebrowser
|
See link:INSTALL.asciidoc[INSTALL] for directions on how to install qutebrowser
|
||||||
and its dependencies.
|
and its dependencies.
|
||||||
|
@ -35,33 +35,16 @@ try:
|
|||||||
import colorama
|
import colorama
|
||||||
except ImportError:
|
except ImportError:
|
||||||
colorama = None
|
colorama = None
|
||||||
try:
|
|
||||||
import colorlog
|
COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'purple', 'cyan', 'white']
|
||||||
except ImportError:
|
|
||||||
colorlog = None
|
|
||||||
else:
|
|
||||||
# WORKAROUND
|
|
||||||
# colorlog calls colorama.init() which we don't want, also it breaks our
|
|
||||||
# sys.stdout/sys.stderr if they are None. Bugreports:
|
|
||||||
# https://code.google.com/p/colorama/issues/detail?id=61
|
|
||||||
# https://github.com/borntyping/python-colorlog/issues/13
|
|
||||||
# This should be (partially) fixed in colorama 0.3.2.
|
|
||||||
# (stream only gets wrapped if it's not None)
|
|
||||||
if colorama is not None:
|
|
||||||
colorama.deinit()
|
|
||||||
|
|
||||||
# Log formats to use.
|
# Log formats to use.
|
||||||
SIMPLE_FMT = '{asctime:8} {levelname}: {message}'
|
SIMPLE_FMT = ('{green}{asctime:8}{reset} {log_color}{levelname}{reset}: '
|
||||||
EXTENDED_FMT = ('{asctime:8} {levelname:8} {name:10} {module}:{funcName}:'
|
'{message}')
|
||||||
'{lineno} {message}')
|
EXTENDED_FMT = ('{green}{asctime:8}{reset} '
|
||||||
SIMPLE_FMT_COLORED = ('%(green)s%(asctime)-8s%(reset)s '
|
'{log_color}{levelname:8}{reset} '
|
||||||
'%(log_color)s%(levelname)s%(reset)s: %(message)s')
|
'{cyan}{name:10} {module}:{funcName}:{lineno}{reset} '
|
||||||
EXTENDED_FMT_COLORED = (
|
'{log_color}{message}{reset}')
|
||||||
'%(green)s%(asctime)-8s%(reset)s '
|
|
||||||
'%(log_color)s%(levelname)-8s%(reset)s '
|
|
||||||
'%(cyan)s%(name)-10s %(module)s:%(funcName)s:%(lineno)s%(reset)s '
|
|
||||||
'%(log_color)s%(message)s%(reset)s'
|
|
||||||
)
|
|
||||||
EXTENDED_FMT_HTML = (
|
EXTENDED_FMT_HTML = (
|
||||||
'<tr>'
|
'<tr>'
|
||||||
'<td><pre>%(green)s%(asctime)-8s%(reset)s</pre></td>'
|
'<td><pre>%(green)s%(asctime)-8s%(reset)s</pre></td>'
|
||||||
@ -223,12 +206,7 @@ def _init_formatters(level, color, force_color):
|
|||||||
console_formatter/ram_formatter: logging.Formatter instances.
|
console_formatter/ram_formatter: logging.Formatter instances.
|
||||||
use_colorama: Whether to use colorama.
|
use_colorama: Whether to use colorama.
|
||||||
"""
|
"""
|
||||||
if level <= logging.DEBUG:
|
console_fmt = EXTENDED_FMT if level <= logging.DEBUG else SIMPLE_FMT
|
||||||
console_fmt = EXTENDED_FMT
|
|
||||||
console_fmt_colored = EXTENDED_FMT_COLORED
|
|
||||||
else:
|
|
||||||
console_fmt = SIMPLE_FMT
|
|
||||||
console_fmt_colored = SIMPLE_FMT_COLORED
|
|
||||||
ram_formatter = logging.Formatter(EXTENDED_FMT, DATEFMT, '{')
|
ram_formatter = logging.Formatter(EXTENDED_FMT, DATEFMT, '{')
|
||||||
html_formatter = HTMLFormatter(EXTENDED_FMT_HTML, DATEFMT,
|
html_formatter = HTMLFormatter(EXTENDED_FMT_HTML, DATEFMT,
|
||||||
log_colors=LOG_COLORS)
|
log_colors=LOG_COLORS)
|
||||||
@ -236,14 +214,17 @@ def _init_formatters(level, color, force_color):
|
|||||||
return None, ram_formatter, html_formatter, False
|
return None, ram_formatter, html_formatter, False
|
||||||
|
|
||||||
use_colorama = False
|
use_colorama = False
|
||||||
color_supported = colorlog is not None and (os.name == 'posix' or colorama)
|
color_supported = os.name == 'posix' or colorama
|
||||||
|
|
||||||
if color_supported and (sys.stderr.isatty() or force_color) and color:
|
if color_supported and (sys.stderr.isatty() or force_color) and color:
|
||||||
console_formatter = colorlog.ColoredFormatter(
|
use_colors = True
|
||||||
console_fmt_colored, DATEFMT, log_colors=LOG_COLORS)
|
|
||||||
if colorama and os.name != 'posix':
|
if colorama and os.name != 'posix':
|
||||||
use_colorama = True
|
use_colorama = True
|
||||||
else:
|
else:
|
||||||
console_formatter = logging.Formatter(console_fmt, DATEFMT, '{')
|
use_colors = False
|
||||||
|
|
||||||
|
console_formatter = ColoredFormatter(console_fmt, DATEFMT, '{',
|
||||||
|
use_colors=use_colors)
|
||||||
return console_formatter, ram_formatter, html_formatter, use_colorama
|
return console_formatter, ram_formatter, html_formatter, use_colorama
|
||||||
|
|
||||||
|
|
||||||
@ -467,9 +448,43 @@ class RAMHandler(logging.Handler):
|
|||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
class ColoredFormatter(logging.Formatter):
|
||||||
|
|
||||||
|
"""Logging formatter to output colored logs.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
use_colors: Whether to do colored logging or not.
|
||||||
|
|
||||||
|
Class attributes:
|
||||||
|
COLOR_ESCAPES: A dict mapping color names to escape sequences
|
||||||
|
RESET_ESCAPE: The escape sequence using for resetting colors
|
||||||
|
"""
|
||||||
|
|
||||||
|
COLOR_ESCAPES = {color: '\033[{}m'.format(i)
|
||||||
|
for i, color in enumerate(COLORS, start=30)}
|
||||||
|
RESET_ESCAPE = '\033[0m'
|
||||||
|
|
||||||
|
def __init__(self, fmt, datefmt, style, *, use_colors):
|
||||||
|
super().__init__(fmt, datefmt, style)
|
||||||
|
self._use_colors = use_colors
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
if self._use_colors:
|
||||||
|
color_dict = dict(self.COLOR_ESCAPES)
|
||||||
|
color_dict['reset'] = self.RESET_ESCAPE
|
||||||
|
log_color = LOG_COLORS[record.levelname]
|
||||||
|
color_dict['log_color'] = self.COLOR_ESCAPES[log_color]
|
||||||
|
else:
|
||||||
|
color_dict = {color: '' for color in self.COLOR_ESCAPES}
|
||||||
|
color_dict['reset'] = ''
|
||||||
|
color_dict['log_color'] = ''
|
||||||
|
record.__dict__.update(color_dict)
|
||||||
|
return super().format(record)
|
||||||
|
|
||||||
|
|
||||||
class HTMLFormatter(logging.Formatter):
|
class HTMLFormatter(logging.Formatter):
|
||||||
|
|
||||||
"""Formatter for HTML-colored log messages, similar to colorlog.
|
"""Formatter for HTML-colored log messages.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_log_colors: The colors to use for logging levels.
|
_log_colors: The colors to use for logging levels.
|
||||||
@ -489,8 +504,7 @@ class HTMLFormatter(logging.Formatter):
|
|||||||
self._colordict = {}
|
self._colordict = {}
|
||||||
# We could solve this nicer by using CSS, but for this simple case this
|
# We could solve this nicer by using CSS, but for this simple case this
|
||||||
# works.
|
# works.
|
||||||
for color in ['black', 'red', 'green', 'yellow', 'blue', 'purple',
|
for color in COLORS:
|
||||||
'cyan', 'white']:
|
|
||||||
self._colordict[color] = '<font color="{}">'.format(color)
|
self._colordict[color] = '<font color="{}">'.format(color)
|
||||||
self._colordict['reset'] = '</font>'
|
self._colordict['reset'] = '</font>'
|
||||||
|
|
||||||
|
@ -129,7 +129,6 @@ def _module_versions():
|
|||||||
lines = []
|
lines = []
|
||||||
modules = collections.OrderedDict([
|
modules = collections.OrderedDict([
|
||||||
('sip', ['SIP_VERSION_STR']),
|
('sip', ['SIP_VERSION_STR']),
|
||||||
('colorlog', []),
|
|
||||||
('colorama', ['VERSION', '__version__']),
|
('colorama', ['VERSION', '__version__']),
|
||||||
('pypeg2', ['__version__']),
|
('pypeg2', ['__version__']),
|
||||||
('jinja2', ['__version__']),
|
('jinja2', ['__version__']),
|
||||||
|
@ -4,5 +4,4 @@ Pygments==2.1.3
|
|||||||
pyPEG2==2.15.2
|
pyPEG2==2.15.2
|
||||||
PyYAML==3.11
|
PyYAML==3.11
|
||||||
colorama==0.3.7
|
colorama==0.3.7
|
||||||
colorlog==2.6.3
|
|
||||||
cssutils==1.0.1
|
cssutils==1.0.1
|
||||||
|
@ -319,7 +319,6 @@ class ImportFake:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.exists = {
|
self.exists = {
|
||||||
'sip': True,
|
'sip': True,
|
||||||
'colorlog': True,
|
|
||||||
'colorama': True,
|
'colorama': True,
|
||||||
'pypeg2': True,
|
'pypeg2': True,
|
||||||
'jinja2': True,
|
'jinja2': True,
|
||||||
@ -383,15 +382,14 @@ class TestModuleVersions:
|
|||||||
@pytest.mark.usefixtures('import_fake')
|
@pytest.mark.usefixtures('import_fake')
|
||||||
def test_all_present(self):
|
def test_all_present(self):
|
||||||
"""Test with all modules present in version 1.2.3."""
|
"""Test with all modules present in version 1.2.3."""
|
||||||
expected = ['sip: yes', 'colorlog: yes', 'colorama: 1.2.3',
|
expected = ['sip: yes', 'colorama: 1.2.3', 'pypeg2: 1.2.3',
|
||||||
'pypeg2: 1.2.3', 'jinja2: 1.2.3', 'pygments: 1.2.3',
|
'jinja2: 1.2.3', 'pygments: 1.2.3', 'yaml: 1.2.3',
|
||||||
'yaml: 1.2.3', 'cssutils: 1.2.3']
|
'cssutils: 1.2.3']
|
||||||
assert version._module_versions() == expected
|
assert version._module_versions() == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('module, idx, expected', [
|
@pytest.mark.parametrize('module, idx, expected', [
|
||||||
('colorlog', 1, 'colorlog: no'),
|
('colorama', 1, 'colorama: no'),
|
||||||
('colorama', 2, 'colorama: no'),
|
('cssutils', 6, 'cssutils: no'),
|
||||||
('cssutils', 7, 'cssutils: no'),
|
|
||||||
])
|
])
|
||||||
def test_missing_module(self, module, idx, expected, import_fake):
|
def test_missing_module(self, module, idx, expected, import_fake):
|
||||||
"""Test with a module missing.
|
"""Test with a module missing.
|
||||||
@ -405,15 +403,14 @@ class TestModuleVersions:
|
|||||||
assert version._module_versions()[idx] == expected
|
assert version._module_versions()[idx] == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('value, expected', [
|
@pytest.mark.parametrize('value, expected', [
|
||||||
('VERSION', ['sip: yes', 'colorlog: yes', 'colorama: 1.2.3',
|
('VERSION', ['sip: yes', 'colorama: 1.2.3', 'pypeg2: yes',
|
||||||
'pypeg2: yes', 'jinja2: yes', 'pygments: yes',
|
|
||||||
'yaml: yes', 'cssutils: yes']),
|
|
||||||
('SIP_VERSION_STR', ['sip: 1.2.3', 'colorlog: yes', 'colorama: yes',
|
|
||||||
'pypeg2: yes', 'jinja2: yes', 'pygments: yes',
|
|
||||||
'yaml: yes', 'cssutils: yes']),
|
|
||||||
(None, ['sip: yes', 'colorlog: yes', 'colorama: yes', 'pypeg2: yes',
|
|
||||||
'jinja2: yes', 'pygments: yes', 'yaml: yes',
|
'jinja2: yes', 'pygments: yes', 'yaml: yes',
|
||||||
'cssutils: yes']),
|
'cssutils: yes']),
|
||||||
|
('SIP_VERSION_STR', ['sip: 1.2.3', 'colorama: yes', 'pypeg2: yes',
|
||||||
|
'jinja2: yes', 'pygments: yes', 'yaml: yes',
|
||||||
|
'cssutils: yes']),
|
||||||
|
(None, ['sip: yes', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes',
|
||||||
|
'pygments: yes', 'yaml: yes', 'cssutils: yes']),
|
||||||
])
|
])
|
||||||
def test_version_attribute(self, value, expected, import_fake):
|
def test_version_attribute(self, value, expected, import_fake):
|
||||||
"""Test with a different version attribute.
|
"""Test with a different version attribute.
|
||||||
@ -429,7 +426,6 @@ class TestModuleVersions:
|
|||||||
assert version._module_versions() == expected
|
assert version._module_versions() == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('name, has_version', [
|
@pytest.mark.parametrize('name, has_version', [
|
||||||
('colorlog', False),
|
|
||||||
('sip', False),
|
('sip', False),
|
||||||
('colorama', True),
|
('colorama', True),
|
||||||
('pypeg2', True),
|
('pypeg2', True),
|
||||||
@ -442,7 +438,7 @@ class TestModuleVersions:
|
|||||||
"""Check if all dependencies have an expected __version__ attribute.
|
"""Check if all dependencies have an expected __version__ attribute.
|
||||||
|
|
||||||
The aim of this test is to fail if modules suddenly don't have a
|
The aim of this test is to fail if modules suddenly don't have a
|
||||||
__version__ attribute anymore in a newer version, or colorlog has one.
|
__version__ attribute anymore in a newer version.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: The name of the module to check.
|
name: The name of the module to check.
|
||||||
|
Loading…
Reference in New Issue
Block a user