New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Transport API #1522
Transport API #1522
Conversation
Looking real nice!
That bugged me too. Glad to see it. Also in favor of the follow-ups, all seem reasonable and beneficial. |
* Push httpcore exception wrapping out of client into transport * Include close/aclose extensions in docstring * Comment about the request property on RequestError exceptions
* Extensions reason_phrase and http_version as bytes * Update BaseTransport docstring
I think this one is ready to go now. @florimondmanca not sure what your thoughts on context managed interface at this layer are, but even if we do want to discuss that further I think we'd be way better off doing that with this as the starting point. The following core points all apply just as well regardless of if
|
@tomchristie Most of the points I can agree with, two questions:
|
There's not really any difference in The
|
Sounds good. :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very satisfying. 😄
Leaving some nits, and a somewhat important one about Iterator
vs Iterable
in the Transport API signature.
Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
Looks great! Will have a play trying to use this in httpx-caching and see what the diff looks like :) |
So am I right in thinking there is no longer a concept of |
So there's a close or aclose callback that may be present in the extensions... https://github.com/encode/httpx/blob/master/httpx/_transports/base.py#L89 For your codebase it looks to me like where you're currently using (Or something similar) |
Thanks @tomchristie! I just figured this out after I saw errors when trying to serialize |
So, that was fairly painless! https://github.com/johtso/httpx-caching/pull/4/files |
Now RESPX also has a PR lundberg/respx#142 respecting this new transport API! |
This change silently breaks users of existing httpcore transport users by appearing to 'work' fine. May be add a check like
|
This issue was fixed in httpx-socks v0.4.0 |
update httpx-socks to 0.4.0 to fix: encode/httpx#1522 (comment) remove not_evil which has been down for a while now: https://old.reddit.com/r/onions/search/?q=not+evil&restrict_sr=on&t=year
update httpx-socks to 0.4.0 to fix: encode/httpx#1522 (comment) remove not_evil which has been down for a while now: https://old.reddit.com/r/onions/search/?q=not+evil&restrict_sr=on&t=year
downgrade httpx: PR encode/httpx#1522 made some breaking changes in AsyncHTTPTransport that affect our code in https://github.com/searx/searx/blob/master/searx/network/client.py remove not_evil which has been down for a while now: https://old.reddit.com/r/onions/search/?q=not+evil&restrict_sr=on&t=year
downgrade httpx: PR encode/httpx#1522 made some breaking changes in AsyncHTTPTransport that affect our code in https://github.com/searx/searx/blob/master/searx/network/client.py remove not_evil which has been down for a while now: https://old.reddit.com/r/onions/search/?q=not+evil&restrict_sr=on&t=year
adjust searx.network module to the new internal API see encode/httpx#1522
adjust searx.network module to the new internal API see encode/httpx#1522
adjust searx.network module to the new internal API see encode/httpx#1522
adjust searx.network module to the new internal API see encode/httpx#1522
adjust searx.network module to the new internal API see encode/httpx#1522
adjust searx.network module to the new internal API see encode/httpx#1522
downgrade httpx: PR encode/httpx#1522 made some breaking changes in AsyncHTTPTransport that affect our code in https://github.com/searx/searx/blob/master/searx/network/client.py remove not_evil which has been down for a while now: https://old.reddit.com/r/onions/search/?q=not+evil&restrict_sr=on&t=year
We have a little niggle in our API. Almost all the way through
httpcore
is treated as an implementation detail, that just happens to be used by the default transport implementation,httpx.HTTPTransport
.The one place where this isn't the case is in our "Transport API". In that case we're explicitly documenting "subclass httpcore.SyncHTTPTransport and implement it's API".
To make this clearer, here's how the "custom transports" docs look after this change...
(Note I've updated this description to also incorporate the follow-up in #1550)
Writing custom transports
A transport instance must implement the low-level Transport API, which deals with sending a single request, and returning a response. You should either subclass
httpx.BaseTransport
to implement a transport to use withClient
, or subclasshttpx.AsyncBaseTransport
to implement a transport to use withAsyncClient
.At the layer of the transport API we're simply using plain primitives. No
Request
orResponse
models, no fancyURL
orHeader
handling. This strict point of cut-off provides a clear design separation between the HTTPX API, and the low-level network handling.See the
handle_request
andhandle_async_request
docstrings for more details on the specifics of the Transport API.A complete example of a custom transport implementation would be:
Which we can use in the same way:
Examples of calling into a transport instance directly
Standard case:
Streaming case:
Here's a checklist of differences between the previous interface, for developers of custom transports.
httpx.BaseTransport
orhttpx.AsyncBaseTransport
, not the previoushttpcore
classes.handle_request()
/handle_async_request()
, notrequest()
/arequest()
. I think it's far cleaner not to have a name clash vs.client.request()
.httpx.TransportError
hierarchy, instead ofhttpcore
exceptions. Note thathttpcore
->httpx
exception mapping is no longer provided by the client, but instead is handled more correctly within theHTTPTransport()
implementation.httpx.SyncByteStream
/httpx.AsyncByteStream
. For simple cases the concrete implementationhttpx.ByteStream()
provides for returning some fixed byte content, supporting both sync and async interfaces.reason_phrase
instead ofreason
as an extension key, because it then properly follows the HTTP spec naming.reason_phrase
andhttp_version
extensions are nowbytes
, instead ofstr
, for consistency with other elements in the interface, and because, when present in the response they are bytes on the wire. (Note that HTTP/2 onwards does not include version or reason information.)Follow-ups that we're likely to want, that we can treat as seperate PRs once this is in...
httpcore
0.13 version that also has an equivalent API update. Not strictly necessary, but it'd be neater.transport.send_request(method, url, ...)
that provides a nicehttpx
-level API onto transports, and returns fully fledgedResponse
instances. This would be implemented once on the base classes, and would allow users to test transport instances directly. Eg.r = t.send_request("GET", "https://www.example.com")
(Also note the namingsend_request()
/handle_request()
, makes sense together, and avoids clashes with any methods on the client)