Skip to content

AttributeError: '_QtApi' object has no attribute 'QtCore' #166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ghisvail opened this issue Feb 6, 2017 · 16 comments
Closed

AttributeError: '_QtApi' object has no attribute 'QtCore' #166

ghisvail opened this issue Feb 6, 2017 · 16 comments

Comments

@ghisvail
Copy link
Contributor

ghisvail commented Feb 6, 2017

Running:

from pytestqt.qt_compat import qt_api
print(qt_api.QtCore)

results in the following error:

AttributeError: '_QtApi' object has no attribute 'QtCore'

This error arises when pytest collects the test cases from tests/test_modeltest.py.

@The-Compiler
Copy link
Member

Your code snippet doesn't call set_qt_api, which the plugin does. The tests work fine here and on travis - can you show what exactly you're running and the full output?

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 6, 2017

can you show what exactly you're running and the full output?

cd /<<PKGBUILDDIR>>/.pybuild/pythonX.Y_3.5/build; python3.5 -m pytest tests
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
rootdir: /<<PKGBUILDDIR>>, inifile: 
collected 279 items / 1 errors

==================================== ERRORS ====================================
____ ERROR collecting .pybuild/pythonX.Y_3.5/build/tests/test_modeltest.py _____
tests/test_modeltest.py:8: in <module>
    class BasicModel(qt_api.QtCore.QAbstractItemModel):
E   AttributeError: '_QtApi' object has no attribute 'QtCore'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.19 seconds ============================

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 6, 2017

Since BasicModel derives from qt_api.QtCore.QAbstractItemModel, I tried to import qt_api.QtCore manually which yielded the error.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 6, 2017

I am also wondering whether there is a particular reason to implement yet another shim layer for Qt, when we already have stable alternatives such as qtpy?

@The-Compiler
Copy link
Member

This is with the plugin already installed?

As for your question: Because we need lazy loading, and also see #130 (comment)

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 6, 2017

This is with the plugin already installed?

What do you mean? The pytestqt library is staged under /<<PKGBUILDDIR>>/.pybuild/pythonX.Y_3.5/build and the test suite runs against it.

You can achieve the same setup by running:

python3 setup.py build
cp -R tests build/lib
cd build/lib
python3 -m pytest tests

@The-Compiler
Copy link
Member

I see, I can reproduce it with that setup. In all other configurations I tried, pytest picked up the plugin and thus tests succeeded.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 7, 2017

So, any idea how I can get the tests running in my case?

Also, assuming the package is now installed, are there any specific instructions for running the tests besides a call to pytest on the tests directory?

@The-Compiler
Copy link
Member

Simply running pytest (or pytest tests) in the repo root works here. Waiting for @nicoddemus to comment whether running the tests outside of the source tree is something we want to support 😉

When developing we usually use tox to run the tests against different Qt backends in virtualenvs, but if you're a packager you'll want to avoid that.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 7, 2017

Simply running pytest (or pytest tests) in the repo root works here.

So if it works on the root tree, but not for out-of-tree builds, then there is something fragile in the tests setup.

When developing we usually use tox [...]

Tox is just responsible for automation, the tests should work regardless of whether the necessary environment is setup by tox or by hand, correct?

if you're a packager you'll want to avoid that.

Indeed. We have already got packaging build scripts, which are responsible for running the builds against the supported versions of Python, setting up the tests and running them.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 7, 2017

Also, if I now run the tests against the installed package with pytest wrapped into a xvfb-run call, I get the following:

autopkgtest [12:45:01]: test command1: xvfb-run -a pytest tests
autopkgtest [12:45:01]: test command1: [-----------------------
============================= test session starts ==============================
platform linux2 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
PyQt5 5.7 -- Qt runtime 5.7.1 -- Qt compiled 5.7.1
rootdir: /tmp/autopkgtest.8fpesG/build.BEG/pytest-qt-2.1.0, inifile: 
plugins: qt-2.1.0
collected 308 items

tests/test_basics.py ......F....................
tests/test_exceptions.py .............
tests/test_logging.py .............................
tests/test_modeltest.py .............................
tests/test_qtest_proxies.py ......x......
tests/test_wait_signal.py ........................................................................................................................................................................s................s.....
tests/test_wait_until.py ......

=================================== FAILURES ===================================
______________________ test_wait_window[waitActive-True] _______________________

show = True, method_name = 'waitActive'
qtbot = <pytestqt.qtbot.QtBot object at 0x7fa7bd24dd90>

    @pytest.mark.parametrize('show', [True, False])
    @pytest.mark.parametrize('method_name', ['waitExposed', 'waitActive'])
    def test_wait_window(show, method_name, qtbot):
        """
        Using one of the wait-widget methods should not raise anything if the widget
        is properly displayed, otherwise should raise a TimeoutError.
        """
        method = getattr(qtbot, method_name)
        if qt_api.pytest_qt_api != 'pyqt5':
            with pytest.raises(RuntimeError) as exc_info:
                with method(None, None):
                    pass
            assert str(exc_info.value) == 'Available in PyQt5 only'
        else:
            widget = qt_api.QLineEdit()
            qtbot.add_widget(widget)
            if show:
                with method(widget, timeout=1000):
                    widget.move(100, 100)
                    widget.resize(100, 100)
                    widget.setFocus()
                    widget.show()
                    if 'TRAVIS' in os.environ and method_name == 'waitActive':
>                       pytest.xfail('skipping this check on travis, see #160')
E                       TimeoutError: widget <PyQt5.QtWidgets.QLineEdit object at 0x7fa7bd26e478> not activated in 1000 ms.

tests/test_basics.py:96: TimeoutError
----------------------------- Captured Qt messages -----------------------------
None:None:0:
    QtWarningMsg: Unsupported screen format: depth: 8, red_mask: 0, blue_mask: 0
None:None:0:
    QtWarningMsg: QPainter::begin: Paint device returned engine == 0, type: 3
None:None:0:
    QtWarningMsg: QPainter::setCompositionMode: Painter not active
None:None:0:
    QtWarningMsg: QWidget::paintEngine: Should no longer be called
None:None:0:
    QtWarningMsg: QPainter::begin: Paint device returned engine == 0, type: 1
None:None:0:
    QtWarningMsg: QPainter::save: Painter not active
None:None:0:
    QtWarningMsg: QPainter::setRenderHint: Painter must be active to set rendering hints
None:None:0:
    QtWarningMsg: QPainter::translate: Painter not active
None:None:0:
    QtWarningMsg: QPainter::setPen: Painter not active
None:None:0:
    QtWarningMsg: QPainter::setBrush: Painter not active
None:None:0:
    QtWarningMsg: QPainter::setPen: Painter not active
None:None:0:
    QtWarningMsg: QPainter::restore: Unbalanced save/restore
None:None:0:
    QtWarningMsg: QPainter::setClipRect: Painter not active
None:None:0:
    QtWarningMsg: QPainter::setPen: Painter not active
None:None:0:
    QtWarningMsg: QPainter::pen: Painter not active
========= 1 failed, 304 passed, 2 skipped, 1 xfailed in 27.54 seconds ==========

@The-Compiler
Copy link
Member

That test actually needs a window manager running. I'm not sure if there's a better check we could have there to ensure that's actually the case.

@nicoddemus
Copy link
Member

Hi guys, sorry for the delay.

@ghisvail, pytest-qt will configure the appropriate Qt api when loaded by pytest, as can be seen here:

def pytest_configure(config):
    ...

    qt_api.set_qt_api(config.getini('qt_api'))

This does not seem to be happening in your environment, that's why you are seeing that error.

pytest uses setuptools entry points to load installed plugins, and the output from your session hints that this mechanism is not working win your environment:

cd /<<PKGBUILDDIR>>/.pybuild/pythonX.Y_3.5/build; python3.5 -m pytest tests
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
rootdir: /<<PKGBUILDDIR>>, inifile: 
collected 279 items / 1 errors

You should see a header like this instead:

============================= test session starts =============================
platform win32 -- Python 3.5.0, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
PyQt5 5.7 -- Qt runtime 5.7.0 -- Qt compiled 5.7.0
rootdir: X:\pytest-qt, inifile:
plugins: qt-2.1.0

(Notice the plugins: qt-2.1.0 line and pytest-qt's header: PyQt5 5.7 -- Qt runtime 5.7.0 -- Qt compiled 5.7.0).

You can also execute the below to verify this:

>>> from pkg_resources import iter_entry_points
>>> list(iter_entry_points('pytest11'))
[EntryPoint.parse('pytest-qt = pytestqt.plugin')]

I'm guessing that iter_entry_points is returning an empty list in your environment.

Please note that you will probably have the same problems with all pytest plugins, as pytest relies on the setuptools entry points mechanism mentioned above.

Having said all that, there are ways to force pytest to load a plugin:

  1. Set the PYTEST_PLUGINS environment variable to pytestqt.plugin.
  2. Pass -p pytestqt.plugin when invoking pytest;
  3. Setting a pytest_plugins at your root conftest.py file:
pytest_plugins = 'pytestqt.plugin'

But please note that either way you will need PySide, PyQt4 or PyQt5 in your environment in order to run pytest-qt's tests as well.

Tox is just responsible for automation, the tests should work regardless of whether the necessary environment is setup by tox or by hand, correct?

That depends, you still need to setup the necessary environment correctly; tox will create a virtual environment and install the package and its dependencies on that environment, which is how we expect things to work.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 8, 2017

That test actually needs a window manager running. I'm not sure if there's a better check we could have there to ensure that's actually the case.

Perhaps using a custom marker, something like needs_wm? Not a true solution but at least I would not have to rely on patching.

@ghisvail
Copy link
Contributor Author

ghisvail commented Feb 8, 2017

@nicoddemus You can actually get the plugin detected by doing this additional step:

python3 setup.py build
cp -r tests build/lib
cp -r *.egg-info build/lib  # What was missing earlier... 
cd build/lib
python3 -m pytest tests

With these steps, you can now run in build/lib:

>>> from pkg_resources import iter_entry_points
>>> list(iter_entry_points('pytest11'))
[EntryPoint.parse('pytest-qt = pytestqt.plugin')]

It still does not succeed, so I am giving up on getting the tests run at build time and run them as a CI service on the installed Debian packages.

@nicoddemus
Copy link
Member

You can actually get the plugin detected by doing this additional step

That's nice to know, thanks.

It still does not succeed,

What happens, the plugin is still not loaded?

so I am giving up on getting the tests run at build time and run them as a CI service on the installed Debian packages.

Well OK then. I'm closing this for now. Thanks for trying so hard to get the tests to run though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants