Uh oh!
There was an error while loading. Please reload this page.
Add asyncio backend - #930
Conversation
0d3a1f4 to
db58896Compare| return backend_name, options | ||
| class Server(uvicorn.Server): |
There was a problem hiding this comment.
Seems the httpbin fixture doesnt work well for pool related testing as it always closes the connections immeaditely... So adding this for testing those aspects in integration testing file
009249a to
56447f4CompareIn order to land this PR I'd suggest adding an The default backend should not change as part of the pull request, and the existing The documentation for https://www.encode.io/httpcore/network-backends/#async-network-backends should also be modified to include the native network_backend=httpcore.AsyncIOBackend()
asyncwithhttpcore.AsyncConnectionPool(network_backend=network_backend) ashttp:
...Similar to the existing (Minimal incremental changes ftw) |
56447f4 to
81b6b1fCompareMarkusSintonen
commented
Jun 17, 2024
Yep that I didnt change as you previously suggested |
MarkusSintonen
commented
Jun 17, 2024
This is now done, so there is only the new backend and export. + The tests I used add the full coverage and verify it works |
MarkusSintonen
commented
Jun 17, 2024
Added this |
MarkusSintonen
commented
Jun 18, 2024
lovelydinosaur
commented
Jun 18, 2024
The code & docs changes look reasonable. |
9f13ff5 to
3814cf4Compare
Previously the With this and the added integration testing (eg UDS, socket options) we get close to 100% coverage for the new network backend. (Actually we get even more coverage for anyio/trio backends so there is some no longer needed |
Uh oh!
There was an error while loading. Please reload this page.
lovelydinosaur
left a comment
There was a problem hiding this comment.
Looks great, thanks so much.
Naming... should we use AsyncIOStream and AsyncIOBackend or AsyncioStream and AsyncioBackend?
bdraco
commented
Sep 29, 2024
Thanks for doing this. I think this will close out the discussion I opened #893 |
MarkusSintonen
commented
Oct 1, 2024
No problem! Its now renamed to |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
lovelydinosaur
commented
Oct 1, 2024
Fantastic, appreciate your time on this @MarkusSintonen. We could either get #957 released first then merge this, or include this now and bump the version. |
MarkusSintonen
commented
Oct 1, 2024
@tomchristie no problem! Would it be possible to include also the asyncio-based synchronization changes as part of this? |
| self._read_lock = asyncio.Lock() | ||
| self._write_lock = asyncio.Lock() |
There was a problem hiding this comment.
We don't use locking at the stream layer, let's remove these.
(I think we had locking at this layer in the past, the locking is now handled at the HTTP11Connection/HTTP2Connection layer)
There was a problem hiding this comment.
Done, they are now gone
| self._read_lock = asyncio.Lock() | ||
| self._write_lock = asyncio.Lock() |
There was a problem hiding this comment.
| self._read_lock=asyncio.Lock() | |
| self._write_lock=asyncio.Lock() |
| async with self._read_lock: | ||
| with map_exceptions(exc_map): | ||
| try: | ||
| return await asyncio.wait_for( | ||
| self._stream_reader.read(max_bytes), timeout | ||
| ) | ||
| except AttributeError as exc: # pragma: nocover | ||
| if "resume_reading" in str(exc): | ||
| # Python's asyncio has a bug that can occur when a | ||
| # connection has been closed, while it is paused. | ||
| # See: https://github.com/encode/httpx/issues/1213 | ||
| # | ||
| # Returning an empty byte-string to indicate connection | ||
| # close will eventually raise an httpcore.RemoteProtocolError | ||
| # to the user when this goes through our HTTP parsing layer. | ||
| return b"" | ||
| raise |
There was a problem hiding this comment.
| asyncwithself._read_lock: | |
| withmap_exceptions(exc_map): | |
| try: | |
| returnawaitasyncio.wait_for( | |
| self._stream_reader.read(max_bytes), timeout | |
| ) | |
| exceptAttributeErrorasexc: # pragma: nocover | |
| if"resume_reading"instr(exc): | |
| # Python's asyncio has a bug that can occur when a | |
| # connection has been closed, while it is paused. | |
| # See: https://github.com/encode/httpx/issues/1213 | |
| # | |
| # Returning an empty byte-string to indicate connection | |
| # close will eventually raise an httpcore.RemoteProtocolError | |
| # to the user when this goes through our HTTP parsing layer. | |
| returnb"" | |
| raise | |
| withmap_exceptions(exc_map): | |
| try: | |
| returnawaitasyncio.wait_for( | |
| self._stream_reader.read(max_bytes), timeout | |
| ) | |
| exceptAttributeErrorasexc: # pragma: nocover | |
| if"resume_reading"instr(exc): | |
| # Python's asyncio has a bug that can occur when a | |
| # connection has been closed, while it is paused. | |
| # See: https://github.com/encode/httpx/issues/1213 | |
| # | |
| # Returning an empty byte-string to indicate connection | |
| # close will eventually raise an httpcore.RemoteProtocolError | |
| # to the user when this goes through our HTTP parsing layer. | |
| returnb"" | |
| raise |
| async with self._write_lock: | ||
| with map_exceptions(exc_map): | ||
| self._stream_writer.write(data) | ||
| return await asyncio.wait_for(self._stream_writer.drain(), timeout) |
There was a problem hiding this comment.
| asyncwithself._write_lock: | |
| withmap_exceptions(exc_map): | |
| self._stream_writer.write(data) | |
| returnawaitasyncio.wait_for(self._stream_writer.drain(), timeout) | |
| withmap_exceptions(exc_map): | |
| self._stream_writer.write(data) | |
| returnawaitasyncio.wait_for(self._stream_writer.drain(), timeout) |
Uh oh!
There was an error while loading. Please reload this page.
Thanks for the ask, let's not do that. Mixing too many different concerns. (Minimal incremental changes ftw) |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |


Summary
Adds back native
asynciobackend based on commit where it was removed here. Adjusts it to work with the new interfaces. This also adds some additional integration testing.Doesn't make the new backend the default.
AnyIO has considerable overhead so this allows again using the native backend. Also this is going to allow removal of the AnyIO as the dependency (when the synchronization PR goes in also).
Overhead is about 1.45x here (the synchronization has a lot more overhead in the other PR).
Previously (is master):

Now:

Related discussion https://github.com/encode/httpx/issues/3215#issuecomment-2155630018.
Checklist