On Fri, Jun 3, 2016 at 8:46 PM, Łukasz Langa firstname.lastname@example.org wrote:
Amber, Glyph, Ben, is asyncio.Future’s event loop callback scheduling the biggest barrier in interoperability at the moment?
For Tornado, this was not an issue. Things are perhaps not as efficient as they could be (and this is why tornado.concurrent.Future cannot be an alias for asyncio.Future), but it basically just works.
The biggest problem that arises when people use Tornado and asyncio together is that some asyncio functions (such as asyncio.gather()) are written to accept bare coroutines and run them in asyncio.Task. This works in a pure asyncio world, but fails in an application that tries to mix the two frameworks. Since Tornado's coroutine runner is essentially a superset of asyncio's, the workaround is simple: use the Tornado version of any functions that exhibit this problem (e.g. tornado.gen.multi() instead of asyncio.gather()). And while I'm happy to promote Tornado's more flexible coroutine runner over asyncio's, it's clearly problematic for standardization and interop. This approach only works so long as there is some coroutine runner that is a superset of all the others.
The root of the problem is that without a standardized coroutine runner, async functions are difficult to use: each asynchronous function embeds assumptions about what runner will be used, and these assumptions will need to be documented along with instructions for what to do when you need to cross runner boundaries. We kind of have a standardized coroutine runner in async.Task, but it's inflexible, dealing only in asyncio.Futures. I think the right thing to do is to give asyncio a functools.singledispatch-based hook like the one in Tornado (tornado.gen.convert_yielded) for converting yielded objects into Futures. If we could register Tornado's Futures with asyncio then I don't think there would be a reason to prefer Tornado's coroutine runner when asyncio is available.