Pytest: Error writing to terminal (from pytest 6.1.0 version)

Created on 11 Oct 2020  路  7Comments  路  Source: pytest-dev/pytest

It works fine with pytest 6.0.2 or with the older versions, it doesn't work with both 6.1.0 and 6.1.1.

It just crashes preventing the final report with failing tests details.

$ python
Python 3.8.6 (default, Sep 25 2020, 09:36:53) 
[GCC 10.2.0] on linux
$ uname -a
Linux debian 5.8.0-2-amd64 #1 SMP Debian 5.8.10-1 (2020-09-19) x86_64 GNU/Linux
$ pip list
Package                       Version    Location
----------------------------- ---------- --------------------------------------------------------
channels                      2.4.0
channels-redis                3.1.0
Django                        3.1.2
djangorestframework           3.12.1
djangorestframework-simplejwt 4.4.0
pytest                        6.1.0
pytest-asyncio                0.14.0
pytest-cov                    2.10.1
pytest-django                 3.10.0
pytest-mock                   3.3.1
pytest-ordering               0.6
selenium                      3.141.0



md5-3b1769e423b7ed4ea1200f21b7204d95



utils/tests/test_helpers.py::TestUserMediaImagesHelpersFunctions::test_utils_helpers_reduced_image_size_returns_max_height PASSED [ 95%]
utils/tests/test_helpers.py::TestUserMediaImagesHelpersFunctions::test_utils_helpers_save_user_image_calls_reduced_image_size_thumbnail  

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 257, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 313, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR>     self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/main.py", line 338, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR>     self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 110, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 127, in runtestprotocol
INTERNALERROR>     reports.append(call_and_report(item, "call", log))
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/runner.py", line 220, in call_and_report
INTERNALERROR>     hook.pytest_runtest_logreport(report=report)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR>     self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/terminal.py", line 546, in pytest_runtest_logreport
INTERNALERROR>     self.write_ensure_prefix(line, word, **markup)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/terminal.py", line 408, in write_ensure_prefix
INTERNALERROR>     self._tw.write(prefix)
INTERNALERROR>   File "/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/_io/terminalwriter.py", line 155, in write
INTERNALERROR>     self._file.write(msg)
INTERNALERROR> TypeError: write() argument must be str, not MagicMock
Destroying test database for alias 'default'...

If I comment out that last test (test_utils_helpers_save_user_image_calls_reduced_image_size_thumbnail) then it fails on the very previous one (test_utils_helpers_reduced_image_size_returns_max_height).

Most helpful comment

Oops, didn't know. So in this case I'd use unittest.mock.patch directly. Or better, try to avoid patching functions like os.path.join as it is very widely used. To make it a little safer you can create a wrapper function over os.path.join in your module and then patch that.

All 7 comments

There is some sort of active mock that interferes with pytest internals. I suggest temporarily adding a print() or breakpoint() call before

"/home/ipaleka/dev/venvs/project/lib/python3.8/site-packages/_pytest/_io/terminalwriter.py", line 155

to find out which mock it is, then we will have more information.

Well, thx, it looks like I've managed to isolate the error after adding the following block of code where you suggested:

except TypeError:
    breakpoint()

The problem is with mocking module's os.path.join (this syntax is functionality from pytest-mock package):

def test_utils_some_function_returns_empty_list_for_not_existing_path(self, mocker):
    mocker.patch("utils.helpers.os.path.join")
    mocker.patch("utils.helpers.os.path.exists", return_value=False)
    assert some_function(mocker.MagicMock()) == []

Wherever I use that os.path.join patch it will crash the test runner. The main problem is that this worked prior to 6.1.0 version.

Some internal details in pytest changed, but it is not quite possible for pytest to protect itself against all sorts of patching of functions it uses. In this particular case I recommend changing the test to clean up the mock on its own instead of letting pytest do it:

with mocker.patch("utils.helpers.os.path.join"), mocker.patch("utils.helpers.os.path.exists", return_value=False):
    assert some_function(mocker.MagicMock()) == []

I'm afraid that's not possible in pytest-mock, it raises "ValueError: Using mocker in a with context is not supported.":

https://github.com/pytest-dev/pytest-mock#note-about-usage-as-context-manager

Although mocker's API is intentionally the same as mock.patch's, its use as context manager and function decorator is not supported through the fixture:

def test_context_manager(mocker):
    a = A()
    with mocker.patch.object(a, 'doIt', return_value=True, autospec=True):  # DO NOT DO THIS
        assert a.doIt() == True

The purpose of this plugin is to make the use of context managers and function decorators for mocking unnecessary.

Oops, didn't know. So in this case I'd use unittest.mock.patch directly. Or better, try to avoid patching functions like os.path.join as it is very widely used. To make it a little safer you can create a wrapper function over os.path.join in your module and then patch that.

Yep, it works with unittest.mock.patch.

Thank you very much for your time and effort!

I'm glad we worked out what was happening :slightly_smiling_face:

Was this page helpful?
0 / 5 - 0 ratings