From 09a85aa73e1c39d567af77cd6dbebd9199d268d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 14:23:13 +0000 Subject: [PATCH 1/9] update example in Readme --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index d5743f2..b73e783 100644 --- a/README.md +++ b/README.md @@ -46,17 +46,13 @@ class MainWindow(QWidget): if __name__ == "__main__": app = QApplication(sys.argv) - event_loop = QEventLoop(app) - asyncio.set_event_loop(event_loop) - app_close_event = asyncio.Event() app.aboutToQuit.connect(app_close_event.set) main_window = MainWindow() main_window.show() - with event_loop: - event_loop.run_until_complete(app_close_event.wait()) + asyncio.run(app_close_event.wait(), loop_factory=lambda:QEventLoop(app)) ``` More detailed examples can be found [here](https://github.com/CabbageDevelopment/qasync/tree/master/examples). From fccccb8963bf6a0637bfa32c5dd685984c362bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 14:26:31 +0000 Subject: [PATCH 2/9] update aiohttp example --- examples/aiohttp_fetch.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/aiohttp_fetch.py b/examples/aiohttp_fetch.py index 1fdcc4a..9390c31 100644 --- a/examples/aiohttp_fetch.py +++ b/examples/aiohttp_fetch.py @@ -68,15 +68,14 @@ async def on_btn_fetch_clicked(self): if __name__ == "__main__": app = QApplication(sys.argv) - event_loop = QEventLoop(app) - asyncio.set_event_loop(event_loop) - app_close_event = asyncio.Event() app.aboutToQuit.connect(app_close_event.set) main_window = MainWindow() main_window.show() - event_loop.create_task(main_window.boot()) - event_loop.run_until_complete(app_close_event.wait()) - event_loop.close() + async def async_main(): + asyncio.create_task(main_window.boot()) + await app_close_event.wait() + + asyncio.run(async_main(), loop_factory=lambda:QEventLoop(app)) From 1a1b2ccd8c46e8324ede231e5147223b98095467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 14:28:21 +0000 Subject: [PATCH 3/9] update executor example --- examples/executor_example.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/executor_example.py b/examples/executor_example.py index f208912..d51d177 100644 --- a/examples/executor_example.py +++ b/examples/executor_example.py @@ -34,8 +34,4 @@ def last_50(progress, loop): if __name__ == "__main__": app = QApplication(sys.argv) - event_loop = QEventLoop(app) - asyncio.set_event_loop(event_loop) - - event_loop.run_until_complete(master()) - event_loop.close() + asyncio.run(master(), loop_factory=lambda:QEventLoop(app)) From 2132aa7a172b5faa11174a8b091c24310fa73250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 14:33:19 +0000 Subject: [PATCH 4/9] update _QEventLoop doc --- qasync/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/qasync/__init__.py b/qasync/__init__.py index 732f7a2..f6bbfcb 100644 --- a/qasync/__init__.py +++ b/qasync/__init__.py @@ -331,11 +331,8 @@ class _QEventLoop: ... assert x + y == 4 ... await asyncio.sleep(.1) >>> - >>> loop = QEventLoop(app) - >>> asyncio.set_event_loop(loop) - >>> with loop: - ... loop.run_until_complete(xplusy(2, 2)) - + >>> asyncio.run(xplusy(2, 2), loop_factory=lambda:QEventLoop(app)) + If the event loop shall be used with an existing and already running QApplication it must be specified in the constructor via already_running=True In this case the user is responsible for loop cleanup with stop() and close() From f1d9cdf293adaae90b95167edd1c2505520b106a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 14:49:39 +0000 Subject: [PATCH 5/9] add tests for asyncio.run() --- tests/test_qeventloop.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_qeventloop.py b/tests/test_qeventloop.py index 9eefc72..f9ecf36 100644 --- a/tests/test_qeventloop.py +++ b/tests/test_qeventloop.py @@ -808,6 +808,48 @@ async def mycoro(): assert "seconds" in msg +def test_asyncio_run(application): + "Test that QEventLoop is compatible with asyncio.run()" + done = False + loop = None + async def main(): + nonlocal done, loop + assert loop.is_running() + assert asyncio.get_running_loop() is loop + await asyncio.sleep(0.01) + done = True + + def factory(): + nonlocal loop + loop = qasync.QEventLoop(application) + return loop + + asyncio.run(main(), loop_factory=factory) + assert done + assert loop.is_closed() + assert not loop.is_running() + + +def test_asyncio_run_cleanup(application): + "Test that running tasks are_cleaned up" + task = None + cancelled = False + async def main(): + nonlocal task, cancelled + async def long_task(): + nonlocal cancelled + try: + await asyncio.sleep(10) + except asyncio.CancelledError: + cancelled = True + + task = asyncio.create_task(long_task()) + await asyncio.sleep(0.01) + + asyncio.run(main(), loop_factory=lambda:qasync.QEventLoop(application)) + assert cancelled + + def teardown_module(module): """ Remove handlers from all loggers From 0aa72b3b4f24d907cd39c16b6c2b200cbc3054ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 16:59:20 +0000 Subject: [PATCH 6/9] add qasync.run*() test --- tests/test_qeventloop.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_qeventloop.py b/tests/test_qeventloop.py index f9ecf36..c352121 100644 --- a/tests/test_qeventloop.py +++ b/tests/test_qeventloop.py @@ -808,6 +808,7 @@ async def mycoro(): assert "seconds" in msg +@pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires Python 3.12+") def test_asyncio_run(application): "Test that QEventLoop is compatible with asyncio.run()" done = False @@ -830,6 +831,7 @@ def factory(): assert not loop.is_running() +@pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires Python 3.12+") def test_asyncio_run_cleanup(application): "Test that running tasks are_cleaned up" task = None @@ -850,6 +852,28 @@ async def long_task(): assert cancelled +def test_qasync_run(application): + done = False + loop = None + async def main(): + nonlocal done, loop + loop = asyncio.get_running_loop() + assert loop.is_running() + await asyncio.sleep(0.01) + done = True + + def factory(): + nonlocal loop + loop = qasync.QEventLoop(application) + return loop + + # qasync.run uses an EventLoopPolicy to create the loop + qasync.run(main()) + assert done + assert loop.is_closed() + assert not loop.is_running() + + def teardown_module(module): """ Remove handlers from all loggers From 1d9356cf9cd6b3e897cea9862d42e8cabe572251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 23 Jul 2025 17:21:26 +0000 Subject: [PATCH 7/9] update examples to use qasync.run() --- README.md | 1 + examples/aiohttp_fetch.py | 4 ++-- examples/executor_example.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b73e783..3577363 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ if __name__ == "__main__": main_window.show() asyncio.run(app_close_event.wait(), loop_factory=lambda:QEventLoop(app)) + # for 3.11 or older use: qasync.run(app_close_event.wait()) ``` More detailed examples can be found [here](https://github.com/CabbageDevelopment/qasync/tree/master/examples). diff --git a/examples/aiohttp_fetch.py b/examples/aiohttp_fetch.py index 9390c31..aa77ccd 100644 --- a/examples/aiohttp_fetch.py +++ b/examples/aiohttp_fetch.py @@ -13,7 +13,7 @@ QVBoxLayout, QWidget, ) -from qasync import QEventLoop, asyncClose, asyncSlot +from qasync import asyncClose, asyncSlot, run as qasync_run class MainWindow(QWidget): @@ -78,4 +78,4 @@ async def async_main(): asyncio.create_task(main_window.boot()) await app_close_event.wait() - asyncio.run(async_main(), loop_factory=lambda:QEventLoop(app)) + qasync_run(async_main()) diff --git a/examples/executor_example.py b/examples/executor_example.py index d51d177..c4f24d9 100644 --- a/examples/executor_example.py +++ b/examples/executor_example.py @@ -5,7 +5,7 @@ # from PyQt6.QtWidgets import from PySide6.QtWidgets import QApplication, QProgressBar -from qasync import QEventLoop, QThreadExecutor +from qasync import QThreadExecutor, run as qasync_run async def master(): @@ -34,4 +34,4 @@ def last_50(progress, loop): if __name__ == "__main__": app = QApplication(sys.argv) - asyncio.run(master(), loop_factory=lambda:QEventLoop(app)) + qasync_run(master()) From 4561d1ed62113c4580eb4211fd4b84d53294ba8d Mon Sep 17 00:00:00 2001 From: Alex March Date: Thu, 24 Jul 2025 11:13:45 +0900 Subject: [PATCH 8/9] tidy up tests --- tests/test_qeventloop.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_qeventloop.py b/tests/test_qeventloop.py index c352121..d2feb96 100644 --- a/tests/test_qeventloop.py +++ b/tests/test_qeventloop.py @@ -810,21 +810,22 @@ async def mycoro(): @pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires Python 3.12+") def test_asyncio_run(application): - "Test that QEventLoop is compatible with asyncio.run()" + """Test that QEventLoop is compatible with asyncio.run()""" done = False loop = None + async def main(): nonlocal done, loop assert loop.is_running() assert asyncio.get_running_loop() is loop await asyncio.sleep(0.01) done = True - + def factory(): nonlocal loop loop = qasync.QEventLoop(application) return loop - + asyncio.run(main(), loop_factory=factory) assert done assert loop.is_closed() @@ -833,11 +834,13 @@ def factory(): @pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires Python 3.12+") def test_asyncio_run_cleanup(application): - "Test that running tasks are_cleaned up" + """Test that running tasks are cleaned up""" task = None cancelled = False + async def main(): nonlocal task, cancelled + async def long_task(): nonlocal cancelled try: @@ -847,26 +850,23 @@ async def long_task(): task = asyncio.create_task(long_task()) await asyncio.sleep(0.01) - - asyncio.run(main(), loop_factory=lambda:qasync.QEventLoop(application)) + + asyncio.run(main(), loop_factory=lambda: qasync.QEventLoop(application)) assert cancelled def test_qasync_run(application): + """Test running with qasync.run()""" done = False loop = None + async def main(): nonlocal done, loop loop = asyncio.get_running_loop() assert loop.is_running() await asyncio.sleep(0.01) done = True - - def factory(): - nonlocal loop - loop = qasync.QEventLoop(application) - return loop - + # qasync.run uses an EventLoopPolicy to create the loop qasync.run(main()) assert done From 2016e78a9bc250f0841edfe8cad364142a2c5971 Mon Sep 17 00:00:00 2001 From: Alex March Date: Thu, 24 Jul 2025 11:16:56 +0900 Subject: [PATCH 9/9] mention qasync.run but use asyncio.run --- README.md | 5 +++-- examples/aiohttp_fetch.py | 11 +++++++---- examples/executor_example.py | 11 +++++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3577363..b4a347f 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,9 @@ if __name__ == "__main__": main_window = MainWindow() main_window.show() - asyncio.run(app_close_event.wait(), loop_factory=lambda:QEventLoop(app)) - # for 3.11 or older use: qasync.run(app_close_event.wait()) + # for 3.11 or older use qasync.run instead of asyncio.run + # qasync.run(app_close_event.wait()) + asyncio.run(app_close_event.wait(), loop_factory=QEventLoop) ``` More detailed examples can be found [here](https://github.com/CabbageDevelopment/qasync/tree/master/examples). diff --git a/examples/aiohttp_fetch.py b/examples/aiohttp_fetch.py index aa77ccd..1e4c2ee 100644 --- a/examples/aiohttp_fetch.py +++ b/examples/aiohttp_fetch.py @@ -13,7 +13,8 @@ QVBoxLayout, QWidget, ) -from qasync import asyncClose, asyncSlot, run as qasync_run + +from qasync import QEventLoop, asyncClose, asyncSlot class MainWindow(QWidget): @@ -70,12 +71,14 @@ async def on_btn_fetch_clicked(self): app_close_event = asyncio.Event() app.aboutToQuit.connect(app_close_event.set) - + main_window = MainWindow() main_window.show() async def async_main(): asyncio.create_task(main_window.boot()) await app_close_event.wait() - - qasync_run(async_main()) + + # for 3.11 or older use qasync.run instead of asyncio.run + # qasync.run(async_main()) + asyncio.run(async_main(), loop_factory=QEventLoop) diff --git a/examples/executor_example.py b/examples/executor_example.py index c4f24d9..2dee234 100644 --- a/examples/executor_example.py +++ b/examples/executor_example.py @@ -1,11 +1,12 @@ -import functools import asyncio -import time +import functools import sys +import time # from PyQt6.QtWidgets import from PySide6.QtWidgets import QApplication, QProgressBar -from qasync import QThreadExecutor, run as qasync_run + +from qasync import QEventLoop, QThreadExecutor async def master(): @@ -34,4 +35,6 @@ def last_50(progress, loop): if __name__ == "__main__": app = QApplication(sys.argv) - qasync_run(master()) + # for 3.11 or older use qasync.run instead of asyncio.run + # qasync.run(master()) + asyncio.run(master(), loop_factory=QEventLoop)