question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pytest 3.3: cannot mock root logger

See original GitHub issue

Pytest 3.3.0 uses the root logger in each test so it can no longer be mocked with monkeypatch

import logging
import pytest
from unittest.mock import MagicMock
@pytest.fixture
def setup(monkeypatch):
    mock = MagicMock()
    monkeypatch.setattr('logging.getLogger', lambda: mock) 
    return mock
def test_x(setup, monkeypatch):
    assert id(setup) == id(logging.getLogger())

raires

TypeError: <lambda>() takes 0 positional arguments but 1 was given
<python-path>\lib\contextlib.py:81: TypeError

changing the lambda to lambda _: mock raises

TypeError: '<' not supported between instances of 'int' and 'MagicMock'
<python-path>\lib\contextlib.py:81: TypeError

Moving the monkeypatch call in the function does not help since the second error is always raised. The error is raised in <python-path>\lib\site-packages\_pytest\logging.py on line 102 or 110

Using the --no-print-logs flag does not change the output

Pytest 3.2.5 does not have this problem

Also if there are subsequent tests after the one that caused the previous error, pytest crashes completely: appending

def test_y():
    assert True

after test_x in the example above results in the following error when running pytest without filters

x_test.py FE                                                             [100%]
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\main.py", line 103, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\main.py", line 141, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 617, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 222, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 216, in <lambda>
INTERNALERROR>     firstresult=hook.spec_opts.get('firstresult'),
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 201, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 76, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 180, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\main.py", line 164, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 617, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 222, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 216, in <lambda>
INTERNALERROR>     firstresult=hook.spec_opts.get('firstresult'),
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 201, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 76, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 180, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\runner.py", line 63, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\runner.py", line 71, in runtestprotocol
INTERNALERROR>     rep = call_and_report(item, "setup", log)
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\runner.py", line 159, in call_and_report
INTERNALERROR>     report = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 617, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 222, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\__init__.py", line 216, in <lambda>
INTERNALERROR>     firstresult=hook.spec_opts.get('firstresult'),
INTERNALERROR>   File "<python-path>\lib\site-packages\pluggy\callers.py", line 196, in _multicall
INTERNALERROR>     gen.send(outcome)
INTERNALERROR>   File "<python-path>\lib\site-packages\_pytest\skipping.py", line 264, in pytest_runtest_makereport
INTERNALERROR>     elif item._skipped_by_mark and rep.skipped and type(rep.longrepr) is tuple:
INTERNALERROR> AttributeError: 'Function' object has no attribute '_skipped_by_mark'

Selecting only test_x does not raises the AttributeError I’m on windows 10 with python 3.6.3, but I’ve confirmed the same bug on linux under python 3.5 and 3.6

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
mikeneronecommented, Apr 5, 2018

BTW, you can still patch getLogger() in a fixture - just patch it with a function that only applies the patch for non-root loggers. As an extra precaution, I would also exclude loggers with “pytest” in the name. E.g.

@pytest.fixture(autouse=True)
def _always_patch_loggers(mocker):
    real_get_logger = logging.getLogger

    def get_patched_logger(name=None):
        logger = real_get_logger(name)
        if not isinstance(logger._log, mocker.Mock) and name and 'pytest' not in name:
            mocker.patch.object(logger, '_log')
        return logger

    mocker.patch.object(logging, 'getLogger', side_effect=get_patched_logger)

(Oh, and I’m using pytest-mock here - adapt if you want to use monkeypatch.)

1reaction
CaselITcommented, Mar 1, 2018

Yes, of course, it should take into consideration the flag -p no:logging. Again I’m not familiar at all with the internals of pytest, so it may be totally unfeasible doing what I suggested

Read more comments on GitHub >

github_iconTop Results From Across the Web

python - Get all logging output with mock - Stack Overflow
Show activity on this post. I found this solution: def test_foo(self): logs=[] def my_log(self, *args, **kwargs): logs. append((args, kwargs)) with mock.
Read more >
How to Mock Logging in Python | Simeon Visser
If you're using Python 3.3 (or higher) than you can import from the unittest. mock (documentation) module rather than installing mock from PyPI....
Read more >
How to manage logging — pytest documentation
pytest captures log messages of level WARNING or above automatically and displays them in their own section for each failed test in the...
Read more >
pytest-mock 1.6.2 - PyPI
This plugin installs a mocker fixture which is a thin-wrapper around the patching API provided by the mock package, but with the benefit...
Read more >
Mocking, Monkey Patching, and Faking Functionality
It was so useful that it was built into Python 3.3+'s unittest library. We're currently using pytest , so we don't have to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found