Skip to content
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

Merged
merged 26 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
970bdd5
Added httpx.BaseTransport and httpx.AsyncBaseTransport
tomchristie Mar 17, 2021
38fd805
Test coverage and default transports to calling .close on __exit__
tomchristie Mar 18, 2021
9d413e7
BaseTransport documentation
tomchristie Mar 18, 2021
c8b5939
Merge branch 'master' into basetransport
tomchristie Mar 22, 2021
09e17eb
Use 'handle_request' for the transport API.
tomchristie Mar 22, 2021
ad36aef
Docs tweaks
tomchristie Mar 22, 2021
9b01ff5
Docs tweaks
tomchristie Mar 22, 2021
2c1c3aa
Minor docstring tweak
tomchristie Mar 22, 2021
1edc9c3
Transport API docs
tomchristie Mar 22, 2021
0a6220a
Drop 'Optional' on Transport API
tomchristie Mar 22, 2021
5b48a5b
Docs tweaks
tomchristie Mar 22, 2021
072a28b
Tweak CHANGELOG
tomchristie Mar 22, 2021
e94beae
Drop erronous example.py
tomchristie Mar 22, 2021
ee2a612
Push httpcore exception wrapping out of client into transport (#1524)
tomchristie Mar 23, 2021
4b4769a
Extensions reason_phrase and http_version as bytes (#1526)
tomchristie Mar 23, 2021
41ee5e7
Neaten up our try...except structure for ensuring responses (#1525)
tomchristie Mar 23, 2021
b8883cb
Fix CHANGELOG typo
tomchristie Mar 24, 2021
d3dc4d0
Fix CHANGELOG typo
tomchristie Mar 24, 2021
51cdf17
stream: Iterator[bytes] -> stream: Iterable[bytes]
tomchristie Mar 24, 2021
d7b2f3b
Use proper bytestream interfaces when calling into httpcore
tomchristie Mar 24, 2021
e106ad0
Grungy typing workaround due to httpcore using Iterator instead of It…
tomchristie Mar 24, 2021
1de069b
Merge branch 'master' into transport-api
florimondmanca Mar 24, 2021
7bcd7fd
Update docs/advanced.md
tomchristie Mar 24, 2021
7b8760b
Consistent typing imports across tranports
tomchristie Mar 24, 2021
ead250d
Merge branch 'transport-api' of https://github.com/encode/httpx into …
tomchristie Mar 24, 2021
f949198
Update docs/advanced.md
tomchristie Mar 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 19 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## 0.17.1
## Master

The 0.18.x release series formalises our low-level Transport API, introducing the
base classes `httpx.BaseTransport` and `httpx.AsyncBaseTransport`.

See the "Writing custom transports" documentation and the `httpx.BaseTransport.handle_request()`
docstring for more complete details on implementing custom transports.

Pull request #1522 includes a checklist of differences from the previous `httpcore` transport API,
for developers implementing custom transports.

### Changed

* Transport instances now inherit from `httpx.BaseTransport` or `httpx.AsyncBaseTransport`,
and should implement either the `handle_request` method or `handle_async_request` method.
* The `response.ext` property and `Response(ext=...)` argument are now named `extensions`.

## 0.17.1 (March 15th, 2021)

### Fixed

* Type annotation on `CertTypes` allows `keyfile` and `password` to be optional. (Pull #1503)
* Fix httpcore pinned version. (Pull #1495)

## 0.17.0
## 0.17.0 (February 28th, 2021)

### Added

Expand Down
41 changes: 24 additions & 17 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -1015,31 +1015,39 @@ This [public gist](https://gist.github.com/florimondmanca/d56764d78d748eb9f73165

### Writing custom transports

A transport instance must implement the Transport API defined by
[`httpcore`](https://www.encode.io/httpcore/api/). You
should either subclass `httpcore.AsyncHTTPTransport` to implement a transport to
use with `AsyncClient`, or subclass `httpcore.SyncHTTPTransport` to implement a
transport to use with `Client`.
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 with `Client`,
or subclass `httpx.AsyncBaseTransport` to implement a transport to
use with `AsyncClient`.

At the layer of the transport API we're simply using plain primitives.
tomchristie marked this conversation as resolved.
Show resolved Hide resolved
No `Request` or `Response` models, no fancy `URL` or `Header` 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` and `handle_async_request` docstrings for more details
tomchristie marked this conversation as resolved.
Show resolved Hide resolved
on the specifics of the Transport API.

A complete example of a custom transport implementation would be:

```python
import json
import httpcore
import httpx


class HelloWorldTransport(httpcore.SyncHTTPTransport):
class HelloWorldTransport(httpx.BaseTransport):
"""
A mock transport that always returns a JSON "Hello, world!" response.
"""

def request(self, method, url, headers=None, stream=None, ext=None):
def handle_request(self, method, url, headers, stream, extensions):
message = {"text": "Hello, world!"}
content = json.dumps(message).encode("utf-8")
stream = httpcore.PlainByteStream(content)
stream = [content]
headers = [(b"content-type", b"application/json")]
ext = {"http_version": b"HTTP/1.1"}
return 200, headers, stream, ext
extensions = {}
return 200, headers, stream, extensions
```

Which we can use in the same way:
Expand Down Expand Up @@ -1084,24 +1092,23 @@ which transport an outgoing request should be routed via, with [the same style
used for specifying proxy routing](#routing).

```python
import httpcore
import httpx

class HTTPSRedirectTransport(httpcore.SyncHTTPTransport):
class HTTPSRedirectTransport(httpx.BaseTransport):
"""
A transport that always redirects to HTTPS.
"""

def request(self, method, url, headers=None, stream=None, ext=None):
def handle_request(self, method, url, headers, stream, extensions):
scheme, host, port, path = url
if port is None:
location = b"https://%s%s" % (host, path)
else:
location = b"https://%s:%d%s" % (host, port, path)
stream = httpcore.PlainByteStream(b"")
stream = [b""]
headers = [(b"location", location)]
ext = {"http_version": b"HTTP/1.1"}
return 303, headers, stream, ext
extensions = {}
return 303, headers, stream, extensions


# A client where any `http` requests are always redirected to `https`
Expand Down
3 changes: 3 additions & 0 deletions httpx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from ._models import URL, Cookies, Headers, QueryParams, Request, Response
from ._status_codes import StatusCode, codes
from ._transports.asgi import ASGITransport
from ._transports.base import AsyncBaseTransport, BaseTransport
from ._transports.default import AsyncHTTPTransport, HTTPTransport
from ._transports.mock import MockTransport
from ._transports.wsgi import WSGITransport
Expand All @@ -45,9 +46,11 @@
"__title__",
"__version__",
"ASGITransport",
"AsyncBaseTransport",
"AsyncClient",
"AsyncHTTPTransport",
"Auth",
"BaseTransport",
"BasicAuth",
"Client",
"CloseError",
Expand Down