python-trio / trio-asyncio

a re-implementation of the asyncio mainloop on top of Trio

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Explore interposition

smurfix opened this issue · comments

Interposition code, thanks to @Fuyukai and slightly embellished:

import types
@types.coroutine
def run_with_shim(fn, *args):
    """
    ;)
    """
    coro = fn(*args)
    # start the coroutine
    yielded = coro.send(None)
    while True:
        try:
            if isinstance(yielded, asyncio.Future):
                next_send = yield from trio_asyncio.run_future(yielded)
            else:
                next_send = yield yielded
        except BaseException as exc:
            p,a = coro.throw,exc
        else:
            p,a = coro.send,next_send
        try:
            yielded = p(a)
        except StopIteration as e:
            return e.value

Benchmarking shows an additional 10% slow-down for asyncio and no measurable change for trio, which is OK with me.

#!/usr/bin/python3
N=5000; D=0.001
async def chk(s, sleep):
    t1 = time()
    for x in range(N):
        await sleep(D)
    s = " "*(12-len(s))+s
    print("%12s %.6f" % (s,((time()-t1)/N-D)*1000))

import trio
from time import time
trio.run(chk, "trio", trio.sleep)

import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(chk("asyncio",asyncio.sleep))

async def inner(p,*a):
    await p(*a)
import trio_asyncio
trio_asyncio.run(chk, "ta:trio", trio.sleep)
trio_asyncio.run(inner, run_with_shim, chk, "ta:s:trio", trio.sleep)
trio_asyncio.run(trio_asyncio.run_asyncio, chk, "ta:asyncio", asyncio.sleep)
trio_asyncio.run(inner, run_with_shim, chk, "ta:s:asyncio", asyncio.sleep)

TODO: incorporate that into trio-asyncio and check whether the tests still work, esp. those related to cancellation semantics.

… they don't, of course. This may or may not actually affect real-world code, so I'll include it, but it obviously can't be the default.

The tests probably don't pass because the shim doesn't do things like set current_task etc which some libs rely on.

I also never tested it on anything more complex than a very basic sleep.

@Fuyukai It doesn't need to, run_future already does that (by delegating execution to asyncio).

Lots of tests pass, and one doesn't need to keep track all those fiddly wrappers …

When I tried aiohttp, it was complaining that there was no task context or something.

Yeah, aiohttp is demanding about its perceived lack of task context. I've hit that problem last month (i.e. without your wrapper ;-) but no time yet to investigate further – besides, I'd rather spend time working on a real http solution for trio anyway.

I believe that aiohttp depends on being able to do current_task().cancel() to implement timeouts (via the async-timeout package).

(and obviously this doesn't work if we're actually in a trio task and haven't switched into an asyncio task.)

Simple version added. Complicated cases still require distinct modes.