diff --git a/README.md b/README.md
index b0e213760d..7623ef0c44 100644
--- a/README.md
+++ b/README.md
@@ -16,40 +16,24 @@
-HTTPX is an asynchronous HTTP client, that supports HTTP/2 and HTTP/1.1.
+HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.
-It can be used in high-performance async web frameworks, using either asyncio
-or trio, and is able to support making large numbers of requests concurrently.
-
-**Note**: *HTTPX should still be considered in alpha. We'd love early users and feedback,
-but would strongly recommend pinning your dependencies to the latest median
-release, so that you're able to properly review API changes between package
-updates. Currently you should be using `httpx==0.10.*`.*
-
-*In particular, the 0.8 release switched HTTPX into focusing exclusively on
-providing an async client, in order to move the project forward, and help
-us [change our approach to providing sync+async support][sync-support]. If
-you have been using the sync client, you may want to pin to `httpx==0.7.*`,
-and wait until our sync client is reintroduced.*
+**Note**: _HTTPX should be considered in beta. We believe we've got the public API to
+a stable point now, but would strongly recommend pinning your dependencies to the `0.11.*`
+release, so that you're able to properly review API changes between package updates.
+A 1.0 release is expected to be issued sometime on or before April 2020._
---
Let's get started...
-*The standard Python REPL does not allow top-level async statements.*
-
-*To run async examples directly you'll probably want to either use `ipython`,
-or use Python 3.8 with `python -m asyncio`.*
-
```python
>>> import httpx
->>> r = await httpx.get('https://www.example.org/')
+>>> r = httpx.get('https://www.example.org/')
>>> r
>>> r.status_code
200
->>> r.http_version
-'HTTP/1.1'
>>> r.headers['content-type']
'text/html; charset=UTF-8'
>>> r.text
@@ -61,11 +45,12 @@ or use Python 3.8 with `python -m asyncio`.*
HTTPX builds on the well-established usability of `requests`, and gives you:
* A requests-compatible API wherever possible.
-* HTTP/2 and HTTP/1.1 support.
-* Ability to [make requests directly to ASGI applications](https://www.encode.io/httpx/advanced/#calling-into-python-web-apps).
+* Standard synchronous interface, but with [async support if you need it](https://www.encode.io/httpx/async/).
+* HTTP/1.1 [and HTTP/2 support](https://www.encode.io/httpx/http2/).
+* Ability to make requests directly to [WSGI applications](https://www.encode.io/httpx/advanced/#calling-into-python-web-apps) or [ASGI applications](https://www.encode.io/httpx/async/#calling-into-python-web-apps).
* Strict timeouts everywhere.
* Fully type annotated.
-* 100% test coverage.
+* 99% test coverage.
Plus all the standard features of `requests`...
@@ -101,7 +86,7 @@ Project documentation is available at [www.encode.io/httpx/](https://www.encode.
For a run-through of all the basics, head over to the [QuickStart](https://www.encode.io/httpx/quickstart/).
-For more advanced topics, see the [Advanced Usage](https://www.encode.io/httpx/advanced/) section.
+For more advanced topics, see the [Advanced Usage](https://www.encode.io/httpx/advanced/) section, the [async support](https://www.encode.io/httpx/async/) section, or the [HTTP/2](https://www.encode.io/httpx/http2/) section.
The [Developer Interface](https://www.encode.io/httpx/api/) provides a comprehensive API reference.
@@ -113,8 +98,9 @@ If you want to contribute with HTTPX check out the [Contributing Guide](https://
The httpx project relies on these excellent libraries:
-* `h2` - HTTP/2 support.
+* `urllib3` - Sync client support.
* `h11` - HTTP/1.1 support.
+* `h2` - HTTP/2 support.
* `certifi` - SSL certificates.
* `chardet` - Fallback auto-detection for response encoding.
* `hstspreload` - determines whether IDNA-encoded host should be only accessed via HTTPS.
@@ -129,5 +115,3 @@ inspiration around the lower-level networking details.
— ⭐️ —
HTTPX is BSD licensed code. Designed & built in Brighton, England.
-
-[sync-support]: https://github.com/encode/httpx/issues/572
diff --git a/docs/advanced.md b/docs/advanced.md
index c22ff647a9..1d2e4ca0af 100644
--- a/docs/advanced.md
+++ b/docs/advanced.md
@@ -19,21 +19,21 @@ all outgoing requests.
The recommended way to use a `Client` is as a context manager. This will ensure that connections are properly cleaned up when leaving the `with` block:
```python
->>> async with httpx.AsyncClient() as client:
-... r = await client.get('https://example.com')
+>>> with httpx.Client() as client:
+... r = client.get('https://example.com')
...
>>> r
```
-Alternatively, you can explicitly close the connection pool without block-usage using `.aclose()`:
+Alternatively, you can explicitly close the connection pool without block-usage using `.close()`:
```python
->>> client = httpx.AsyncClient()
+>>> client = httpx.Client()
>>> try:
-... r = await client.get('https://example.com')
+... r = client.get('https://example.com')
... finally:
-... await client.aclose()
+... client.close()
...
>>> r
@@ -50,8 +50,8 @@ For example, to apply a set of custom headers on every request:
```python
>>> url = 'http://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
->>> async with httpx.AsyncClient(headers=headers) as client:
-... r = await client.get(url)
+>>> with httpx.Client(headers=headers) as client:
+... r = client.get(url)
...
>>> r.json()['headers']['User-Agent']
'my-app/0.0.1'
@@ -68,79 +68,68 @@ Additionally, `Client` accepts some parameters that aren't available at the requ
One particularly useful parameter is `base_url`, which allows you to define a base URL to prepend to all outgoing requests:
```python
->>> async with httpx.AsyncClient(base_url='http://httpbin.org') as client:
-... r = await client.get('/headers')
+>>> with httpx.Client(base_url='http://httpbin.org') as client:
+... r = client.get('/headers')
...
>>> r.request.url
URL('http://httpbin.org/headers')
```
-For a list of all available client-level parameters, see the [`AsyncClient` API reference](/api/#async-client).
+For a list of all available client-level parameters, see the [`Client` API reference](/api/#client).
## Calling into Python Web Apps
-You can configure an `httpx` client to call directly into a Python web
-application using the ASGI protocol.
+You can configure an `httpx` client to call directly into a Python web application using the WSGI protocol.
This is particularly useful for two main use-cases:
* Using `httpx` as a client inside test cases.
* Mocking out external services during tests or in dev/staging environments.
-Let's take this Starlette application as an example:
+Here's an example of integrating against a Flask application:
```python
-from starlette.applications import Starlette
-from starlette.responses import HTMLResponse
-from starlette.routing import Route
-
+from flask import Flask
+import httpx
-async def hello():
- return HTMLResponse("Hello World!")
+app = Flask(__name__)
-app = Starlette(routes=[Route("/", hello)])
-```
+@app.route("/")
+def hello():
+ return "Hello World!"
-We can make requests directly against the application, like so:
-
-```python
->>> import httpx
->>> async with httpx.AsyncClient(app=app) as client:
-... r = client.get('http://example/')
-... assert r.status_code == 200
-... assert r.text == "Hello World!"
+with httpx.Client(app=app) as client:
+ r = client.get('http://example/')
+ assert r.status_code == 200
+ 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 customize the WSGI dispatch. 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`.
-* Use a given client address for requests by setting `client`.
+* Mount the WSGI application at a subpath by setting `script_name` (WSGI).
+* Use a given client address for requests by setting `remote_addr` (WSGI).
For example:
```python
-# Instantiate a client that makes ASGI requests with a client IP of "1.2.3.4",
-# on port 123.
-dispatch = httpx.dispatch.ASGIDispatch(app=app, client=("1.2.3.4", 123))
-async with httpx.AsyncClient(dispatch=dispatch) as client:
+# 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) as client:
...
```
-See [the ASGI documentation](https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope) for more details on the `client` and `root_path`
-keys.
-
## Build Request
You can use `Client.build_request()` to build a request and
make modifications before sending the request.
```python
->>> async with httpx.AsyncClient() as client:
+>>> with httpx.Client() as client:
... req = client.build_request("OPTIONS", "https://example.com")
... req.url.full_path = "*" # Build an 'OPTIONS *' request for CORS
-... r = await client.send(req)
+... r = client.send(req)
...
>>> r
@@ -158,7 +147,7 @@ not defined, HTTPX tries to add auth into request's header from .netrc file.
As default `trust_env` is true. To set false:
```python
->>> await httpx.get('https://example.org/', trust_env=False)
+>>> httpx.get('https://example.org/', trust_env=False)
```
If `NETRC` environment is empty, HTTPX tries to use default files.
@@ -182,22 +171,7 @@ password example-password
When using `Client` instances, `trust_env` should be set on the client itself, rather that on the request methods:
```python
-client = httpx.AsyncClient(trust_env=False)
-```
-
-## Unix Domain Sockets
-
-You can configure an `httpx` client to connect through a unix domain socket via the `uds` parameter. This is useful when making requests to a server that is bound to a socket file rather than an IP address.
-
-Here's an example requesting the Docker Engine API:
-
-```python
-import httpx
-
-
-async with httpx.AsyncClient(uds="/var/run/docker.sock") as client:
- # This request will connect through the socket file.
- resp = await client.get("http://localhost/version")
+client = httpx.Client(trust_env=False)
```
## HTTP Proxying
@@ -211,7 +185,7 @@ to `http://127.0.0.1:3081` your `proxies` config would look like this:
... "http": "http://127.0.0.1:3080",
... "https": "http://127.0.0.1:3081"
... }
->>> async with httpx.AsyncClient(proxies=proxies) as client:
+>>> with httpx.Client(proxies=proxies) as client:
... ...
```
@@ -226,11 +200,11 @@ to use for a given request this same order is used.
... "http": "...", # Scheme
... "all": "...", # All
... }
->>> async with httpx.AsyncClient(proxies=proxies) as client:
+>>> with httpx.Client(proxies=proxies) as client:
... ...
...
>>> proxy = "..." # Shortcut for {'all': '...'}
->>> async with httpx.AsyncClient(proxies=proxy) as client:
+>>> with httpx.Client(proxies=proxy) as client:
... ...
```
@@ -239,27 +213,26 @@ to use for a given request this same order is used.
and even if the proxy_url uses HTTPS, it is recommended to
use HTTPS and tunnel requests if possible.
-By default `HTTPProxy` will operate as a forwarding proxy for `http://...` requests
+By default `httpx.Proxy` will operate as a forwarding proxy for `http://...` requests
and will establish a `CONNECT` TCP tunnel for `https://` requests. This doesn't change
-regardless of the `proxy_url` being `http` or `https`.
+regardless of the proxy `url` being `http` or `https`.
Proxies can be configured to have different behavior such as forwarding or tunneling all requests:
```python
-proxy = httpx.HTTPProxy(
- proxy_url="https://127.0.0.1",
- proxy_mode="TUNNEL_ONLY" # May be "TUNNEL_ONLY" or "FORWARD_ONLY". Defaults to "DEFAULT".
+proxy = httpx.Proxy(
+ url="https://127.0.0.1",
+ mode="TUNNEL_ONLY" # May be "TUNNEL_ONLY" or "FORWARD_ONLY". Defaults to "DEFAULT".
)
-async with httpx.AsyncClient(proxies=proxy) as client:
+with httpx.Client(proxies=proxy) as client:
# This request will be tunneled instead of forwarded.
- r = await client.get("http://example.com")
+ r = client.get("http://example.com")
```
!!! note
- Per request proxy configuration, i.e. `client.get(url, proxies=...)`,
- has not been implemented yet. To use proxies you must pass the proxy
- information at `Client` initialization.
+ To use proxies you must pass the proxy information at `Client` initialization,
+ rather than on the `.get(...)` call or other request methods.
## Timeout Configuration
@@ -274,22 +247,22 @@ You can set timeouts for an individual request:
```python
# Using the top-level API:
-await httpx.get('http://example.com/api/v1/example', timeout=10.0)
+httpx.get('http://example.com/api/v1/example', timeout=10.0)
# Using a client instance:
-async with httpx.AsyncClient() as client:
- await client.get("http://example.com/api/v1/example", timeout=10.0)
+with httpx.Client() as client:
+ client.get("http://example.com/api/v1/example", timeout=10.0)
```
Or disable timeouts for an individual request:
```python
# Using the top-level API:
-await httpx.get('http://example.com/api/v1/example', timeout=None)
+httpx.get('http://example.com/api/v1/example', timeout=None)
# Using a client instance:
-async with httpx.AsyncClient() as client:
- await client.get("http://example.com/api/v1/example", timeout=None)
+with httpx.Client() as client:
+ client.get("http://example.com/api/v1/example", timeout=None)
```
### Setting a default timeout on a client
@@ -298,9 +271,9 @@ You can set a timeout on a client instance, which results in the given
`timeout` being used as the default for requests made with this client:
```python
-client = httpx.AsyncClient() # Use a default 5s timeout everywhere.
-client = httpx.AsyncClient(timeout=10.0) # Use a default 10s timeout everywhere.
-client = httpx.AsyncClient(timeout=None) # Disable all timeouts by default.
+client = httpx.Client() # Use a default 5s timeout everywhere.
+client = httpx.Client(timeout=10.0) # Use a default 10s timeout everywhere.
+client = httpx.Client(timeout=None) # Disable all timeouts by default.
```
### Fine tuning the configuration
@@ -330,9 +303,9 @@ You can configure the timeout behavior for any of these values...
```python
# A client with a 60s timeout for connecting, and a 10s timeout elsewhere.
timeout = httpx.Timeout(10.0, connect_timeout=60.0)
-client = httpx.AsyncClient(timeout=timeout)
+client = httpx.Client(timeout=timeout)
-response = await client.get('http://example.com/')
+response = client.get('http://example.com/')
```
## Multipart file encoding
@@ -343,7 +316,7 @@ name of the payloads as keys and either tuple of elements or a file-like object
```python
>>> files = {'upload-file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel')}
->>> r = await httpx.post("https://httpbin.org/post", files=files)
+>>> r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
@@ -368,7 +341,7 @@ MIME header field.
```python
>>> files = {'upload-file': (None, 'text content', 'text/plain')}
->>> r = await httpx.post("https://httpbin.org/post", files=files)
+>>> r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
@@ -456,7 +429,7 @@ If you'd like to use a custom CA bundle, you can use the `verify` parameter.
```python
import httpx
-r = await httpx.get("https://example.org", verify="path/to/client.pem")
+r = httpx.get("https://example.org", verify="path/to/client.pem")
```
You can also disable the SSL verification...
@@ -464,7 +437,7 @@ You can also disable the SSL verification...
```python
import httpx
-r = await httpx.get("https://example.org", verify=False)
+r = httpx.get("https://example.org", verify=False)
```
### SSL configuration on client instances
@@ -472,7 +445,7 @@ r = await httpx.get("https://example.org", verify=False)
If you're using a `Client()` instance, then you should pass any SSL settings when instantiating the client.
```python
-client = httpx.AsyncClient(verify=False)
+client = httpx.Client(verify=False)
```
The `client.get(...)` method and other request methods *do not* support changing the SSL settings on a per-request basis. If you need different SSL settings in different cases you should use more that one client instance, with different settings on each. Each client will then be using an isolated connection pool with a specific fixed SSL configuration on all connections within that pool.
@@ -489,60 +462,7 @@ If you do need to make HTTPS connections to a local server, for example to test
```python
>>> import httpx
->>> r = await httpx.get("https://localhost:8000", verify="/tmp/client.pem")
+>>> r = httpx.get("https://localhost:8000", verify="/tmp/client.pem")
>>> r
Response <200 OK>
```
-
-## Supported async environments
-
-HTTPX supports either `asyncio` or `trio` as an async environment.
-
-By default it will auto-detect which of those two to use as the backend
-for socket operations and concurrency primitives.
-
-You can also explicitly select a backend by instantiating a client with the
-`backend` argument...
-
-```python
-client = httpx.AsyncClient(backend='auto') # Autodetection. The default case.
-client = httpx.AsyncClient(backend='asyncio') # Use asyncio as the backend.
-client = httpx.AsyncClient(backend='trio') # Use trio as the backend.
-```
-
-### [AsyncIO](https://docs.python.org/3/library/asyncio.html)
-
-AsyncIO is Python's [built-in library](https://docs.python.org/3/library/asyncio.html)
-for writing concurrent code with the async/await syntax.
-
-```python
-import asyncio
-import httpx
-
-async def main():
- client = httpx.AsyncClient()
- response = await client.get('https://www.example.com/')
- print(response)
-
-asyncio.run(main())
-```
-
-### [Trio](https://github.com/python-trio/trio)
-
-Trio is [an alternative async library](https://trio.readthedocs.io/en/stable/),
-designed around the [the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency).
-
-```python
-import httpx
-import trio
-
-async def main():
- client = httpx.AsyncClient()
- response = await client.get('https://www.example.com/')
- print(response)
-
-trio.run(main)
-```
-
-!!! important
- The `trio` package must be installed to use the Trio backend.
diff --git a/docs/api.md b/docs/api.md
index ae4d201eb5..b01def1464 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -32,11 +32,11 @@
::: httpx.delete
:docstring:
-## `AsyncClient`
+## `Client`
-::: httpx.AsyncClient
+::: httpx.Client
:docstring:
- :members: headers cookies params request get head options post put patch delete build_request send aclose
+ :members: headers cookies params request get head options post put patch delete build_request send close
## `Response`
@@ -61,6 +61,13 @@
the total elapsed seconds.
* `def .raise_for_status()` - **None**
* `def .json()` - **Any**
+* `def .read()` - **bytes**
+* `def .iter_raw()` - **async bytes iterator**
+* `def .iter_bytes()` - **async bytes iterator**
+* `def .iter_text()` - **async text iterator**
+* `def .iter_lines()` - **async text iterator**
+* `def .close()` - **None**
+* `def .next()` - **Response**
* `def .aread()` - **bytes**
* `def .aiter_raw()` - **async bytes iterator**
* `def .aiter_bytes()` - **async bytes iterator**
@@ -76,13 +83,13 @@ what gets sent over the wire.*
```python
>>> request = httpx.Request("GET", "https://example.org", headers={'host': 'example.org'})
->>> response = await client.send(request)
+>>> response = client.send(request)
```
* `def __init__(method, url, [params], [data], [json], [headers], [cookies])`
* `.method` - **str**
* `.url` - **URL**
-* `.content` - **byte** or **byte async iterator**
+* `.content` - **byte**, **byte iterator**, or **byte async iterator**
* `.headers` - **Headers**
* `.cookies` - **Cookies**
diff --git a/docs/async.md b/docs/async.md
new file mode 100644
index 0000000000..be0c2cfa37
--- /dev/null
+++ b/docs/async.md
@@ -0,0 +1,210 @@
+# Async Support
+
+HTTPX offers a standard synchronous API by default, but also gives you
+the option of an async client if you need it.
+
+Async is a concurrency model that is far more efficient than multi-threading,
+and can provide significant performance benefits and enable the use of
+long-lived network connections such as WebSockets.
+
+If you're working with an async web framework then you'll also want to use an
+async client for sending outgoing HTTP requests.
+
+## Making Async requests
+
+To make asynchronous requests, you'll need an `AsyncClient`.
+
+```python
+>>> async with httpx.AsyncClient() as client:
+>>> r = await client.get('https://www.example.com/')
+>>> r
+
+```
+
+!!! tip
+ Use [IPython](https://ipython.readthedocs.io/en/stable/) or Python 3.8+ with `python -m asyncio` to try this code interactively, as they support executing `async`/`await` expressions in the console.
+
+## API Differences
+
+If you're using an async client then there are a few bits of API that
+use async methods.
+
+### Making requests
+
+The request methods are all async, so you should use `response = await client.get(...)` style for all of the following:
+
+* `AsyncClient.get(url, ...)`
+* `AsyncClient.options(url, ...)`
+* `AsyncClient.head(url, ...)`
+* `AsyncClient.post(url, ...)`
+* `AsyncClient.put(url, ...)`
+* `AsyncClient.patch(url, ...)`
+* `AsyncClient.delete(url, ...)`
+* `AsyncClient.request(method, url, ...)`
+* `AsyncClient.send(request, ...)`
+
+### Opening and closing clients
+
+Use `async with httpx.AsyncClient()` if you want a context-managed client...
+
+```python
+async with httpx.AsyncClient() as client:
+ ...
+```
+
+Alternatively, use `await client.aclose()` if you want to close a client explicitly:
+
+```python
+client = httpx.AsyncClient()
+...
+await client.aclose()
+```
+
+### Streaming responses
+
+The `AsyncClient.stream(method, url, ...)` method is an async context block.
+
+```python
+>>> client = httpx.AsyncClient()
+>>> async with client.stream('GET', 'https://www.example.com/') as response:
+>>> async for chunk in r.aiter_content():
+>>> ...
+```
+
+The async response streaming methods are:
+
+* `Response.aread()` - For conditionally reading a response inside a stream block.
+* `Response.aiter_content()` - For streaming the response content as bytes.
+* `Response.aiter_text()` - For streaming the response content as text.
+* `Response.aiter_lines()` - For streaming the response content as lines of text.
+* `Response.aiter_raw()` - For streaming the raw response bytes, without applying content decoding.
+* `Response.aclose()` - For closing the response. You don't usually need this, since `.stream` block close the response automatically on exit.
+
+### Streaming requests
+
+When sending a streaming request body with an `AsyncClient` instance, you should use an async butes generator instead of a bytes generator:
+
+```python
+async def upload_bytes():
+ ... # yield byte content
+
+await client.post(url, data=upload_bytes())
+```
+
+## Supported async environments
+
+HTTPX supports either `asyncio` or `trio` as an async environment.
+
+By default it will auto-detect which of those two to use as the backend
+for socket operations and concurrency primitives.
+
+You can also explicitly select a backend by instantiating a client with the
+`backend` argument...
+
+```python
+client = httpx.AsyncClient(backend='auto') # Autodetection. The default case.
+client = httpx.AsyncClient(backend='asyncio') # Use asyncio as the backend.
+client = httpx.AsyncClient(backend='trio') # Use trio as the backend.
+```
+
+### [AsyncIO](https://docs.python.org/3/library/asyncio.html)
+
+AsyncIO is Python's [built-in library](https://docs.python.org/3/library/asyncio.html)
+for writing concurrent code with the async/await syntax.
+
+```python
+import asyncio
+import httpx
+
+async def main():
+ async with httpx.AsyncClient() as client:
+ response = await client.get('https://www.example.com/')
+ print(response)
+
+asyncio.run(main())
+```
+
+### [Trio](https://github.com/python-trio/trio)
+
+Trio is [an alternative async library](https://trio.readthedocs.io/en/stable/),
+designed around the [the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency).
+
+```python
+import httpx
+import trio
+
+async def main():
+ async with httpx.AsyncClient() as client:
+ response = await client.get('https://www.example.com/')
+ print(response)
+
+trio.run(main)
+```
+
+!!! important
+ The `trio` package must be installed to use the Trio backend.
+
+## Calling into Python Web Apps
+
+Just as `httpx.Client` allows you to call directly into WSGI web applications,
+the `httpx.AsyncClient` class allows you to call directly into ASGI web applications.
+
+Let's take this Starlette application as an example:
+
+```python
+from starlette.applications import Starlette
+from starlette.responses import HTMLResponse
+from starlette.routing import Route
+
+
+async def hello():
+ return HTMLResponse("Hello World!")
+
+
+app = Starlette(routes=[Route("/", hello)])
+```
+
+We can make requests directly against the application, like so:
+
+```python
+>>> import httpx
+>>> async with httpx.AsyncClient(app=app) as client:
+... r = await client.get('http://example/')
+... assert r.status_code == 200
+... assert r.text == "Hello World!"
+```
+
+For some more complex cases you might need to customise the ASGI dispatch. 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`.
+* Use a given client address for requests by setting `client`.
+
+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) as client:
+ ...
+```
+
+See [the ASGI documentation](https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope) for more details on the `client` and `root_path` keys.
+
+## Unix Domain Sockets
+
+The async client provides support for connecting through a unix domain socket via the `uds` parameter. This is useful when making requests to a server that is bound to a socket file rather than an IP address.
+
+Here's an example requesting the Docker Engine API:
+
+```python
+import httpx
+
+
+async with httpx.AsyncClient(uds="/var/run/docker.sock") as client:
+ # This request will connect through the socket file.
+ resp = await client.get("http://localhost/version")
+```
+
+This functionality is not currently available in the synchronous client.
diff --git a/docs/compatibility.md b/docs/compatibility.md
index fc3a615731..aefee2f086 100644
--- a/docs/compatibility.md
+++ b/docs/compatibility.md
@@ -22,17 +22,17 @@ HTTPX provides a `.stream()` interface rather than using `stream=True`. This ens
For example:
```python
-async with request.stream("GET", "https://www.example.com") as response:
+with request.stream("GET", "https://www.example.com") as response:
...
```
Within a `stream()` block request data is made available with:
-* `.aiter_bytes()` - Instead of `response.iter_content()`
-* `.aiter_text()` - Instead of `response.iter_content(decode_unicode=True)`
-* `.aiter_lines()` - Instead of `response.iter_lines()`
-* `.aiter_raw()` - Use this instead of `response.raw`
-* `.aread()` - Read the entire response body, making `request.text` and `response.content` available.
+* `.iter_bytes()` - Instead of `response.iter_content()`
+* `.iter_text()` - Instead of `response.iter_content(decode_unicode=True)`
+* `.iter_lines()` - Corresponding to `response.iter_lines()`
+* `.iter_raw()` - Use this instead of `response.raw`
+* `.read()` - Read the entire response body, making `request.text` and `response.content` available.
## SSL configuration
@@ -52,7 +52,7 @@ We don't support `response.is_ok` since the naming is ambiguous there, and might
## Client instances
-The HTTPX equivalent of `requests.Session` is `httpx.AsyncClient`.
+The HTTPX equivalent of `requests.Session` is `httpx.Client`.
```python
session = requests.Session(**kwargs)
@@ -61,7 +61,7 @@ session = requests.Session(**kwargs)
is generally equivalent to
```python
-client = httpx.AsyncClient(**kwargs)
+client = httpx.Client(**kwargs)
```
## Mocking
diff --git a/docs/environment_variables.md b/docs/environment_variables.md
index 6289d03055..123a890847 100644
--- a/docs/environment_variables.md
+++ b/docs/environment_variables.md
@@ -4,7 +4,7 @@ Environment Variables
The HTTPX library can be configured via environment variables.
Environment variables are used by default. To ignore environment variables, `trust_env` has to be set `False`. There are two ways to set `trust_env` to disable environment variables:
-* On the client via `httpx.AsyncClient(trust_env=False)`.
+* On the client via `httpx.Client(trust_env=False)`.
* Using the top-level API, such as `httpx.get("", trust_env=False)`.
Here is a list of environment variables that HTTPX recognizes and what function they serve:
@@ -24,7 +24,7 @@ Example:
# test_script.py
import httpx
-with httpx.AsyncClient() as client:
+with httpx.Client() as client:
r = client.get("https://google.com")
```
diff --git a/docs/http2.md b/docs/http2.md
index 25705bf289..d68a908d49 100644
--- a/docs/http2.md
+++ b/docs/http2.md
@@ -18,14 +18,14 @@ For a comprehensive guide to HTTP/2 you may want to check out "[HTTP2 Explained]
## Enabling HTTP/2
-The HTTPX client provides provisional HTTP/2 support.
+The HTTPX client provides HTTP/2 support, **which is currently only available with the async client**.
HTTP/2 support is not enabled by default, because HTTP/1.1 is a mature,
-battle-hardened transport layer. With HTTP/2 being newer and significantly more
-complex, our implementation should be considered a less robust option at this
-point in time.
+battle-hardened transport layer, and our HTTP/1.1 may be considered the more robust
+option at this point in time. It is possible that a future version of `httpx` may
+enable HTTP/2 support by default.
-However, if you're issuing highly concurrent requests you might want to consider
+If you're issuing highly concurrent requests you might want to consider
trying out our HTTP/2 support. You can do so by instantiating a client with
HTTP/2 support enabled:
diff --git a/docs/index.md b/docs/index.md
index 61c076b382..a8285834ad 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -24,41 +24,27 @@ HTTPX
A next-generation HTTP client for Python.
-HTTPX is an asynchronous client library that supports HTTP/1.1 and HTTP/2.
+HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.
-It can be used in high-performance async web frameworks, using either asyncio
-or trio, and is able to support making large numbers of concurrent requests.
!!! note
- HTTPX should currently be considered in alpha. We'd love early users and feedback,
- but would strongly recommend pinning your dependencies to the latest median
- release, so that you're able to properly review API changes between package
- updates. Currently you should be using `httpx==0.10.*`.
+ HTTPX should currently be considered in beta.
- In particular, the 0.8 release switched HTTPX into focusing exclusively on
- providing an async client, in order to move the project forward, and help
- us [change our approach to providing sync+async support][sync-support]. If
- you have been using the sync client, you may want to pin to `httpx==0.7.*`,
- and wait until our sync client is reintroduced.
+ We believe we've got the public API to a stable point now, but would strongly recommend pinning your dependencies to the `0.11.*` release, so that you're able to properly review API changes between package updates.
+
+ A 1.0 release is expected to be issued sometime on or before April 2020.
---
Let's get started...
-The standard Python REPL does not allow top-level async statements.
-
-To run these async examples you'll probably want to either use `ipython`,
-or use Python 3.8 with `python -m asyncio`.
-
```python
>>> import httpx
->>> r = await httpx.get('https://www.example.org/')
+>>> r = httpx.get('https://www.example.org/')
>>> r
>>> r.status_code
200
->>> r.http_version
-'HTTP/1.1'
>>> r.headers['content-type']
'text/html; charset=UTF-8'
>>> r.text
@@ -70,12 +56,13 @@ or use Python 3.8 with `python -m asyncio`.
HTTPX is a high performance asynchronous HTTP client, that builds on the
well-established usability of `requests`, and gives you:
-* A broadly requests-compatible API.
-* HTTP/1.1 and [HTTP/2 support](http2.md).
-* Ability to [make requests directly to ASGI applications](advanced.md#calling-into-python-web-apps).
+* A requests-compatible API wherever possible.
+* Standard synchronous interface, but with [async support if you need it](async.md).
+* HTTP/1.1 [and HTTP/2 support](http2.md).
+* Ability to make requests directly to [WSGI applications](https://www.encode.io/httpx/advanced/#calling-into-python-web-apps) or [ASGI applications](https://www.encode.io/httpx/async/#calling-into-python-web-apps).
* Strict timeouts everywhere.
* Fully type annotated.
-* 100% test coverage.
+* 99% test coverage.
Plus all the standard features of `requests`...
@@ -100,7 +87,7 @@ Plus all the standard features of `requests`...
For a run-through of all the basics, head over to the [QuickStart](quickstart.md).
For more advanced topics, see the [Advanced Usage](advanced.md) section,
-or the [HTTP/2](http2.md) section.
+the [async support](async.md) section, or the [HTTP/2](http2.md) section.
The [Developer Interface](api.md) provides a comprehensive API reference.
@@ -108,8 +95,9 @@ The [Developer Interface](api.md) provides a comprehensive API reference.
The HTTPX project relies on these excellent libraries:
-* `h2` - HTTP/2 support.
+* `urllib3` - Sync client support.
* `h11` - HTTP/1.1 support.
+* `h2` - HTTP/2 support.
* `certifi` - SSL certificates.
* `chardet` - Fallback auto-detection for response encoding.
* `hstspreload` - determines whether IDNA-encoded host should be only accessed via HTTPS.
diff --git a/docs/quickstart.md b/docs/quickstart.md
index 0d68965b2a..eeabcb0c8d 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -1,11 +1,5 @@
# QuickStart
-!!! note
- The standard Python REPL does not allow top-level async statements.
-
- To run async examples directly you'll probably want to either use `ipython`,
- or use Python 3.8 with `python -m asyncio`.
-
First, start by importing HTTPX:
```python
@@ -15,7 +9,7 @@ First, start by importing HTTPX:
Now, let’s try to get a webpage.
```python
->>> r = await httpx.get('https://httpbin.org/get')
+>>> r = httpx.get('https://httpbin.org/get')
>>> r
```
@@ -23,16 +17,16 @@ Now, let’s try to get a webpage.
Similarly, to make an HTTP POST request:
```python
->>> r = await httpx.post('https://httpbin.org/post', data={'key': 'value'})
+>>> r = httpx.post('https://httpbin.org/post', data={'key': 'value'})
```
The PUT, DELETE, HEAD, and OPTIONS requests all follow the same style:
```python
->>> r = await httpx.put('https://httpbin.org/put', data={'key': 'value'})
->>> r = await httpx.delete('https://httpbin.org/delete')
->>> r = await httpx.head('https://httpbin.org/get')
->>> r = await httpx.options('https://httpbin.org/get')
+>>> r = httpx.put('https://httpbin.org/put', data={'key': 'value'})
+>>> r = httpx.delete('https://httpbin.org/delete')
+>>> r = httpx.head('https://httpbin.org/get')
+>>> r = httpx.options('https://httpbin.org/get')
```
## Passing Parameters in URLs
@@ -41,7 +35,7 @@ To include URL query parameters in the request, use the `params` keyword:
```python
>>> params = {'key1': 'value1', 'key2': 'value2'}
->>> r = await httpx.get('https://httpbin.org/get', params=params)
+>>> r = httpx.get('https://httpbin.org/get', params=params)
```
To see how the values get encoding into the URL string, we can inspect the
@@ -56,7 +50,7 @@ You can also pass a list of items as a value:
```python
>>> params = {'key1': 'value1', 'key2': ['value2', 'value3']}
->>> r = await httpx.get('https://httpbin.org/get', params=params)
+>>> r = httpx.get('https://httpbin.org/get', params=params)
>>> r.url
URL('https://httpbin.org/get?key1=value1&key2=value2&key2=value3')
```
@@ -66,7 +60,7 @@ URL('https://httpbin.org/get?key1=value1&key2=value2&key2=value3')
HTTPX will automatically handle decoding the response content into Unicode text.
```python
->>> r = await httpx.get('https://www.example.org/')
+>>> r = httpx.get('https://www.example.org/')
>>> r.text
'\n\n\nExample Domain...'
```
@@ -111,7 +105,7 @@ For example, to create an image from binary data returned by a request, you can
Often Web API responses will be encoded as JSON.
```python
->>> r = await httpx.get('https://api.github.com/events')
+>>> r = httpx.get('https://api.github.com/events')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...' ... }}]
```
@@ -123,7 +117,7 @@ To include additional headers in the outgoing request, use the `headers` keyword
```python
>>> url = 'http://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
->>> r = await httpx.get(url, headers=headers)
+>>> r = httpx.get(url, headers=headers)
```
## Sending Form Encoded Data
@@ -134,7 +128,7 @@ which is used for HTML forms.
```python
>>> data = {'key1': 'value1', 'key2': 'value2'}
->>> r = await httpx.post("https://httpbin.org/post", data=data)
+>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
...
@@ -150,7 +144,7 @@ Form encoded data can also include multiple values form a given key.
```python
>>> data = {'key1': ['value1', 'value2']}
->>> r = await httpx.post("https://httpbin.org/post", data=data)
+>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
...
@@ -170,7 +164,7 @@ You can also upload files, using HTTP multipart encoding:
```python
>>> files = {'upload-file': open('report.xls', 'rb')}
->>> r = await httpx.post("https://httpbin.org/post", files=files)
+>>> r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
@@ -186,7 +180,7 @@ of items for the file value:
```python
>>> files = {'upload-file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel')}
->>> r = await httpx.post("https://httpbin.org/post", files=files)
+>>> r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
...
@@ -204,7 +198,7 @@ For more complicated data structures you'll often want to use JSON encoding inst
```python
>>> data = {'integer': 123, 'boolean': True, 'list': ['a', 'b', 'c']}
->>> r = await httpx.post("https://httpbin.org/post", json=data)
+>>> r = httpx.post("https://httpbin.org/post", json=data)
>>> print(r.text)
{
...
@@ -234,7 +228,7 @@ binary data.
We can inspect the HTTP status code of the response:
```python
->>> r = await httpx.get('https://httpbin.org/get')
+>>> r = httpx.get('https://httpbin.org/get')
>>> r.status_code
200
```
@@ -249,7 +243,7 @@ True
We can raise an exception for any Client or Server error responses (4xx or 5xx status codes):
```python
->>> not_found = await httpx.get('https://httpbin.org/status/404')
+>>> not_found = httpx.get('https://httpbin.org/status/404')
>>> not_found.status_code
404
>>> not_found.raise_for_status()
@@ -303,24 +297,24 @@ For large downloads you may want to use streaming responses that do not load the
You can stream the binary content of the response...
```
->>> async with httpx.stream("GET", "https://www.example.com") as r:
-... async for data in r.aiter_bytes():
+>>> with httpx.stream("GET", "https://www.example.com") as r:
+... for data in r.iter_bytes():
... print(data)
```
Or the text of the response...
```
->>> async with httpx.stream("GET", "https://www.example.com") as r:
-... async for text in r.aiter_text():
+>>> with httpx.stream("GET", "https://www.example.com") as r:
+... for text in r.iter_text():
... print(text)
```
Or stream the text, on a line-by-line basis...
```
->>> async with httpx.stream("GET", "https://www.example.com") as r:
-... async for line in r.aiter_lines():
+>>> with httpx.stream("GET", "https://www.example.com") as r:
+... for line in r.iter_lines():
... print(line)
```
@@ -329,17 +323,17 @@ HTTPX will use universal line endings, normalising all cases to `\n`.
In some cases you might want to access the raw bytes on the response without applying any HTTP content decoding. In this case any content encoding that the web server has applied such as `gzip`, `deflate`, or `brotli` will not be automatically decoded.
```
->>> async with httpx.stream("GET", "https://www.example.com") as r:
-... async for chunk in r.aiter_raw():
+>>> with httpx.stream("GET", "https://www.example.com") as r:
+... for chunk in r.iter_raw():
... print(chunk)
```
If you're using streaming responses in any of these ways then the `response.content` and `response.text` attributes will not be available, and will raise errors if accessed. However you can also use the response streaming functionality to conditionally load the response body:
-```
->>> async with httpx.stream("GET", "https://www.example.com") as r:
+```python
+>>> with httpx.stream("GET", "https://www.example.com") as r:
... if r.headers['Content-Length'] < TOO_LONG:
-... await r.aread()
+... r.read()
... print(r.text)
```
@@ -348,7 +342,7 @@ If you're using streaming responses in any of these ways then the `response.cont
Any cookies that are set on the response can be easily accessed:
```python
->>> r = await httpx.get('http://httpbin.org/cookies/set?chocolate=chip', allow_redirects=False)
+>>> r = httpx.get('http://httpbin.org/cookies/set?chocolate=chip', allow_redirects=False)
>>> r.cookies['chocolate']
'chip'
```
@@ -357,7 +351,7 @@ To include cookies in an outgoing request, use the `cookies` parameter:
```python
>>> cookies = {"peanut": "butter"}
->>> r = await httpx.get('http://httpbin.org/cookies', cookies=cookies)
+>>> r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'peanut': 'butter'}}
```
@@ -369,7 +363,7 @@ with additional API for accessing cookies by their domain or path.
>>> cookies = httpx.Cookies()
>>> cookies.set('cookie_on_domain', 'hello, there!', domain='httpbin.org')
>>> cookies.set('cookie_off_domain', 'nope.', domain='example.org')
->>> r = await httpx.get('http://httpbin.org/cookies', cookies=cookies)
+>>> r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'cookie_on_domain': 'hello, there!'}}
```
@@ -385,7 +379,7 @@ in which they were made.
For example, GitHub redirects all HTTP requests to HTTPS.
```python
->>> r = await httpx.get('http://github.com/')
+>>> r = httpx.get('http://github.com/')
>>> r.url
URL('https://github.com/')
>>> r.status_code
@@ -397,7 +391,7 @@ URL('https://github.com/')
You can modify the default redirection handling with the allow_redirects parameter:
```python
->>> r = await httpx.get('http://github.com/', allow_redirects=False)
+>>> r = httpx.get('http://github.com/', allow_redirects=False)
>>> r.status_code
301
>>> r.history
@@ -407,7 +401,7 @@ You can modify the default redirection handling with the allow_redirects paramet
If you’re making a `HEAD` request, you can use this to enable redirection:
```python
->>> r = await httpx.head('http://github.com/', allow_redirects=True)
+>>> r = httpx.head('http://github.com/', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
@@ -424,13 +418,13 @@ The default timeout for network inactivity is five seconds. You can modify the
value to be more or less strict:
```python
->>> await httpx.get('https://github.com/', timeout=0.001)
+>>> httpx.get('https://github.com/', timeout=0.001)
```
You can also disable the timeout behavior completely...
```python
->>> await httpx.get('https://github.com/', timeout=None)
+>>> httpx.get('https://github.com/', timeout=None)
```
For advanced timeout management, see [Timeout fine-tuning](https://www.encode.io/httpx/advanced/#fine-tuning-the-configuration).
@@ -444,7 +438,7 @@ plaintext `str` or `bytes` objects as the `auth` argument to the request
functions:
```python
->>> await httpx.get("https://example.com", auth=("my_user", "password123"))
+>>> httpx.get("https://example.com", auth=("my_user", "password123"))
```
To provide credentials for Digest authentication you'll need to instantiate
@@ -454,6 +448,6 @@ as above:
```python
>>> auth = httpx.DigestAuth("my_user", "password123")
->>> await httpx.get("https://example.com", auth=auth)
+>>> httpx.get("https://example.com", auth=auth)
```
diff --git a/mkdocs.yml b/mkdocs.yml
index a2a733c935..f4ab09ebbf 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -12,6 +12,7 @@ nav:
- Introduction: 'index.md'
- QuickStart: 'quickstart.md'
- Advanced Usage: 'advanced.md'
+ - Async Support: 'async.md'
- HTTP/2 Support: 'http2.md'
- Environment Variables: 'environment_variables.md'
- Requests Compatibility: 'compatibility.md'