Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Using aiohttp with QtAsyncio
QtWS25 Last Chance

Using aiohttp with QtAsyncio

Scheduled Pinned Locked Moved Unsolved Qt for Python
10 Posts 6 Posters 1.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • F Offline
    F Offline
    Faerbit
    wrote on 2 Mar 2024, 16:57 last edited by
    #1

    Hi,

    I'd like to use QtAsyncio in my application. I want to make web requests from it, using aiohttp, and therefore want to have an application-wide aiohttp.ClientSession .
    I have come this far:

    import asyncio
    import sys
    
    import PySide6.QtAsyncio as QtAsyncio
    import aiohttp
    from PySide6.QtCore import Qt
    from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
    
    
    class AioSessionHolder:
        def __init__(self):
            loop = asyncio.get_event_loop()
            self.session = loop.run_until_complete(self.create_session())
    
        @staticmethod
        async def create_session():
            return aiohttp.ClientSession()
    
        def close(self):
            if self.session:
                loop = asyncio.new_event_loop()
                loop.run_until_complete(self.session.close())
                self.session = None
    
        async def async_close(self):
            if self.session:
                await self.session.close()
                self.session = None
    
    
    class MainWindow(QMainWindow):
    
        def __init__(self, aio_session: AioSessionHolder):
            super().__init__()
            self.aio_session = aio_session.session
    
            widget = QWidget()
            self.setCentralWidget(widget)
    
            layout = QVBoxLayout(widget)
    
            self.text = QLabel("The answer is 42.")
            layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
    
            async_trigger = QPushButton(text="What is the question?")
            async_trigger.clicked.connect(lambda: asyncio.ensure_future(self.set_text()))
            layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
    
        async def set_text(self):
            resp = await self.aio_session.get("https://httpbin.org/get")
            self.text.setText(resp.status_code)
    
    
    if __name__ == "__main__":
        aio_session = AioSessionHolder()
    
        app = QApplication(sys.argv)
        main_window = MainWindow(aio_session)
        main_window.show()
    
        QtAsyncio.run()
    

    However this has at least two problems:

    1. The request part does not work, I get Timeout context manager should be used inside a task as an error message
    2. I was unable to integrate any session closing, without error messages (see my attempts above)

    Can anybody give a minimal working example like the one above?

    I would really like to use async in my application 🙂

    Q 1 Reply Last reply 22 Apr 2024, 14:48
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 2 Mar 2024, 19:36 last edited by
      #2

      Hi and welcome to devnet,

      Not that I don't want to help you with that module (though I haven't used it yet) but why not make use QNetworkAccessManager as it is already asynchronous and does not require an external library ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      F W Q 3 Replies Last reply 3 Mar 2024, 11:48
      1
      • S SGaist
        2 Mar 2024, 19:36

        Hi and welcome to devnet,

        Not that I don't want to help you with that module (though I haven't used it yet) but why not make use QNetworkAccessManager as it is already asynchronous and does not require an external library ?

        F Offline
        F Offline
        Faerbit
        wrote on 3 Mar 2024, 11:48 last edited by
        #3

        Thank you for your suggestion.

        First of all it didn't occur to me, that this exists. aiohttp seems fairly standard, that's why I found while googling for solutions to my problems.

        Having looked at it, I still think, that I prefer using aiohttp since it uses the python-native asynchronicity, which means I can use await instead of Slots. Unless I'm mistaken, and didn't understand how QNetworkAccessManager works?
        Using await makes for cleaner code imo.

        As for it being external: I actually think of this as a feature, as this will allow for greater separation between functionality and presentation.

        1 Reply Last reply
        1
        • S SGaist
          2 Mar 2024, 19:36

          Hi and welcome to devnet,

          Not that I don't want to help you with that module (though I haven't used it yet) but why not make use QNetworkAccessManager as it is already asynchronous and does not require an external library ?

          W Offline
          W Offline
          Winton
          wrote on 12 Mar 2024, 18:18 last edited by
          #4

          @SGaist I am also experiencing a similar problem using the Python HTTPX package. The code simply makes an async request to an HTTP API that returns a JSON data. The code works perfectly everywhere else, but fails with QtAsyncio of PySide6.

          The reason we prefer HTTPX to QNetworkAccessManager is because we come from a Python background and are more familiar with packages like HTTPX, aiohttp etc, which have much bigger community with more examples as well.

          Returning to the topic. After debugging for a while, I believe our problem is the QAsyncioEventLoop class in QtAsyncio doesn't have the relavent network async events implemented, e.g. DNS, Sockets etc. In other words, the current even loop is not complete. An async HTTP request requires the even loop (QAsyncioEventLoop) to be able to handle DNS event (and more). If not, the event lool will raise a NotImplementedError exception that is not properly captured and the app just fails silently.

          S Q 2 Replies Last reply 14 Mar 2024, 20:43
          0
          • W Winton
            12 Mar 2024, 18:18

            @SGaist I am also experiencing a similar problem using the Python HTTPX package. The code simply makes an async request to an HTTP API that returns a JSON data. The code works perfectly everywhere else, but fails with QtAsyncio of PySide6.

            The reason we prefer HTTPX to QNetworkAccessManager is because we come from a Python background and are more familiar with packages like HTTPX, aiohttp etc, which have much bigger community with more examples as well.

            Returning to the topic. After debugging for a while, I believe our problem is the QAsyncioEventLoop class in QtAsyncio doesn't have the relavent network async events implemented, e.g. DNS, Sockets etc. In other words, the current even loop is not complete. An async HTTP request requires the even loop (QAsyncioEventLoop) to be able to handle DNS event (and more). If not, the event lool will raise a NotImplementedError exception that is not properly captured and the app just fails silently.

            S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 14 Mar 2024, 20:43 last edited by
            #5

            @Winton thanks for the hints. Based on the QtAsyncio TP announcement, you are likely correct about the network side of things (See the Level 1 and 2 implementations).

            Which version of PySide6 are you using ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            0
            • S SGaist
              2 Mar 2024, 19:36

              Hi and welcome to devnet,

              Not that I don't want to help you with that module (though I haven't used it yet) but why not make use QNetworkAccessManager as it is already asynchronous and does not require an external library ?

              Q Offline
              Q Offline
              QtIoAdmin
              wrote on 14 Apr 2024, 02:17 last edited by QtIoAdmin
              #6
              This post is deleted!
              1 Reply Last reply
              0
              • W Winton
                12 Mar 2024, 18:18

                @SGaist I am also experiencing a similar problem using the Python HTTPX package. The code simply makes an async request to an HTTP API that returns a JSON data. The code works perfectly everywhere else, but fails with QtAsyncio of PySide6.

                The reason we prefer HTTPX to QNetworkAccessManager is because we come from a Python background and are more familiar with packages like HTTPX, aiohttp etc, which have much bigger community with more examples as well.

                Returning to the topic. After debugging for a while, I believe our problem is the QAsyncioEventLoop class in QtAsyncio doesn't have the relavent network async events implemented, e.g. DNS, Sockets etc. In other words, the current even loop is not complete. An async HTTP request requires the even loop (QAsyncioEventLoop) to be able to handle DNS event (and more). If not, the event lool will raise a NotImplementedError exception that is not properly captured and the app just fails silently.

                Q Offline
                Q Offline
                QtIoAdmin
                wrote on 14 Apr 2024, 02:37 last edited by QtIoAdmin
                #7
                This post is deleted!
                1 Reply Last reply
                0
                • F Faerbit
                  2 Mar 2024, 16:57

                  Hi,

                  I'd like to use QtAsyncio in my application. I want to make web requests from it, using aiohttp, and therefore want to have an application-wide aiohttp.ClientSession .
                  I have come this far:

                  import asyncio
                  import sys
                  
                  import PySide6.QtAsyncio as QtAsyncio
                  import aiohttp
                  from PySide6.QtCore import Qt
                  from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
                  
                  
                  class AioSessionHolder:
                      def __init__(self):
                          loop = asyncio.get_event_loop()
                          self.session = loop.run_until_complete(self.create_session())
                  
                      @staticmethod
                      async def create_session():
                          return aiohttp.ClientSession()
                  
                      def close(self):
                          if self.session:
                              loop = asyncio.new_event_loop()
                              loop.run_until_complete(self.session.close())
                              self.session = None
                  
                      async def async_close(self):
                          if self.session:
                              await self.session.close()
                              self.session = None
                  
                  
                  class MainWindow(QMainWindow):
                  
                      def __init__(self, aio_session: AioSessionHolder):
                          super().__init__()
                          self.aio_session = aio_session.session
                  
                          widget = QWidget()
                          self.setCentralWidget(widget)
                  
                          layout = QVBoxLayout(widget)
                  
                          self.text = QLabel("The answer is 42.")
                          layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
                  
                          async_trigger = QPushButton(text="What is the question?")
                          async_trigger.clicked.connect(lambda: asyncio.ensure_future(self.set_text()))
                          layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
                  
                      async def set_text(self):
                          resp = await self.aio_session.get("https://httpbin.org/get")
                          self.text.setText(resp.status_code)
                  
                  
                  if __name__ == "__main__":
                      aio_session = AioSessionHolder()
                  
                      app = QApplication(sys.argv)
                      main_window = MainWindow(aio_session)
                      main_window.show()
                  
                      QtAsyncio.run()
                  

                  However this has at least two problems:

                  1. The request part does not work, I get Timeout context manager should be used inside a task as an error message
                  2. I was unable to integrate any session closing, without error messages (see my attempts above)

                  Can anybody give a minimal working example like the one above?

                  I would really like to use async in my application 🙂

                  Q Offline
                  Q Offline
                  QtIoAdmin
                  wrote on 22 Apr 2024, 14:48 last edited by QtIoAdmin
                  #8

                  @Faerbit This seems to be a known problem due to the fact, and there seems to be a library to address this problem:qasync. The following is a minimal sample I have assembled to use async/await aiohttp in a PySide6 app. It worked on my computer.

                  import asyncio
                  import aiohttp
                  import qasync
                  from PySide6.QtCore import Qt
                  from PySide6.QtWidgets import QApplication, QPushButton, QMainWindow, QVBoxLayout, QWidget
                  
                  
                  class MainWindow(QMainWindow):
                      def __init__(self):
                          super().__init__()
                          self.resize(600, 400)
                          self.central_widget = QWidget()
                          self.layout = QVBoxLayout()
                          self.button = QPushButton("Fetch Data")
                          self.button.clicked.connect(lambda : asyncio.create_task(self.fetch_data()))
                          self.layout.addWidget(self.button, alignment=Qt.AlignCenter)
                          self.central_widget.setLayout(self.layout)
                          self.setCentralWidget(self.central_widget)
                  
                      async def fetch_data(self):
                          self.setWindowTitle("Loading...")
                          async with aiohttp.ClientSession() as session:
                              async with session.get('https://reqres.in/api/users?delay=5') as response:
                                  if response.status == 200:
                                      text = await response.text()
                                      print(text)
                                      self.setWindowTitle("Got")
                  
                  
                  app = QApplication([])
                  window = MainWindow()
                  window.show()
                  loop = qasync.QEventLoop(app)
                  with loop:
                      loop.run_forever()
                  
                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    misantroop
                    wrote on 12 Mar 2025, 08:37 last edited by
                    #9

                    An alternative approach is to switch policy to the default, this can be problematic if there are other events but in my case it works, nest_asyncio is required.

                    import nest_asyncio
                    nest_asyncio.apply()
                    
                    class CustomAsyncEventPolicy:
                        def __init__(self, policy: asyncio.AbstractEventLoopPolicy | None):
                            self._policy = policy
                            self._qt_policy = asyncio.get_event_loop_policy()
                    
                        def __enter__(self) -> None:
                            asyncio.set_event_loop_policy(self._policy)
                    
                        def __exit__(self, exc_type, exc_val, exc_tb) -> None:
                            asyncio.set_event_loop_policy(self._qt_policy)
                    
                    class Main(QtWidgets.QMainWindow):
                        def __init__(self):
                            super(Main, self).__init__()
                    
                        def do_something_with_native_policy(self) -> None:
                            with CustomAsyncEventPolicy(None):
                                self.default_async_policy_loop_call()
                    
                        def default_async_policy_loop_call(self):
                            try:
                                loop = asyncio.new_event_loop()
                                asyncio.set_event_loop(loop)
                                loop.run_until_complete(
                                    self.your_aiohttp_async_fn()
                                )
                            except Exception:
                                raise
                            finally:
                                if not loop.is_closed():
                                    loop.close()
                    
                        def your_aiohttp_async_fn(self):
                            ...
                    
                    if __name__ == "__main__":
                        app = QtWidgets.QApplication(sys.argv)
                        main = Main()
                        main.show()
                        QtAsyncio.run(keep_running=True, quit_qapp=True)
                    
                    1 Reply Last reply
                    0
                    • F Offline
                      F Offline
                      friedemannkleint
                      wrote on 12 Mar 2025, 12:08 last edited by
                      #10

                      For the record, there is https://bugreports.qt.io/browse/PYSIDE-2713 . Contributions for this would be very welcome.

                      1 Reply Last reply
                      1

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved