-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is an initial solution for #120. Allow spawning `asyncio` based actors which run `trio` in guest mode. This enables spawning `tractor` actors on top of the `asyncio` event loop whilst still leveraging the SC focused internal actor supervision machinery. Add a `tractor.to_syncio.run()` api to allow spawning tasks on the `asyncio` loop from an embedded (remote) `trio` task and return or stream results all the way back through the `tractor` IPC system using a very similar api to portals. One outstanding problem is getting SC around calls to `asyncio.create_task()`. Currently a task that crashes isn't able to easily relay the error to the embedded `trio` task without us fully enforcing the portals based message protocol (which seems superfluous given the error ref is in process). Further experiments using `anyio` task groups may alleviate this.
- Loading branch information
Showing
5 changed files
with
160 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
""" | ||
Infection apis for ``asyncio`` loops running ``trio`` using guest mode. | ||
""" | ||
import asyncio | ||
import inspect | ||
from typing import ( | ||
Any, | ||
Callable, | ||
AsyncGenerator, | ||
Awaitable, | ||
Union, | ||
) | ||
|
||
import trio | ||
|
||
|
||
async def _invoke( | ||
from_trio, | ||
to_trio, | ||
coro | ||
) -> Union[AsyncGenerator, Awaitable]: | ||
"""Await or stream awaiable object based on type into | ||
``trio`` memory channel. | ||
""" | ||
async def stream_from_gen(c): | ||
async for item in c: | ||
to_trio.put_nowait(item) | ||
to_trio.put_nowait | ||
|
||
async def just_return(c): | ||
to_trio.put_nowait(await c) | ||
|
||
if inspect.isasyncgen(coro): | ||
return await stream_from_gen(coro) | ||
elif inspect.iscoroutine(coro): | ||
return await coro | ||
|
||
|
||
# TODO: make this some kind of tractor.to_asyncio.run() | ||
async def run( | ||
func: Callable, | ||
qsize: int = 2**10, | ||
**kwargs, | ||
) -> Any: | ||
"""Run an ``asyncio`` async function or generator in a task, return | ||
or stream the result back to ``trio``. | ||
""" | ||
# ITC (inter task comms) | ||
from_trio = asyncio.Queue(qsize) | ||
to_trio, from_aio = trio.open_memory_channel(qsize) | ||
|
||
# allow target func to accept/stream results manually | ||
kwargs['to_trio'] = to_trio | ||
kwargs['from_trio'] = to_trio | ||
|
||
coro = func(**kwargs) | ||
|
||
# start the asyncio task we submitted from trio | ||
# TODO: try out ``anyio`` asyncio based tg here | ||
asyncio.create_task(_invoke(from_trio, to_trio, coro)) | ||
|
||
# determine return type async func vs. gen | ||
if inspect.isasyncgen(coro): | ||
await from_aio.get() | ||
elif inspect.iscoroutine(coro): | ||
async def gen(): | ||
async for tick in from_aio: | ||
yield tuple(tick) | ||
|
||
return gen() |