PytestWarning: Module already imported so cannot be rewritten: pytest_remotedata

Viewed 6002

I created some unit tests and run them from the same file. For tests in the same file:

if __name__ == "__main__":
    import pytest
    pytest.main(['--tb=short', __file__])

For tests in another file:

if __name__ == '__main__':
    import pytest
    pytest.main(['./test_stuff.py', "--capture=sys"])

In either case, when I execute the file the first time, it works fine, but the second and subsequent times it gives a bunch of warnings:

============================== warnings summary ===============================
C:\Anaconda3\lib\site-packages\_pytest\config\__init__.py:754
  C:\Anaconda3\lib\site-packages\_pytest\config\__init__.py:754: PytestWarning: Module already imported so cannot be rewritten: pytest_remotedata
    self._mark_plugins_for_rewrite(hook)
  C:\Anaconda3\lib\site-packages\_pytest\config\__init__.py:754: PytestWarning: Module already imported so cannot be rewritten: pytest_openfiles
    self._mark_plugins_for_rewrite(hook)
  C:\Anaconda3\lib\site-packages\_pytest\config\__init__.py:754: PytestWarning: Module already imported so cannot be rewritten: pytest_doctestplus
    self._mark_plugins_for_rewrite(hook)
  C:\Anaconda3\lib\site-packages\_pytest\config\__init__.py:754: PytestWarning: Module already imported so cannot be rewritten: pytest_arraydiff
    self._mark_plugins_for_rewrite(hook)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
==================== 1 passed, 4 warnings in 0.06 seconds 

Is there any way to make these warnings go away?

Restarting the kernel works, but IPython's %reset and %clear aren't enough to fix it, either.

3 Answers

It would appear that pytest just isn't really designed to perform pytest.main() calls repeatedly from within the same process. The pytest documentation mentions:

Calling pytest.main() will result in importing your tests and any modules that they import. Due to the caching mechanism of python’s import system, making subsequent calls to pytest.main() from the same process will not reflect changes to those files between the calls. For this reason, making multiple calls to pytest.main() from the same process (in order to re-run tests, for example) is not recommended.

So actually the real danger of performing multiple pytest.main() calls within the same process is that you could get false-positive or false-negative test results if you've edited your code files inbetween.

The Spyder IDE has a nifty feature though that seems to negate this issue: the User Module Reloader (UMR). If enabled, then any changed user modules are automatically reloaded when a script file is (re-)run.

Therefore I think that as long as you're working in Spyder (with the UMR feature enabled!), you can safely rerun pytest.main() without the need for a new console. The pytest module already-imported warnings you can then simply suppress, since those pytest modules will not have changed. This can be done with pytest's -W flag. Example:

if __name__ == '__main__':
    import pytest
    pytest.main(['./test_stuff.py', "--capture=sys", "-W", "ignore:Module already imported:pytest.PytestWarning"])

If you are only concerned about warnings, you can use --disable-warnings argument. You can also filter out only the warnings you are getting now with --pythonwarnings=PYTHONWARNINGS argument in pytest.main . pytest --help has more info about these arguments.

It seems that you are invoking pytest with ipython or jupyter. python.main imports pytest specific modules at the time of pytest initialization. When you run pytest.main again, it throws the warnings. There are two possible ways to reload pytest to avoid getting this initialization warnings.

You can either use pytest.exit after pytest.main or reload pytest after pytest.main .

Let us know if these approaches work.

Use subprocess instead of pytest.main:

if __name__ == "__main__":
    import subprocess
    subprocess.call(['pytest', '--tb=short', str(__file__)])

If the above does not print anything, try the workaround (as suggested in comments):

if __name__ == "__main__":
    from subprocess import Popen, PIPE
    with Popen(['pytest',
                '--tb=short',  # shorter traceback format
                str(__file__)], stdout=PIPE, bufsize=1,
                universal_newlines=True) as p:
        for line in p.stdout:
            print(line, end='')
Related