CabbageDevelopment / qasync

Python library for using asyncio in Qt-based applications.
BSD 2-Clause "Simplified" License
307 stars 43 forks source link

QEventLoop confuses asyncio's `get_running_loop` #72

Closed mdickinson closed 1 year ago

mdickinson commented 1 year ago

QEventLoop.__init__ does asyncio.events._set_running_loop(self), which has the effect that asyncio.get_running_loop() then returns an event loop.

For cases where a QEventLoop is being created within an already running asyncio event loop, this may be the right thing to do; I'm not sure. In our use-case, we're creating the QEventLoop before entering the asyncio event loop, so this causes some confusion.

Additionally, after the event loop has exited, asyncio.get_running_loop() should also be raising RuntimeError. Instead, it's returning the QEventLoop, even though there's no running loop at that point.

This is particularly affecting our unit tests: after a unit test that's using the QEventLoop we want to set asyncio back to a state that's more-or-less what happens before the QEventLoop was created. For that I'm doing:

        if asyncio.get_event_loop().is_closed():
            asyncio.set_event_loop(asyncio.new_event_loop())

But this silently fails to work (tested on Python 3.8): asyncio.get_event_loop continues to return the QEventLoop, since that event loop is still registered as running.

In case others run into the same issue: we're working around this in our own code by using the set_running_loop=False option when creating the QEventLoop, and then wrapping calls to run_forever as follows:

asyncio.events._set_running_loop(event_loop)
try:
    event_loop.run_forever()
finally:
    asyncio.events._set_running_loop(None)
hosaka commented 1 year ago

@mdickinson this is fixed in #71 (includes the changes from #75). asyncio.events._set_running_loop(None) is now being set by run_forever()

mdickinson commented 1 year ago

@hosaka Thank you! I look forward to testing it.