Skip to content

Commit

Permalink
caplog un-disable logging, feedback per review pytest-dev#8711
Browse files Browse the repository at this point in the history
- Resolves issues from review provided by https://github.com/andrewdotn

- Fixed spelling errors

- Improved logic in `force_enable_logging`

- Make a fixture to un-disable logging after a test so that logging will be un-disabled even if the test fails due to an assertion error.
The fixture is in `test_fixture.py` because we can't `import logging` in `conftest.py` because there is a module called `logging` in the same folder.

- Mypy implicit import error. I can't commit without this.

Issue: pytest-dev#8711
PR: pytest-dev#8758
  • Loading branch information
alexlambson committed May 18, 2023
1 parent e480dfe commit bc5a27f
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import importlib_metadata
from _pytest.compat import importlib_metadata # type: ignore[attr-defined]
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.pathlib import absolutepath
Expand Down
15 changes: 8 additions & 7 deletions src/_pytest/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ def force_enable_logging(
) -> int:
"""Enable the desired logging level if the level was disabled.
Only enables logging levels equal too and higher than the requested ``level``.
Only enables logging levels greater than or equal to the requested ``level``.
Does nothing if the desired ``level`` wasn't disabled.
Expand All @@ -478,16 +478,17 @@ def force_enable_logging(
original_disable_level: int = logger_obj.manager.disable # type: ignore[attr-defined]

if isinstance(level, str):
# Try to translate the level string to an int for `logging.disable()`
level = logging.getLevelName(level)

if not isinstance(level, int):
# The level provided was not valid, so just un-disable all logging.
logging.disable(logging.NOTSET)
return original_disable_level
if logger_obj.isEnabledFor(level):
return original_disable_level

disable_level = max(level - 10, logging.NOTSET)
logging.disable(disable_level)
elif not logger_obj.isEnabledFor(level):
# Each level is `10` away from other levels.
# https://docs.python.org/3/library/logging.html#logging-levels
disable_level = max(level - 10, logging.NOTSET)
logging.disable(disable_level)

return original_disable_level

Expand Down
30 changes: 18 additions & 12 deletions testing/logging/test_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@
sublogger = logging.getLogger(__name__ + ".baz")


@pytest.fixture
def cleanup_disabled_logging():
"""Simple fixture that ensures that a test doesn't disable logging.
This is necessary because ``logging.disable()`` is global, so a test disabling logging
and not cleaning up after will break every test that runs after it.
This behavior was moved to a fixture so that logging will be un-disabled even if the test fails an assertion.
"""
yield
logging.disable(logging.NOTSET)


def test_fixture_help(pytester: Pytester) -> None:
result = pytester.runpytest("--fixtures")
result.stdout.fnmatch_lines(["*caplog*"])
Expand All @@ -29,7 +42,7 @@ def test_change_level(caplog):
assert "CRITICAL" in caplog.text


def test_change_level_logging_disabled(caplog):
def test_change_level_logging_disabled(caplog, cleanup_disabled_logging):
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
caplog.set_level(logging.WARNING)
Expand All @@ -45,9 +58,6 @@ def test_change_level_logging_disabled(caplog):
assert "SUB_WARNING" not in caplog.text
assert "SUB_CRITICAL" in caplog.text

# logging.disable needs to be reset because it's global and causes future tests will break.
logging.disable(logging.NOTSET)


def test_change_level_undo(pytester: Pytester) -> None:
"""Ensure that 'set_level' is undone after the end of the test.
Expand Down Expand Up @@ -75,7 +85,9 @@ def test2(caplog):
result.stdout.no_fnmatch_line("*log from test2*")


def test_change_disabled_level_undo(pytester: Pytester) -> None:
def test_change_disabled_level_undo(
pytester: Pytester, cleanup_disabled_logging
) -> None:
"""Ensure that 'force_enable_logging' in 'set_level' is undone after the end of the test.
Tests the logging output themselves (affected by disabled logging level).
Expand Down Expand Up @@ -103,9 +115,6 @@ def test2(caplog):
result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"])
result.stdout.no_fnmatch_line("*log from test2*")

# logging.disable needs to be reset because it's global and causes future tests will break.
logging.disable(logging.NOTSET)


def test_change_level_undos_handler_level(pytester: Pytester) -> None:
"""Ensure that 'set_level' is undone after the end of the test (handler).
Expand Down Expand Up @@ -150,7 +159,7 @@ def test_with_statement(caplog):
assert "CRITICAL" in caplog.text


def test_with_statement_logging_disabled(caplog):
def test_with_statement_logging_disabled(caplog, cleanup_disabled_logging):
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
with caplog.at_level(logging.WARNING):
Expand All @@ -175,9 +184,6 @@ def test_with_statement_logging_disabled(caplog):
assert "SUB_CRITICAL" in caplog.text
assert logging.root.manager.disable == logging.CRITICAL

# logging.disable needs to be reset because it's global and causes future tests will break.
logging.disable(logging.NOTSET)


def test_log_access(caplog):
caplog.set_level(logging.INFO)
Expand Down

0 comments on commit bc5a27f

Please sign in to comment.