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
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):
...
self._nursery.start_soon(self._heartbeat_task)
async def aclose(self):
...
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.