Trio support.
See original GitHub issueRevisiting some thoughts on this following on from #118
I’m not against having trio-support as one of the built-in options. Presumably that’d mean using --loop trio
, and having that switch out:
- The loop implementation.
- The server class implementation.
- Using a new trio h11 based protocol implementation for the default
--http auto
.
The asyncio
/trio
/curio
split is problematic, but it’s an ecosystem issue, rather than an ASGI issue (ASGI is just an async/await interface, and is agnostic what event loop implementation is used to call into that interface.)
The problem is that whatever event loop implementation you’re running in must also match any async primitives used in the application codebase.
If you’re just using async
/await
and send
/receive
, then it doesn’t matter, but as soon as you start using things like asyncio.sleep()/trio.sleep()
, or creating concurrent tasks, or accessing network or file I/O directly, then you’re locked into whichever system you’re in.
I’d say that Trio is indisputably more refined and more properly constrained than asyncio
, but the ecosystem split is problematic.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:7
- Comments:23 (18 by maintainers)
I’ve done more investigation, and from what I found out it seems there may not be a safe, simple way to transition incrementally from the current state to an “async agnostic” state, at least without partial rewrites of lots of stuff.
For example, the “protocols” code (
h11
andhttptools
for HTTP/1.1,wsproto
for WebSocket) is very much entangled withasyncio
(mostly because of the use of the transport/protocol API, rather than the streams API).Additive rewrite?
So I’m exploring the space of what’s possible on a branch: https://github.com/encode/uvicorn/tree/fm/async-agnostic
My approach there is to rewrite parts of Uvicorn (basically everything that touches networking, plus the lifespan code) independently of the existing code paths.
The rewritten code uses an
AsyncBackend
API (approach very similar to what we’ve done in HTTPCore).The new code path can be taken by passing an
--async-library ...
flag. This selects the async backend on startup (and we can auto-detect it when inside the server code for our own convenience).Current status
I’m quite happy to say that I’ve got a lot working on that branch yet:
h11
+httptools
asyncio
+trio
+curio
🎉File structure (approximately):
The
_async_agnostic
directory lives withinuvicorn/
. When--async-library
is given, the mainrun()
function targets the new async-agnostic server’s.run()
, rather than the existing asyncio-only server. (That one is currently in_impl/asyncio.py
, we may want to revert that and move it to_server.py
, or similar.)I also have a
tests/async_agnostic
directory, with relevant tests copied over and adapted using the new internal async-agnostic APIs. I’m happy to say that all tests pass. ✅Is this madness?
Well, probably. 😃
Obviously we can’t ever downright replace what’s in Uvicorn right now with what I’m working on in that branch.
Also I’m aware the code I’m working on mixes two problems: async-agnosticity, and preparing for HTTP/2 support. It’s very possible that preparing HTTP/2 support could be done separately. (And an internal “handler API” could be a sensible way to go. It should make it much easier to add HTTP/2 support incrementally, by simply adding an HTTP/2 handler, implemented with
h2
for example.)But as for the async-agnostic side of things, there may be a way forward in the form of a “new implementation” that exists alongside the existing one, and that users are able to opt into (using
--async-library (asyncio|trio|curio)
).If we think this is an interesting path forward, we could go for something like this:
--async-library
deprecated.There are pain points, though:
But…
Happy to read thoughts about this! I keep working on my branch since I think it’s a valuable bunch of work anyway (I’ve already cumulatively spent a few days straight on it!), at least for figuring out what can be done and how.
(Also interesting to think about this wrt HTTPCore: I think we have a “someday” ambition to build a server-side minimal HTTP server implementation, compatible with sync/async, with support for various async libraries, various parsers, etc…)
@graingert Let’s remember from past discussions that we want to keep a fast-track code path for asyncio, as Uvicorn is also meant to be a flagship for a fast and lightweight async server. This means keeping the existing asyncio-optimized code, and so I don’t think we should make anyio required like we did in Starlette (where I think we could have done without anyio magic too for the sake of future maintenance, but I didn’t participate in that decision).
I had also laid out ways we could move forward without anyio for trio support. I’d understand the motivation for using that as a first step for trio support, but this is just a note that I hope it is clear that we really do want to keep the existing asyncio code in place.