CabbageDevelopment / qasync

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

TypeException When Running with PyCharm Debugger #111

Closed j9ac9k closed 9 months ago

j9ac9k commented 10 months ago

Hello qasync maintainers,

Love the library! I am running into an issue that I don't know if it's an issue with PyCharm or an issue with the library. I figured I'd post here for insight.

When running a qasync application with the pycharm debugger, this exception is triggered right away, specifically the if not callable(callback) if-statemenet.

    def call_later(self, delay, callback, *args, context=None):
        """Register callback to be invoked after a certain delay."""
        if asyncio.iscoroutinefunction(callback):
            raise TypeError("coroutines cannot be used with call_later")
        if not callable(callback):
            raise TypeError(
                "callback must be callable: {}".format(type(callback).__name__)
            )

The example code:

import sys
import asyncio

from qasync import QEventLoop, QApplication, asyncClose, asyncSlot
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setLayout(QVBoxLayout())
        self.lbl_status = QLabel("Idle", self)
        self.layout().addWidget(self.lbl_status)

    @asyncClose
    async def closeEvent(self, event):
        pass

    @asyncSlot()
    async def onMyEvent(self):
        pass

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())

To trigger the fault, inside pycharm run the debugger on this file.

For extra debug information:

EDIT: Realized I didn't provide the full stack-trace, my apologies:

Connected to pydev debugger (build 233.13763.11)
Traceback (most recent call last):
  File "/Users/ogi/Developer/qasync-example/problem.py", line 37, in <module>
    event_loop.run_until_complete(app_close_event.wait())
  File "/Users/ogi/.pyenv/versions/doppkit/lib/python3.11/site-packages/qasync/__init__.py", line 394, in run_until_complete
    future = asyncio.ensure_future(future, loop=self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers-pro/pydevd_asyncio/pydevd_nest_asyncio.py", line 156, in ensure_future
    return loop.create_task(coro_or_future)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ogi/.pyenv/versions/3.11.4/lib/python3.11/asyncio/base_events.py", line 436, in create_task
    task = tasks.Task(coro, loop=self, name=name, context=context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Applications/PyCharm.app/Contents/plugins/python/helpers-pro/pydevd_asyncio/pydevd_nest_asyncio.py", line 390, in task_new_init
    self._loop.call_soon(self, context=self._context)
  File "/Users/ogi/.pyenv/versions/doppkit/lib/python3.11/site-packages/qasync/__init__.py", line 481, in call_soon
    return self.call_later(0, callback, *args, context=context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ogi/.pyenv/versions/doppkit/lib/python3.11/site-packages/qasync/__init__.py", line 459, in call_later
    raise TypeError(
TypeError: callback must be callable: Task
python-BaseException
kormax commented 9 months ago

This issue was introduced in PyCharm Debugger 2023.3.3.

It's caused by debugger hooks found in pydevd_nest_asyncio.py file, which monkey-patches the default asyncio.Task implementation (also used by qasync), resulting in this behaviour. As for what exactly changed, I'm not sure.

Rolling back to PyCharm 2023.2.5 fixes the issue.

UPD: Now I'm not sure about pydevd_nest_asyncio.py being the culprit, as when comparing them between two versions, there seems to be only 3 LOC difference, and It doesn't change anything in my case, so the issue could lie deeper, perhaps within pydevd.

j9ac9k commented 9 months ago

I think the debugger works in a newer version, 2023.3.1, I believe the breakage started at 2023.3.2. I'd be happy to open an issue in the pycharm bug tracker but I don't think I could explain the issue sufficiently (or in a way that won't make them point the finger back at this library).

hosaka commented 9 months ago

Need to identify what's causing this behaviour and there's something qasync can do to avoid rasing an exception. Unfortunately I don't use pycharm so not able to reproduce this.

j9ac9k commented 9 months ago

Need to identify what's causing this behaviour and there's something qasync can do to avoid rasing an exception. Unfortunately I don't use pycharm so not able to reproduce this.

Happy to try and drop into a debugger (heh) before the exception is raised and explore some, but I'm not sure what I should be looking for.

geopars commented 9 months ago

Hi guys, I can confirm what j9ac9k wrote. The breakage started with version 2023.3.2 and it also holds with version 2023.3.3

I think the debugger works in a newer version, 2023.3.1, I believe the breakage started at 2023.3.2. I'd be happy to open an issue in the pycharm bug tracker but I don't think I could explain the issue sufficiently (or in a way that won't make them point the finger back at this library).

kormax commented 9 months ago

This message describes the solution to this issue

Pycharm 2023.3.3 enabled asyncio support for the debugger, which was disabled before, even though the files were included with pydevd.

This issue at the JetBrains YouTrack platform describes the same problem happening with uvloop, another custom asyncio loop implementation.

As of now, the only solution is to disable asyncio support for the debugger (which was disabled before in the first place, so It won't be a big loss for those who have only encountered the issue just now).

This can be done by following the steps described here.

In short:

  1. Open PyCharm
  2. Use Shift + Shift (Search Everywhere)
  3. In the popup type: Registry... (with ...) and press Enter
  4. Find Registry... in the list of results and click on it.
  5. In the new popup find python.debug.asyncio.repl line and uncheck the respective checkbox
  6. Press Close.
  7. Restart the IDE.
  8. PROFIT

Considering that other event loop implementations have the same issues, I think it would be unwise to spend time trying to fix it in qasync, as JetBrains might fix it on their end. But it might be worth it for someone to chime in on the tracker so they take qasync into account

j9ac9k commented 9 months ago

Nice find on the existing issue elsewhere, it's a little disheartening that so many posts are about them not being able to replicate the issue.

Given there is a respective issue is already reported upstream, a workaround is posted, and this issue is present with another async library, I'm going to close out the issue. Sorry for the noise, but thanks for pointing out the upstream issue @kormax

j9ac9k commented 9 months ago

https://youtrack.jetbrains.com/issue/PY-62467

Upstream issue marked as fixed, but fixed version isn't specified... so assuming it it will be resolved in the next release?