Allow tests to override "global" `log_level` (rebased) (#7340)
Co-authored-by: Ruaridh Williamson <ruaridh.williamson@flexciton.com>
This commit is contained in:
parent
e78207c936
commit
fcbaab8b0b
1
AUTHORS
1
AUTHORS
|
@ -245,6 +245,7 @@ Romain Dorgueil
|
||||||
Roman Bolshakov
|
Roman Bolshakov
|
||||||
Ronny Pfannschmidt
|
Ronny Pfannschmidt
|
||||||
Ross Lawley
|
Ross Lawley
|
||||||
|
Ruaridh Williamson
|
||||||
Russel Winder
|
Russel Winder
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Samuel Dion-Girardeau
|
Samuel Dion-Girardeau
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
``caplog.set_level()`` will now override any :confval:`log_level` set via the CLI or ``.ini``.
|
|
@ -250,6 +250,9 @@ made in ``3.4`` after community feedback:
|
||||||
|
|
||||||
* Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration
|
* Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration
|
||||||
or ``--log-level`` command-line options. This allows users to configure logger objects themselves.
|
or ``--log-level`` command-line options. This allows users to configure logger objects themselves.
|
||||||
|
Setting :confval:`log_level` will set the level that is captured globally so if a specific test requires
|
||||||
|
a lower level than this, use the ``caplog.set_level()`` functionality otherwise that test will be prone to
|
||||||
|
failure.
|
||||||
* :ref:`Live Logs <live_logs>` is now disabled by default and can be enabled setting the
|
* :ref:`Live Logs <live_logs>` is now disabled by default and can be enabled setting the
|
||||||
:confval:`log_cli` configuration option to ``true``. When enabled, the verbosity is increased so logging for each
|
:confval:`log_cli` configuration option to ``true``. When enabled, the verbosity is increased so logging for each
|
||||||
test is visible.
|
test is visible.
|
||||||
|
|
|
@ -343,7 +343,7 @@ class LogCaptureFixture:
|
||||||
"""Creates a new funcarg."""
|
"""Creates a new funcarg."""
|
||||||
self._item = item
|
self._item = item
|
||||||
# dict of log name -> log level
|
# dict of log name -> log level
|
||||||
self._initial_log_levels = {} # type: Dict[Optional[str], int]
|
self._initial_logger_levels = {} # type: Dict[Optional[str], int]
|
||||||
|
|
||||||
def _finalize(self) -> None:
|
def _finalize(self) -> None:
|
||||||
"""Finalizes the fixture.
|
"""Finalizes the fixture.
|
||||||
|
@ -351,7 +351,7 @@ class LogCaptureFixture:
|
||||||
This restores the log levels changed by :meth:`set_level`.
|
This restores the log levels changed by :meth:`set_level`.
|
||||||
"""
|
"""
|
||||||
# restore log levels
|
# restore log levels
|
||||||
for logger_name, level in self._initial_log_levels.items():
|
for logger_name, level in self._initial_logger_levels.items():
|
||||||
logger = logging.getLogger(logger_name)
|
logger = logging.getLogger(logger_name)
|
||||||
logger.setLevel(level)
|
logger.setLevel(level)
|
||||||
|
|
||||||
|
@ -430,8 +430,9 @@ class LogCaptureFixture:
|
||||||
"""
|
"""
|
||||||
logger_obj = logging.getLogger(logger)
|
logger_obj = logging.getLogger(logger)
|
||||||
# save the original log-level to restore it during teardown
|
# save the original log-level to restore it during teardown
|
||||||
self._initial_log_levels.setdefault(logger, logger_obj.level)
|
self._initial_logger_levels.setdefault(logger, logger_obj.level)
|
||||||
logger_obj.setLevel(level)
|
logger_obj.setLevel(level)
|
||||||
|
self.handler.setLevel(level)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def at_level(
|
def at_level(
|
||||||
|
@ -446,10 +447,13 @@ class LogCaptureFixture:
|
||||||
logger_obj = logging.getLogger(logger)
|
logger_obj = logging.getLogger(logger)
|
||||||
orig_level = logger_obj.level
|
orig_level = logger_obj.level
|
||||||
logger_obj.setLevel(level)
|
logger_obj.setLevel(level)
|
||||||
|
handler_orig_level = self.handler.level
|
||||||
|
self.handler.setLevel(level)
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
logger_obj.setLevel(orig_level)
|
logger_obj.setLevel(orig_level)
|
||||||
|
self.handler.setLevel(handler_orig_level)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -138,3 +138,100 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow
|
||||||
|
|
||||||
# This reaches into private API, don't use this type of thing in real tests!
|
# This reaches into private API, don't use this type of thing in real tests!
|
||||||
assert set(caplog._item._store[catch_log_records_key]) == {"setup", "call"}
|
assert set(caplog._item._store[catch_log_records_key]) == {"setup", "call"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_ini_controls_global_log_level(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
def test_log_level_override(request, caplog):
|
||||||
|
plugin = request.config.pluginmanager.getplugin('logging-plugin')
|
||||||
|
assert plugin.log_level == logging.ERROR
|
||||||
|
logger = logging.getLogger('catchlog')
|
||||||
|
logger.warning("WARNING message won't be shown")
|
||||||
|
logger.error("ERROR message will be shown")
|
||||||
|
assert 'WARNING' not in caplog.text
|
||||||
|
assert 'ERROR' in caplog.text
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
testdir.makeini(
|
||||||
|
"""
|
||||||
|
[pytest]
|
||||||
|
log_level=ERROR
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
# make sure that that we get a '0' exit code for the testsuite
|
||||||
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_caplog_can_override_global_log_level(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
def test_log_level_override(request, caplog):
|
||||||
|
logger = logging.getLogger('catchlog')
|
||||||
|
plugin = request.config.pluginmanager.getplugin('logging-plugin')
|
||||||
|
assert plugin.log_level == logging.WARNING
|
||||||
|
|
||||||
|
logger.info("INFO message won't be shown")
|
||||||
|
|
||||||
|
caplog.set_level(logging.INFO, logger.name)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.DEBUG, logger.name):
|
||||||
|
logger.debug("DEBUG message will be shown")
|
||||||
|
|
||||||
|
logger.debug("DEBUG message won't be shown")
|
||||||
|
|
||||||
|
with caplog.at_level(logging.CRITICAL, logger.name):
|
||||||
|
logger.warning("WARNING message won't be shown")
|
||||||
|
|
||||||
|
logger.debug("DEBUG message won't be shown")
|
||||||
|
logger.info("INFO message will be shown")
|
||||||
|
|
||||||
|
assert "message won't be shown" not in caplog.text
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
testdir.makeini(
|
||||||
|
"""
|
||||||
|
[pytest]
|
||||||
|
log_level=WARNING
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_caplog_captures_despite_exception(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
def test_log_level_override(request, caplog):
|
||||||
|
logger = logging.getLogger('catchlog')
|
||||||
|
plugin = request.config.pluginmanager.getplugin('logging-plugin')
|
||||||
|
assert plugin.log_level == logging.WARNING
|
||||||
|
|
||||||
|
logger.info("INFO message won't be shown")
|
||||||
|
|
||||||
|
caplog.set_level(logging.INFO, logger.name)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.DEBUG, logger.name):
|
||||||
|
logger.debug("DEBUG message will be shown")
|
||||||
|
raise Exception()
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
testdir.makeini(
|
||||||
|
"""
|
||||||
|
[pytest]
|
||||||
|
log_level=WARNING
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["*DEBUG message will be shown*"])
|
||||||
|
assert result.ret == 1
|
||||||
|
|
Loading…
Reference in New Issue