Lower-level TaskGroup API #577
-
Say I have an async context manager import asyncio
from anyio import create_task_group
async def my_task():
await asyncio.Future()
class MyResource:
async def __aenter__(self):
async with create_task_group() as self.task_group:
self.task_group.start_soon(my_task)
async def __aexit__(self, exc_type, exc_val, exc_tb):
self.task_group.cancel_scope.cancel()
async def main():
async with MyResource():
pass
asyncio.run(main())
import asyncio
from anyio import create_task_group
from anyio._backends._asyncio import TaskGroup
async def my_task():
await asyncio.Future()
class MyResource:
async def __aenter__(self):
self.task_group = TaskGroup()
await self.task_group.__aenter__()
self.task_group.start_soon(my_task)
async def __aexit__(self, exc_type, exc_val, exc_tb):
self.task_group.cancel_scope.cancel()
await self.task_group.__aexit__(exc_type, exc_val, exc_tb)
async def main():
async with MyResource():
pass
asyncio.run(main()) But this is relying on a bunch of private methods. |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 1 reply
-
The standard pattern for this is: @contextlib.asynccontextmanager
async def open_my_object(...):
async with anyio.create_task_group() as nursery:
yield MyObject(nursery)
No, because then you could easily violate structured concurrency. The whole point of making it a context manager is to ban you from keeping tasks around after your object exits. |
Beta Was this translation helpful? Give feedback.
-
What private methods? It's totally kosher to call |
Beta Was this translation helpful? Give feedback.
-
It may be kosher in simple situations (this one qualifies), but unwrapping Given that it's fairly easy to code yourself into corners (e.g. you extend your |
Beta Was this translation helpful? Give feedback.
-
Pretty much the only reasonable way to implement a custom |
Beta Was this translation helpful? Give feedback.
-
Thanks for all the feedback, that's very helpful! |
Beta Was this translation helpful? Give feedback.
In nontrivial cases I just use an async exit stack:
What's nice about this approach is that if bad things happen in
__aenter__()
already, it still closes all the resources that have already been pu…