Skip to content

SystemError in guest mode when cancelling finished scope #1576

@altendky

Description

@altendky

Full code at the end but here's the 'interesting' snippet that causes trouble when run in guest mode. Tests run with Python 3.8.3 in Linux and 08fca2a.

async def mine():
    global cscope
    widget = QtWidgets.QWidget()
    with trio.CancelScope() as cscope:
        app.lastWindowClosed.connect(cscope.cancel)
        widget.show()
    return 1

With just widget.show() I get:

/home/altendky/repos/trio/trio/_core/_run.py:2161: RuntimeWarning: Trio guest run got abandoned without properly finishing... weird stuff might happen
  warnings.warn(

With app.lastWindowClosed.connect(cscope.cancel) and widget.show() I get:

StopIteration: 1

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/altendky/repos/trio/trio/_core/_ki.py", line 159, in wrapper
    locals()
SystemError: <built-in function locals> returned a result with an error set
/home/altendky/repos/trio/trio/_core/_run.py:2161: RuntimeWarning: Trio guest run got abandoned without properly finishing... weird stuff might happen
  warnings.warn(

I've tried to recreate the error without PyQt by just putting the scope cancellation further out but it results in no issues. I thought maybe PyQt was holding onto a reference to a no longer valid object but global cscope didn't avoid the SystemError. The same SystemError occurs back to the original commit in #1551, at least there and a few spot checks along the way.

I just bothered to follow the why-not-pyside2 link https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1313 and at least the StopIteration and SystemError are similar albeit against a different function call.

This isn't any sort of a blocker, just a thing where you have to be careful to disconnect a signal.

Thanks again for guest mode. Someday I'll do something more than a trivial demo with it. :]

Full example source
import trio
import sys
from outcome import Error
import traceback

# Can't use PySide2 currently because of
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1313
from PyQt5 import QtCore, QtWidgets

async def mine():
    global cscope
    widget = QtWidgets.QWidget()
    with trio.CancelScope() as cscope:
        app.lastWindowClosed.connect(cscope.cancel)
        widget.show()
    return 1

app = QtWidgets.QApplication(sys.argv)

# This is substantially faster than using a signal... for some reason Qt
# signal dispatch is really slow (and relies on events underneath anyway, so
# this is strictly less work)
REENTER_EVENT = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())

class ReenterEvent(QtCore.QEvent):
    pass

class Reenter(QtCore.QObject):
    def event(self, event):
        event.fn()
        return False

reenter = Reenter()

def run_sync_soon_threadsafe(fn):
    event = ReenterEvent(REENTER_EVENT)
    event.fn = fn
    app.postEvent(reenter, event)

def done_callback(outcome):
    print(f"Outcome: {outcome}")
    if isinstance(outcome, Error):
        exc = outcome.error
        traceback.print_exception(type(exc), exc, exc.__traceback__)
    app.quit()

trio.lowlevel.start_guest_run(
    mine,
    run_sync_soon_threadsafe=run_sync_soon_threadsafe,
    done_callback=done_callback,
)

app.exec_()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions