In a trio, how can I get a background task that lives as long as my object does?

I am writing a class that will run tasks throughout its life. Since I use Trio , I cannot run tasks without a nursery. My first thought was to have self._nurseryin my class so that I could run tasks. But it seems that kindergarten objects can only be used in the context manager, so they are always closed in the same area where they were created. I don’t want to go to the nursery from the outside, because this is a detail of the implementation, but I want my objects to be able to create tasks that continue until the object (for example, a task with a heartbeat).

How can I write a class that has long-lived background tasks using Trio?

+6
source share
1 answer

Great question!

One of Trio’s strangest, most controversial decisions is that it believes that the existence of a background task is not an implementation detail and should be presented as part of your API. In general, I think this is the right decision, but it is definitely a bit experimental and has some tradeoffs.

? , , , : C- , re , , , , , , , , , , ... , , API , , .

, , , Trio, , API : -).

, . , -, , ( "ping" ). - - :

@asynccontextmanager
def open_websocket(url):
    ws = WebSocket()
    await ws._connect(url)
    try:
        async with trio.open_nursery() as nursery:
            nursery.start_soon(ws._heartbeat_task)
            yield ws
            # Cancel the heartbeat task, since we're about to close the connection
            nursery.cancel_scope.cancel()
    finally:
        await ws.aclose()

:

async with open_websocket("https://...") as ws:
    await ws.send("hello")
    ...

fancier, - , , :

class WebSocket(trio.abc.AsyncResource):
    def __init__(self, nursery, url):
        self._nursery = nursery
        self.url = url

    async def connect(self):
        # set up the connection
        ...
        # start the heartbeat task
        self._nursery.start_soon(self._heartbeat_task)

    async def aclose(self):
        # you'll need some way to shut down the heartbeat task here
        ...

API, , :

@asynccontextmanager
async def open_websocket(url):
    async with trio.open_nursery() as nursery:
        async with WebSocket(nursery, url) as ws:
            await ws.connect()
            yield ws

pass-in-a-nursery , , , - , -.

, , , : @asynccontextmanager? , stdlib 3.7, , , , , , . async_generator @asynccontextmanager 3.5.

+6

All Articles