Pytest: ImportPathMismatchError when on UNC path

Created on 23 Aug 2020  ·  29Comments  ·  Source: pytest-dev/pytest

Windows: When running pytest on a directory mounted as UNC path, this fail with ImportPathMismatchError:

c:\vagrant>pytest tests\\functional\\test_path_encodings.py
pytest tests\\functional\\test_path_encodings.py
Adding c:\python38\lib\site-packages\pywin32_system32 to sys.path
ImportError while loading conftest 'c:\vagrant\tests\functional\conftest.py'.
_pytest.pathlib.ImportPathMismatchError: ('conftest', 'c:\\vagrant\\tests\\functional\\conftest.py', WindowsPath('c:/vagrant/tests/functional/conftest.py'))

How to reproduce

  • Fire up a virtual maschine usine Vagrat and sharing the current directory (which contains the package)
  • Intall Python, pytest and requiremnets
  • Try running pytest

You can actually use the setup provided by PyInstaller:

git clone https://github.com/pyinstaller/pyinstaller.git
cd bootloader
# in Vagrantfile, line 434 (provision step "install packages") remove "vcbuildtools"
# … to save avoid installing this huge package
vagrant up --no-provision windows10
vagrant ssh windows10  # password is "Passw0rd!"
pip install -U pip
python -m pip install --upgrade pip setuptools wheel
pip install -U -r tests/requirements-tools.txt
pip install -e .
pytest tests/functional/test_path_encodings.py

C:\Users\IEUser>pip list
Package Version Location


altgraph 0.17
apipkg 1.5
atomicwrites 1.4.0
attrs 20.1.0
colorama 0.4.3
execnet 1.7.1
flake8 3.8.3
future 0.18.2
iniconfig 1.0.1
lxml 4.5.2
mccabe 0.6.1
more-itertools 8.4.0
packaging 20.4
pefile 2019.4.18
pip 20.2.2
pluggy 0.13.1
psutil 5.7.2
py 1.9.0
pycodestyle 2.6.0
pyflakes 2.2.0
pyinstaller 4.1.dev0 \vboxsvr\vagrant\
pyinstaller-hooks-contrib 2020.7
pyparsing 2.4.7
pytest 6.0.1
pytest-drop-dup-tests 0.3.0
pytest-forked 1.3.0
pytest-timeout 1.4.2
pytest-xdist 2.0.0
pywin32 228
pywin32-ctypes 0.2.0
setuptools 47.1.0
six 1.15.0
tinyaes 1.0.1
toml 0.10.1

System: Windows10, Python 3.8.5

windows config bug

All 29 comments

Addendum: This issue also occurs when running tests via cmd.exe.

Looks like I'm also getting this with pytest 6.0.0

I Just tried with pytest 5.4.3 & 5.3.5, and get the same error, so not sure if it is a problem with pytest

Hi @htgoebel,

Sorry for the delay.

You can actually use the setup provided by PyInstaller

Thanks a lot for the detailed and reproducible example, however I'm having trouble with Vagrant on my computer so those steps are not working for me right now (it gives an error trying to rsync the internal paths).

Looking at the error message, the cause of the problem is the os.path.samefile call in this block:

https://github.com/pytest-dev/pytest/blob/634cde9506eb1f48dec3ec77974ee8dc952207c6/src/_pytest/pathlib.py#L523-L537

So can you please try:

  1. Set PY_IGNORE_IMPORTMISMATCH=1. This is a escape hatch of this mismatch mechanism, this should make the problem go away.

  2. Can you try this branch: https://github.com/nicoddemus/pytest/tree/same-path-unc-7678? Installing it directly together with the editable install should do the trick:

    pip install -e . git+https://github.com/nicoddemus/pytest@same-path-unc-7678
    

    If this works, I will introduce that fix into pytest itself.

Thanks!

@echarrod

I Just tried with pytest 5.4.3 & 5.3.5, and get the same error, so not sure if it is a problem with pytest

You mean "it is a problem with pytest", correct?

I was actually thinking that

@echarrod

I Just tried with pytest 5.4.3 & 5.3.5, and get the same error, so not sure if it is a problem with pytest

You mean "it is a problem with pytest", correct?

I was actually thinking that because I rolled back, that it was not pytest, as I remember these versions working previously.

I tried the steps:

Set PY_IGNORE_IMPORTMISMATCH=1
pip install -e . git+https://github.com/nicoddemus/pytest@same-path-unc-7678

I am getting the following error, but I'm unsure if this is a problem with my environment, or pytest:

C:\Users\username\repos\my-repo>pytest
Traceback (most recent call last):
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\username\AppData\Local\Programs\Python\Python38-32\Scripts\pytest.exe\__main__.py", line 7, in <module>
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 185, in console_main
    code = main()
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 141, in main
    config = _prepareconfig(args, plugins)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 316, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 203, in _multicall
    gen.send(outcome)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\helpconfig.py", line 100, in pytest_cmdline_parse
    config = outcome.get_result()  # type: Config
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 998, in pytest_cmdline_parse
    self.parse(args)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 1272, in parse
    self._preparse(args, addopts=addopts)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\_pytest\config\__init__.py", line 1177, in _preparse
    self.hook.pytest_load_initial_conftests(
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 208, in _multicall
    return outcome.get_result()
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pluggy\callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "c:\users\username\appdata\local\programs\python\python38-32\lib\site-packages\pytest_cov\plugin.py", line 124, in pytest_load_initial_conftests
    if early_config.known_args_namespace.cov_source:
AttributeError: 'Namespace' object has no attribute 'cov_source'

I reinstalled everything, and deleted the build folder, and it's now working. So I am using pytest 6.0.1, not the one from the branch, but still the PY_IGNORE_IMPORTMISMATCH=1 is set

@nicoddemus I'll try this, but it will take a few day until I find time for.

@echarrod

I am getting the following error, but I'm unsure if this is a problem with my environment, or pytest:

Argh, unfortunately this is an incompatibility on existing master with pytest-cov. We already have a fix that that should be merged in soon: https://github.com/pytest-dev/pytest/pull/7721.

but still the PY_IGNORE_IMPORTMISMATCH=1 is set

Thanks for confirming. 👍

@htgoebel

@nicoddemus I'll try this, but it will take a few day until I find time for.

No worries, thanks! I see your https://github.com/pyinstaller/pyinstaller/blob/develop/tests/requirements-tools.txt does not include pytest-cov, so you won't run into the issue I mentioned above.

@htgoebel

Just to be clear: setting PY_IGNORE_IMPORTMISMATCH=1 should fix your problem right away, but I would appreciate if you could test my branch without PY_IGNORE_IMPORTMISMATCH being set, so I can incorporate the fix into pytest. 👍

I can test this branch if you want? I am getting the same thing right now.

I can test this branch if you want? I am getting the same thing right now.

That would be great, thanks!

Ok I am testing it now but before I start I would like to mention this is NOT running on Windows but on Linux and I am running into this exact same error, also PY_IGNORE_IMPORTMISMATCH made it go away but I will remove this environment variable while testing your branch. (I noticed the platform Windows tag).

I have just started a new project at work running on minikube + devspace and was handed a project skeleton as a starting point by another developer, while I was improving the skeleton and added pytest support I ran into this.

What could be doing it is the fact that the source of the project is called lets say ~/Projects/foo on my system, but inside the container it is mounted as /usr/src/app (so folder name is not the same "app" rather than "foo")

Here is my folder structure:

~/Projects/foo
~/Projects/foo/backend
~/Projects/foo/backend/setup.py
~/Projects/foo/backend/foo_package

In this setup ~/Projects/foo/backend is mounted in /usr/src/app so the folders "app" and "backend" do not quite match, I didn't think it should do anything because the inner folder foo_package should be all that matters as that is what we're testing here.

Ok I am testing it now but before I start I would like to mention this is NOT running on Windows but on Linux and I am running into this exact same error

Hmm I see, can you post the error you are getting? Because the error in the OP is:

_pytest.pathlib.ImportPathMismatchError: ('conftest', 'c:\\vagrant\\tests\\functional\\conftest.py', WindowsPath('c:/vagrant/tests/functional/conftest.py'))

You will notice the paths are the same, but an internal os.samefile call for some reason fails, so seems like a Windows specific error unless the two files are also equal in your failure message.

I ran into a new error by the way:

Traceback (most recent call last):
  File "/usr/local/bin/pytest", line 8, in <module>
    sys.exit(console_main())
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 185, in console_main
    code = main()
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 141, in main
    config = _prepareconfig(args, plugins)
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 316, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
  File "/usr/local/lib/python3.9/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/usr/local/lib/python3.9/site-packages/_pytest/helpconfig.py", line 100, in pytest_cmdline_parse
    config = outcome.get_result()  # type: Config
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 998, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 1272, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 1177, in _preparse
    self.hook.pytest_load_initial_conftests(
  File "/usr/local/lib/python3.9/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 208, in _multicall
    return outcome.get_result()
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.9/site-packages/pytest_cov/plugin.py", line 125, in pytest_load_initial_conftests
    if early_config.known_args_namespace.cov_source:
AttributeError: 'Namespace' object has no attribute 'cov_source'
Makefile:3: recipe for target 'test' failed
make: *** [test] Error 1

I'll disable the coverage plugin and try again.

Here is the original error when not using your branch:

ImportError while loading conftest '/usr/src/app/web_platform/conftest.py'.
_pytest.pathlib.ImportPathMismatchError: ('web_platform.conftest', '/usr/local/lib/python3.9/site-packages/web_platform/conftest.py', PosixPath('/usr/src/app/web_platform/conftest.py'))

In that case the environment variable mentioned earlier does fix it.

Ok I've gone back again to your branch and uninstalled pytest-cov (invoking without coverage arguments wasn't enough).

And I am still getting this I'm afraid:

ImportError while loading conftest '/usr/src/app/web_platform/conftest.py'.
_pytest.pathlib.ImportPathMismatchError: ('web_platform.conftest', '/usr/local/lib/python3.9/site-packages/web_platform/conftest.py', PosixPath('/usr/src/app/web_platform/conftest.py'))

Error looks the same as when not using your branch.

Thanks @robvdl,

The error is the same but the causes are different... the OP is giving a mismatch error for files that clearly are the same, but os.samefile returns False if one of the paths is an UNC path.

In your case we really have two different paths: /usr/local/lib/python3.9/site-packages/web_platform/conftest.py and /usr/src/app/web_platform/conftest.py.

What is happening in your case I guess is: when you build your container, the .pyc files are generated from the /usr/local/lib/python3.9/site-packages prefix (Python writes the hard-coded path of the .py file inside the .pyc file it generates).

Then you run your container and mount that into /usr/src/app and pytest tries to read it.

pytest when reading .pyc files, will check if the .pyc file it is loading matches the .py file that triggered the import, and notices they are not the same, and raises an error.

In your case using PY_IGNORE_IMPORTMISMATCH as a workaround is fine (it just ignores the error), another approach would be to delete all .pyc files and compile them all again inside the container, under the new mount.

Thank you. I did notice in our Dockerfile we have an environment variable set ENV PYTHONDONTWRITEBYTECODE 1, I'll investigate that when I get a chance next. It could well be doing it, it sounds related.

Hmmm when is PYTHONDONTWRITEBYTECODE set? That actually prevents Python from writing .pyc files, which would in fact prevent the issue I mentioned, unless it is set after the initial .pyc files were written.

It's set right at the top of the Dockerfile before it runs "pip install ." to install our own code.

When is the mount done? Before or after pip install .?

Hmm I don't actually know as it uses a new tool to me called devspace, not quite sure how the mounting works.

It copies the code into the container but there is some sort of magic sync going on I don't yet understand how it works.

This should be fixed in pytest 6.2.1.

Hi @nicoddemus I know this got resolved but just letting you know I found out what caused my situation that raised a similar error.

The issue is that if we did "pip install ." to install our code, then later "pip install -e ." to over-install in development mode using -e, then there is a bug (probably in pip) that it still thinks it's installed part in /usr/local/lib/python3.9 therefore it threw a similar path mismatch error.

Now we don't do that anymore after learning this, we've learned that pip doesn't like this "pip install ." followed by "pip install -e ." over the top.

Now that we figured this out, I no longer need PY_IGNORE_IMPORTMISMATCH so I am pretty sure the bug is in pip.

@robvdl cool, thanks for the follow up!

Was this page helpful?
0 / 5 - 0 ratings