Closed belm0 closed 6 years ago
Something like this should probably either be documented or part of trio_asyncio.
import aiohttp
import trio_asyncio
import asyncio
from async_generator import asynccontextmanager
@asynccontextmanager
async def run_asynccontext(proc):
r = await trio_asyncio.run_asyncio(proc.__aenter__)
try:
yield r
except BaseException as exc:
if not await trio_asyncio.run_asyncio(type(exc), exc, exc.__traceback__):
raise
else:
await trio_asyncio.run_asyncio(proc.__aexit__, None, None, None)
async def t():
async with run_asynccontext(aiohttp.ClientSession()) as c:
async with run_asynccontext(c.ws_connect('wss://echo.websocket.org')) as ws:
await trio_asyncio.run_asyncio(ws.send_str,"Hello")
r = await trio_asyncio.run_asyncio(ws.receive)
print(r)
trio_asyncio.run(t)
Thank you, I'd been fighting with trying to implement that myself.
I wonder if it's possible to make run_asyncio()
support both cases. My attempt at it has failed.
Possible, if you convert run_asyncio
from an async function to something that returns an object with an __await__
method (and, for context management, __aenter__
and __aexit__
).
I'll check whether that works, because if it does we can dispense with all those different wrappers and just use run_asyncio
(or run_trio
) for everything, which would simplify usage a lot.
Works. The next version will have a single run_asyncio
method that can be used for plain function calls, iterators/generators, and context managers. I'm investigating whether run_trio
can safely be convinced to behave the same way, but the interplay with asyncio.Future
might prevent that.
Maybe making aio2trio
and trio2aio
magic might be nicer? Like:
class aio2trio:
def __init__(self, thing):
self._thing = thing
async def __call__(self, *args, **kwargs):
return await run_asyncio(partial(self._thing, *args, **kwargs))
async def __aenter__(self):
return await run_asyncio(self._thing.__aenter__)
async def __aexit__(self, exc_value, exc_type, exc_tb):
return await run_asyncio(self._thing.__aexit__, exc_value, exc_type, exc_tb)
async def __aiter__(self):
aio_ait = self._thing.__aiter__()
try:
while True:
yield await run_asyncio(aio_ait.__anext__)
except StopAsyncIteration:
pass
@aio2trio
async def ...
async with aio2trio(aio_cm):
...
async for ... in aio2trio(aio_async_iterable):
...
Hmm. Possibly. Though first I'll check how much @Fuyukai 's magic shim works out, because frankly all those converters and impositors are scary.
For reference, I believe the "magic shim" is this: https://gitter.im/python-trio/general?at=5b7011345ec2bc174fea1137
IIRC the reason we didn't go with a magic shim in the first place is that it doesn't give us a place to convert back and forth between trio and asyncio's cancellation semantics, so asyncio-mode code sees asyncio.CancelledError
and trio-mode code sees trio.Cancelled
?
Hmm, run_asyncio
is still called when fed with a future, so this might actually work. Will test.
The magic is included in 0.8.2 but needs to be refactored. See https://github.com/python-trio/trio-asyncio/issues/36
I'd like to have the equivalent of
async with aiohttp.ClientSession().ws_connect('wss://echo.websocket.org') as ws
wrapped in run_asyncio(). However it yieldsAttributeError: __aexit__
. Is it merely a matter of propagating__aexit__
?