Skip to content
Open
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
19 changes: 14 additions & 5 deletions cssutils/errorhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,20 @@ def __init__(self, log, defaultloglevel=logging.INFO, raiseExceptions=True):
import sys

self._log = logging.getLogger('CSSUTILS')
hdlr = logging.StreamHandler(sys.stderr)
formatter = logging.Formatter('%(levelname)s\t%(message)s')
hdlr.setFormatter(formatter)
self._log.addHandler(hdlr)
self._log.setLevel(defaultloglevel)
if not self._log.hasHandlers():
# only install a handler if the application hasn't configured logging
hdlr = logging.StreamHandler(sys.stderr)
formatter = logging.Formatter('%(levelname)s\t%(message)s')
hdlr.setFormatter(formatter)
self._log.addHandler(hdlr)
root = logging.getLogger()
if (
self._log.level == logging.NOTSET
and not root.handlers
and root.level == logging.WARNING
):
# only set the default level if the application hasn't chosen one
self._log.setLevel(defaultloglevel)

self.raiseExceptions = raiseExceptions

Expand Down
1 change: 1 addition & 0 deletions newsfragments/13.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Do not override an existing logging configuration when the CSSUTILS logger is created.
70 changes: 70 additions & 0 deletions tests/test_errorhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pytest

import cssutils
from cssutils.errorhandler import _ErrorHandler


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -144,3 +145,72 @@ def test_parsevalidation(self):
s = self._setHandler()
cssutils.parseStyle(style, validate=False)
assert s.getvalue() == ''


class TestErrorHandlerSetup:
"""Tests for the initial configuration of the CSSUTILS logger."""

@pytest.fixture
def logger(self, monkeypatch):
"the logger cssutils configures"
logger = logging.getLogger('CSSUTILS')
monkeypatch.setattr(logger, 'level', logging.NOTSET)
root = logging.getLogger()
monkeypatch.setattr(root, 'level', logging.WARNING)
return logger

def test_installs_default_configuration(self, monkeypatch):
"an unconfigured logger gets the default handler and level"
logger = logging.getLogger('CSSUTILS-TEST')
logger.handlers.clear()
logger.setLevel(logging.NOTSET)
root = logging.getLogger()
monkeypatch.setattr(root, 'handlers', [])
monkeypatch.setattr(root, 'level', logging.WARNING)
real_get_logger = logging.getLogger
monkeypatch.setattr(
logging,
'getLogger',
lambda name=None: logger if name == 'CSSUTILS' else real_get_logger(name),
)

_ErrorHandler(None)

assert len(logger.handlers) == 1
assert logger.handlers[0].stream is sys.stderr
assert logger.level == logging.INFO

def test_keeps_existing_handler(self, logger):
"a handler installed by the application is not replaced"
handler = logging.NullHandler()
logger.addHandler(handler)
before = list(logger.handlers)

_ErrorHandler(None)

assert list(logger.handlers) == before

def test_keeps_configured_level(self, logger):
"a level set for the logger is preserved"
logger.setLevel(logging.ERROR)

_ErrorHandler(None)

assert logger.level == logging.ERROR

def test_keeps_root_level(self, logger):
"a level set for the root logger is preserved"
logging.getLogger().setLevel(logging.ERROR)

_ErrorHandler(None)

assert logger.level == logging.NOTSET

def test_keeps_root_configuration(self, logger, monkeypatch):
"a logging configuration for the root logger is not overridden"
root = logging.getLogger()
monkeypatch.setattr(root, 'handlers', [logging.NullHandler()])

_ErrorHandler(None)

assert logger.level == logging.NOTSET