Skip to content

Commit

Permalink
Transport API (#963)
Browse files Browse the repository at this point in the history
* Deprecate Client arg 'dispatch' and use 'transport'

* Remove line in test from coverage

* Document custom transports

* _dispatch > _transports

Also rename *Dispatch classes to *Transport and added aliases

* Fix linting issues

* Missed one _transports import

* Promote URLLib3Transport to public API

* Remove duplicate arg doc

* Assert that urllib3 is imported to use URLLib3Transport

* `AsyncClient`, not asynchronous `Client`

* Add warning category to warn calls

* Update docs/advanced.md

Co-authored-by: Florimond Manca <florimond.manca@gmail.com>

* Add warn_deprecated utility function

* Amend docs references to dispatch

* Add concrete implementation example

* Clearer transport implementation description

Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
  • Loading branch information
Yeray Diaz Diaz and florimondmanca authored May 21, 2020
1 parent ba073c8 commit d2816c9
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 70 deletions.
59 changes: 56 additions & 3 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ with httpx.Client(app=app, base_url="http://testserver") as client:
assert r.text == "Hello World!"
```

For some more complex cases you might need to customize the WSGI dispatch. This allows you to:
For some more complex cases you might need to customize the WSGI transport. This allows you to:

* Inspect 500 error responses rather than raise exceptions by setting `raise_app_exceptions=False`.
* Mount the WSGI application at a subpath by setting `script_name` (WSGI).
Expand All @@ -183,8 +183,8 @@ For example:

```python
# Instantiate a client that makes WSGI requests with a client IP of "1.2.3.4".
dispatch = httpx.WSGIDispatch(app=app, remote_addr="1.2.3.4")
with httpx.Client(dispatch=dispatch, base_url="http://testserver") as client:
transport = httpx.WSGITransport(app=app, remote_addr="1.2.3.4")
with httpx.Client(transport=transport, base_url="http://testserver") as client:
...
```

Expand Down Expand Up @@ -619,3 +619,56 @@ If you do need to make HTTPS connections to a local server, for example to test
>>> r
Response <200 OK>
```

## Custom Transports

HTTPX's `Client` also accepts a `transport` argument. This argument allows you
to provide a custom Transport object that will be used to perform the actual
sending of the requests.

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`.

For example, HTTPX ships with a transport that uses the excellent
[`urllib3` library](https://urllib3.readthedocs.io/en/latest/):

```python
>>> import httpx
>>> client = httpx.Client(transport=httpx.URLLib3Transport())
>>> client.get("https://example.org")
<Response [200 OK]>
```

A complete example of a transport implementation would be:

```python
import json

import httpcore
import httpx


class JSONEchoTransport(httpcore.SyncHTTPTransport):
"""
A mock transport that returns a JSON response containing the request body.
"""

def request(self, method, url, headers=None, stream=None, timeout=None):
body = b"".join(stream).decode("utf-8")
content = json.dumps({"body": body}).encode("utf-8")
stream = httpcore.SyncByteStream([content])
headers = [(b"content-type", b"application/json")]
return b"HTTP/1.1", 200, b"OK", headers, stream
```

Which we can use in the same way:

```python
>>> client = httpx.Client(transport=JSONEchoTransport())
>>> response = client.post("https://httpbin.org/post", data="Hello, world!")
>>> response.json()
{'body': 'Hello, world!'}
```
6 changes: 3 additions & 3 deletions docs/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ We can make requests directly against the application, like so:
... assert r.text == "Hello World!"
```

For some more complex cases you might need to customise the ASGI dispatch. This allows you to:
For some more complex cases you might need to customise the ASGI transport. This allows you to:

* Inspect 500 error responses rather than raise exceptions by setting `raise_app_exceptions=False`.
* Mount the ASGI application at a subpath by setting `root_path`.
Expand All @@ -176,8 +176,8 @@ For example:
```python
# Instantiate a client that makes ASGI requests with a client IP of "1.2.3.4",
# on port 123.
dispatch = httpx.ASGIDispatch(app=app, client=("1.2.3.4", 123))
async with httpx.AsyncClient(dispatch=dispatch, base_url="http://testserver") as client:
transport = httpx.ASGITransport(app=app, client=("1.2.3.4", 123))
async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
...
```

Expand Down
8 changes: 6 additions & 2 deletions httpx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from ._auth import Auth, BasicAuth, DigestAuth
from ._client import AsyncClient, Client
from ._config import PoolLimits, Proxy, Timeout
from ._dispatch.asgi import ASGIDispatch
from ._dispatch.wsgi import WSGIDispatch
from ._exceptions import (
ConnectTimeout,
CookieConflict,
Expand All @@ -27,6 +25,9 @@
)
from ._models import URL, Cookies, Headers, QueryParams, Request, Response
from ._status_codes import StatusCode, codes
from ._transports.asgi import ASGIDispatch, ASGITransport
from ._transports.urllib3 import URLLib3Transport
from ._transports.wsgi import WSGIDispatch, WSGITransport

__all__ = [
"__description__",
Expand All @@ -44,6 +45,7 @@
"stream",
"codes",
"ASGIDispatch",
"ASGITransport",
"AsyncClient",
"Auth",
"BasicAuth",
Expand Down Expand Up @@ -71,6 +73,7 @@
"TooManyRedirects",
"WriteTimeout",
"URL",
"URLLib3Transport",
"StatusCode",
"Cookies",
"Headers",
Expand All @@ -79,4 +82,5 @@
"Response",
"DigestAuth",
"WSGIDispatch",
"WSGITransport",
]
Loading

0 comments on commit d2816c9

Please sign in to comment.