pytest-dev / pytest-qt

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

macos 14.0 (sonoma) doesn't pass control to qt #521

Closed tlambert03 closed 11 months ago

tlambert03 commented 11 months ago

Just updated a computer to macos 14.0 and noticed something quite different about the way in which python is starting the Qt application, which has some pretty big implications for running tests locally in pytest-qt.

When running pytest on the following code, the terminal simply blocks and nothing happens until you manually switch to the ~additional python process~ new application that was started:

def test_qtbot(qtbot):
    assert True

https://github.com/pytest-dev/pytest-qt/assets/1609449/49f088f7-6fb8-48a3-97b5-49b52bfefdae

(I've noticed a similar thing in IPython: where starting an interactive event loop with %gui qt always used to switch the current macos app over to the new python ~process~ app in the dock ... and now it just starts it in the background).

Anyway, not yet sure what could be done here to allow tests to proceed without manually switching over to the new app.

cc @nicoddemus @The-Compiler ... curious if you guys have thoughts?

tlambert03 commented 11 months ago

(to be clear, I recognize this is something that is in no way an issue in pytest-qt ... but a number of pytest-qt users will certainly come across it :)

tlambert03 commented 11 months ago

note: I also tried running a framework build ... and got the same result

conda install -c conda-forge python.app
pythonw -m pytest
nicoddemus commented 11 months ago

Hi @tlambert03,

Strange that the user is required to start a separate process. Is that a known requirement, or a something particular to your system?

tlambert03 commented 11 months ago

I'm sorry, that was a very bad use of the word "process" :joy: I'm not 100% sure what happens under the hood in macos when a QApplication begins, but what I'm referring to is the additional icon that appears in the dock, and the renaming of the single python process. This has always happened (shown below on macos 12.6).

https://github.com/pytest-dev/pytest-qt/assets/1609449/90007e28-1c14-41e0-9b1c-dc65738b8ff2

before macOS 14, and in the video above, the system would automatically switch over to that new "thing". The terminal loses focus, the name of the current app in the menu bar would change, etc... In sonoma, that icon still appears, but mac doesn't switch over to it. (I've never entirely understood what that "switching" entails, that's what I was misleadingly referring to as a process).

For pytest-qt, all I can say at the moment is that tests simply don't proceed anymore until I command-tab over to that new thing/icon/app that gets created upon instantiation of a QApplication instance. The event loop never starts, the whole thing just hangs.

tlambert03 commented 11 months ago

(I updated the original post to strikeout the word process to avoid confusion for new readers)

tlambert03 commented 11 months ago

Since I know this is hard to work on without being able to reproduce, I'm more than happy to help look in to this on the system with macos 14. If you want to give me pointers on where to start digging

nicoddemus commented 11 months ago

My suggestion would be to reduce this to a simple Qt script:

app = QApplication([])
w = QWidget()
w.show()
app.exec()

(I understand this has the same problem when executed from a terminal)

With that script, is easier to ask on SO for solutions, as this becomes a general Qt issue, unrelated to testing/pytest-qt.

tlambert03 commented 11 months ago

Yes the simple script is simply to instantiate an application (no widget needed).

As mentioned above, I know this isn't a pytest-qt issue directly. But that I suspect using pytest qt will be the first way many discover this change (for example, all my apps work and interact fine, it's only in testing where the change became obvious)

If you like, feel free to close this :)

nicoddemus commented 11 months ago

If it there's a solution, and is easy to integrate with pytest-qt, it would be fine, but I suspect it is more complicate than that.

I will close for now, but if you discover anything interesting @tlambert03, please report back and we can decide on the next course of action if any. 👍

GeorgyIvanov commented 11 months ago

I asked a question on StackOverflow, perhaps someone can come up with workarounds there.

GeorgyIvanov commented 11 months ago

By the way, the new App Activation API in Sonoma sounds somewhat relevant, especially the part

cooperative app activation reduces unexpected application switches, for example, an app switch while you are in the middle of typing.

tlambert03 commented 11 months ago

absolutely... good find

The-Compiler commented 11 months ago

Also see: [QTBUG-114349] Adapt to application activation APIs in macOS 14 - Qt Bug Tracker

tlambert03 commented 11 months ago

perfect. thank you @The-Compiler

GeorgyIvanov commented 11 months ago

Tested a bit more, and it looks like the specific call that hangs is app.processEvents() here:

def _process_events():
    """Calls app.processEvents() while taking care of capturing exceptions
    or not based on the given item's configuration.
    """
    app = qt_api.QtWidgets.QApplication.instance()
    if app is not None:
        app.processEvents()

... which calls this native method.

GeorgyIvanov commented 11 months ago

Lol, and my tests began to pass again after I replaced this by

def _process_events():
    """Calls app.processEvents() while taking care of capturing exceptions
    or not based on the given item's configuration.
    """
    app = qt_api.QtWidgets.QApplication.instance()
    if app is not None:
        # app.processEvents()
        pass

in lib/python3.11/site-packages/pytestqt/plugin.py.