pytest-dev / pytest-qt

pytest plugin for Qt (PyQt5/PyQt6 and PySide2/PySide6) application testing
https://pytest-qt.readthedocs.io
MIT License
409 stars 70 forks source link

Import error when running tests #410

Open svartkanin opened 2 years ago

svartkanin commented 2 years ago

When running any pytest I receive the following error

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/_pytest/main.py", line 264, in wrap_session
INTERNALERROR>     config._do_configure()
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/_pytest/config/__init__.py", line 992, in _do_configure
INTERNALERROR>     self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 277, in call_historic
INTERNALERROR>     res = self._hookexec(self.name, self.get_hookimpls(), kwargs, False)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pytestqt/plugin.py", line 203, in pytest_configure
INTERNALERROR>     qt_api.set_qt_api(config.getini("qt_api"))
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 75, in set_qt_api
INTERNALERROR>     self.pytest_qt_api = self._get_qt_api_from_env() or api or self._guess_qt_api()
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 64, in _guess_qt_api
INTERNALERROR>     if _can_import("PySide6.QtCore"):
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 56, in _can_import
INTERNALERROR>     _import(name)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 23, in _import
INTERNALERROR>     return __import__(name)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/shiboken2/files.dir/shibokensupport/__feature__.py", line 145, in _import
INTERNALERROR>     return original_import(name, *args, **kwargs)
INTERNALERROR>   File "/test/.venv/lib/python3.9/site-packages/IPython/external/qt_loaders.py", line 65, in find_spec
INTERNALERROR>     raise ImportError(
INTERNALERROR> ImportError: 
INTERNALERROR>     Importing PySide6 disabled by IPython, which has
INTERNALERROR>     already imported an Incompatible QT Binding: pyside2

I suspect it has something to do with the Ipython package that is being used as well. Here is the full list of installed dependencies

[tool.poetry.dependencies]
python = ">=3.9,<3.10"
importlib-metadata = "^4.11.2"
ipython = "^8.1.1"
matplotlib = "^3.5.1"
mypy = "^0.931"
mypy-extensions = "^0.4.3"
Pygments = "^2.11.2"
qiskit = "^0.34.2"
pylatexenc = "^2.10"
qtconsole = "^5.2.2"
Rx = "^3.2.0"
typed-ast = "^1.5.2"
typing-extensions = "^4.1.1"
zipp = "^3.7.0"
marshmallow = "^3.14.1"
marshmallow-enum = "^1.5.1"
marshmallow-dataclass = "^8.5.3"
yapf = "^0.32.0"
pre-commit = "^2.17.0"
pytest-qt = "^4.0.2"
pytest = "^7.0.1"
appdirs = "^1.4.4"
PySide2 = "^5.15.2"
The-Compiler commented 2 years ago

That error is coming from IPython, not pytest-qt. It looks like you will need to make sure that you configure the same Qt API for IPython (docs) and pytest-qt (docs).

svartkanin commented 2 years ago

I did configure the env variable QT_API for IPython QT_API=pyside. However, at this stage https://github.com/pytest-dev/pytest-qt/blob/master/src/pytestqt/qt_compat.py#L64, pytest-qt is trying to import any possible qt library, which in this case will throw a nasty error from IPython since that one is not allowing any other imports than the ones defined. Currently only ModuleNotFoundError is handled, but wouldn't it be possible to cater for others as well since there's a lot that might go wrong here?

        def _can_import(name):
            try:
                _import(name)
                return True
            except ModuleNotFoundError as e:
                self._import_errors[name] = str(e)
                return False

As an alternative and very simple solution may I propose to just change the order in which the imports are being tried, to something like

        if _can_import("PySide2.QtCore"):
            return "pyside2"
        elif _can_import("PyQt6.QtCore"):
            return "pyqt6"
        elif _can_import("PyQt5.QtCore"):
            return "pyqt5"
        elif _can_import("PySide6.QtCore"):
            return "pyside6"
The-Compiler commented 2 years ago

Hmm, right, apologies for closing this issue so quickly - reopening it for now.

eyllanesc commented 2 years ago

@svartkanin One workaround is to use PYTEST_QT_API=pyside2.

nicoddemus commented 2 years ago

@eyllanesc got it right: this will prevent pytest-qt from trying to guess which API to use (the _guess_api function).