You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
2.4 KiB
Python

"""A trio loop runner."""
import builtins
import logging
import signal
import threading
import traceback
import warnings
import trio
class TrioRunner:
"""A trio loop runner."""
def __init__(self):
"""Initialize the runner."""
self._cell_cancel_scope = None
self._trio_token = None
def initialize(self, kernel, io_loop):
"""Initialize the runner."""
kernel.shell.set_trio_runner(self)
kernel.shell.run_line_magic("autoawait", "trio")
kernel.shell.magics_manager.magics["line"]["autoawait"] = lambda _: warnings.warn(
"Autoawait isn't allowed in Trio background loop mode.", stacklevel=2
)
self._interrupted = False
bg_thread = threading.Thread(target=io_loop.start, daemon=True, name="TornadoBackground")
bg_thread.start()
def interrupt(self, signum, frame):
"""Interrupt the runner."""
if self._cell_cancel_scope:
self._cell_cancel_scope.cancel()
else:
msg = "Kernel interrupted but no cell is running"
raise Exception(msg)
def run(self):
"""Run the loop."""
old_sig = signal.signal(signal.SIGINT, self.interrupt)
def log_nursery_exc(exc):
exc = "\n".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
logging.error("An exception occurred in a global nursery task.\n%s", exc)
async def trio_main():
"""Run the main loop."""
self._trio_token = trio.lowlevel.current_trio_token()
async with trio.open_nursery() as nursery:
# TODO This hack prevents the nursery from cancelling all child
# tasks when an uncaught exception occurs, but it's ugly.
nursery._add_exc = log_nursery_exc
builtins.GLOBAL_NURSERY = nursery # type:ignore[attr-defined]
await trio.sleep_forever()
trio.run(trio_main)
signal.signal(signal.SIGINT, old_sig)
def __call__(self, async_fn):
"""Handle a function call."""
async def loc(coro):
"""A thread runner context."""
self._cell_cancel_scope = trio.CancelScope()
with self._cell_cancel_scope:
return await coro
self._cell_cancel_scope = None # type:ignore[unreachable]
return None
return trio.from_thread.run(loc, async_fn, trio_token=self._trio_token)