diff --git a/docs/clients.md b/docs/clients.md
index 9693f13..6c944d6 100644
--- a/docs/clients.md
+++ b/docs/clients.md
@@ -2,15 +2,25 @@
HTTP requests are sent by using a `Client` instance. Client instances are thread safe interfaces that maintain a pool of HTTP connections.
-```python
+
+
+```{ .python .httpx }
>>> cli = httpx.Client()
>>> cli
```
+```{ .python .ahttpx .hidden }
+>>> cli = ahttpx.Client()
+>>> cli
+
+```
+
The client representation provides an indication of how many connections are currently in the pool.
-```python
+
+
+```{ .python .httpx }
>>> r = cli.get("https://www.example.com")
>>> r = cli.get("https://www.wikipedia.com")
>>> r = cli.get("https://www.theguardian.com")
@@ -18,19 +28,42 @@ The client representation provides an indication of how many connections are cur
```
+```{ .python .ahttpx .hidden }
+>>> r = await cli.get("https://www.example.com")
+>>> r = await cli.get("https://www.wikipedia.com")
+>>> r = await cli.get("https://www.theguardian.com")
+>>> cli
+
+```
+
The connections in the pool can be explicitly closed, using the `close()` method...
-```python
+
+
+```{ .python .httpx }
>>> cli.close()
>>> cli
```
+```{ .python .ahttpx .hidden }
+>>> await cli.close()
+>>> cli
+
+```
+
Client instances support being used in a context managed scope. You can use this style to enforce properly scoped resources, ensuring that the connection pool is cleanly closed when no longer required.
-```python
+
+
+```{ .python .httpx }
>>> with httpx.Client() as cli:
-... cli.get("https://www.example.com")
+... r = cli.get("https://www.example.com")
+```
+
+```{ .python .ahttpx .hidden }
+>>> async with ahttpx.Client() as cli:
+... r = await cli.get("https://www.example.com")
```
It is important to scope the use of client instances as widely as possible.
@@ -43,13 +76,24 @@ The recommened usage is to *either* a have single global instance created at imp
Client instances can be configured with a base URL that is used when constructing requests...
-```python
->>> cli = httpx.Client(url="https://www.httpbin.org")
->>> r = cli.get("/json")
->>> r
+
+
+```{ .python .httpx }
+>>> with httpx.Client(url="https://www.httpbin.org") as cli:
+>>> r = cli.get("/json")
+>>> print(r)
->>> r.request.url
-'https://www.httpbin.org/json'
+>>> print(r.request.url)
+
+```
+
+```{ .python .ahttpx .hidden }
+>>> async with ahttpx.Client(url="https://www.httpbin.org") as cli:
+>>> r = cli.get("/json")
+>>> print(r)
+
+>>> print(r.request.url)
+
```
## Setting client headers
@@ -65,25 +109,46 @@ The default headers are:
You can override this behavior by explicitly specifying the default headers...
-```python
+
+
+```{ .python .httpx }
>>> headers = {"User-Agent": "dev", "Accept-Encoding": "gzip"}
->>> cli = httpx.Client(headers=headers)
->>> r = cli.get("https://www.example.com/")
+>>> with httpx.Client(headers=headers) as cli:
+>>> r = cli.get("https://www.example.com/")
+```
+
+```{ .python .ahttpx .hidden }
+>>> headers = {"User-Agent": "dev", "Accept-Encoding": "gzip"}
+>>> async with ahttpx.Client(headers=headers) as cli:
+>>> r = await cli.get("https://www.example.com/")
```
## Configuring the connection pool
The connection pool used by the client can be configured in order to customise the SSL context, the maximum number of concurrent connections, or the network backend.
-```python
+
+
+```{ .python .httpx }
>>> # Setup an SSL context to allow connecting to improperly configured SSL.
>>> no_verify = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> no_verify.check_hostname = False
>>> no_verify.verify_mode = ssl.CERT_NONE
>>> # Instantiate a client with our custom SSL context.
->>> with httpx.ConnectionPool(ssl_context=no_verify) as pool:
->>> with httpx.Client(transport=pool) as cli:
->>> ...
+>>> pool = httpx.ConnectionPool(ssl_context=no_verify):
+>>> with httpx.Client(transport=pool) as cli:
+>>> ...
+```
+
+```{ .python .ahttpx .hidden }
+>>> # Setup an SSL context to allow connecting to improperly configured SSL.
+>>> no_verify = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+>>> no_verify.check_hostname = False
+>>> no_verify.verify_mode = ssl.CERT_NONE
+>>> # Instantiate a client with our custom SSL context.
+>>> pool = ahttpx.ConnectionPool(ssl_context=no_verify)
+>>> async with ahttpx.Client(transport=pool) as cli:
+>>> ...
```
## Sending requests
@@ -106,7 +171,9 @@ By default requests are sent using the `ConnectionPool` class. Alternative imple
For example, a mock transport class that doesn't make any network requests and instead always returns a fixed response.
-```python
+
+
+```{ .python .httpx }
class MockTransport(httpx.Transport):
def __init__(self, response):
self._response = response
@@ -120,8 +187,28 @@ class MockTransport(httpx.Transport):
response = httpx.Response(200, content=httpx.Text('Hello, world'))
transport = MockTransport(response=response)
-cli = httpx.Client(transport=transport)
-print(cli.get('https://www.example.com'))
+with httpx.Client(transport=transport) as cli:
+ r = cli.get('https://www.example.com')
+ print(r)
+```
+
+```{ .python .ahttpx .hidden }
+class MockTransport(ahttpx.Transport):
+ def __init__(self, response):
+ self._response = response
+
+ @contextlib.contextmanager
+ def send(self, request):
+ yield response
+
+ def close(self):
+ pass
+
+response = ahttpx.Response(200, content=httpx.Text('Hello, world'))
+transport = MockTransport(response=response)
+async with ahttpx.Client(transport=transport) as cli:
+ r = await cli.get('https://www.example.com')
+ print(r)
```
---
@@ -151,7 +238,9 @@ If you're working with a large codebase you might want to create a custom client
The following example demonstrates a custom API client that only exposes `GET` and `POST` requests, and always uses JSON payloads.
-```python
+
+
+```{ .python .httpx }
class APIClient:
def __init__(self):
self.url = httpx.URL('https://www.example.com')
@@ -184,6 +273,39 @@ class APIClient:
return response
```
+```{ .python .ahttpx .hidden }
+class APIClient:
+ def __init__(self):
+ self.url = ahttpx.URL('https://www.example.com')
+ self.headers = ahttpx.Headers({
+ 'Accept-Encoding': 'gzip',
+ 'Connection': 'keep-alive',
+ 'User-Agent': 'dev'
+ })
+ self.via = ahttpx.RedirectMiddleware(ahttpx.ConnectionPool())
+
+ async def get(self, path: str) -> Response:
+ request = ahttpx.Request(
+ method="GET",
+ url=self.url.join(path),
+ headers=self.headers,
+ )
+ async with self.via.send(request) as response:
+ await response.read()
+ return response
+
+ async def post(self, path: str, payload: Any) -> ahttpx.Response:
+ request = ahttpx.Request(
+ method="POST",
+ url=self.url.join(path),
+ headers=self.headers,
+ content=httpx.JSON(payload),
+ )
+ async with self.via.send(request) as response:
+ await response.read()
+ return response
+```
+
You can expand on this pattern to provide behavior such as request or response schema validation, consistent timeouts, or standardised logging and exception handling.
---
diff --git a/docs/headers.md b/docs/headers.md
index e2df937..3b84e27 100644
--- a/docs/headers.md
+++ b/docs/headers.md
@@ -2,7 +2,9 @@
The `Headers` class provides an immutable case-insensitive multidict interface for accessing HTTP headers.
-```python
+
+
+```{ .python .httpx }
>>> headers = httpx.Headers({"Accept": "*/*"})
>>> headers
@@ -10,6 +12,14 @@ The `Headers` class provides an immutable case-insensitive multidict interface f
'*/*'
```
+```{ .python .ahttpx .hidden }
+>>> headers = ahttpx.Headers({"Accept": "*/*"})
+>>> headers
+
+>>> headers['accept']
+'*/*'
+```
+
Header values should always be printable ASCII strings. Attempting to set invalid header name or value strings will raise a `ValueError`.
### Accessing headers
diff --git a/docs/index.md b/docs/index.md
index 53ecdfe..ecb6f8f 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -34,7 +34,7 @@ $ pip install --pre ahttpx
200
>>> r.headers['content-type']
'text/html; charset=UTF-8'
->>> r.text
+>>> r.text()
'\n\n\nExample Domain...'
```
@@ -46,7 +46,7 @@ $ pip install --pre ahttpx
200
>>> r.headers['content-type']
'text/html; charset=UTF-8'
->>> r.text
+>>> await r.text()
'\n\n\nExample Domain...'
```
diff --git a/docs/quickstart.md b/docs/quickstart.md
index cf4a18b..3d0f6f7 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -2,76 +2,143 @@
Install using ...
-```shell
-$ pip install git+https://github.com/encode/httpnext.git
+
+
+```{ .shell .httpx }
+$ pip install --pre httpx
+```
+
+```{ .shell .ahttpx .hidden }
+$ pip install --pre ahttpx
```
First, start by importing `httpx`...
-```python
+
+
+```{ .python .httpx }
>>> import httpx
```
+```{ .python .ahttpx .hidden }
+>>> import ahttpx
+```
+
Now, let’s try to get a webpage.
-```python
+
+
+```{ .python .httpx }
>>> r = httpx.get('https://httpbin.org/get')
>>> r
```
+```{ .python .ahttpx .hidden }
+>>> r = await ahttpx.get('https://httpbin.org/get')
+>>> r
+
+```
+
To make an HTTP `POST` request, including some content...
-```python
+
+
+```{ .python .httpx }
>>> form = httpx.Form({'key': 'value'})
>>> r = httpx.post('https://httpbin.org/post', content=form)
```
+```{ .python .ahttpx .hidden }
+>>> form = httpx.Form({'key': 'value'})
+>>> r = await ahttpx.post('https://httpbin.org/post', content=form)
+```
+
Shortcut methods for `PUT`, `PATCH`, and `DELETE` requests follow the same style...
-```python
+
+
+```{ .python .httpx }
>>> r = httpx.put('https://httpbin.org/put', content=form)
>>> r = httpx.patch('https://httpbin.org/patch', content=form)
>>> r = httpx.delete('https://httpbin.org/delete')
```
+```{ .python .ahttpx .hidden }
+>>> r = await ahttpx.put('https://httpbin.org/put', content=form)
+>>> r = await ahttpx.patch('https://httpbin.org/patch', content=form)
+>>> r = await ahttpx.delete('https://httpbin.org/delete')
+```
+
## Passing Parameters in URLs
To include URL query parameters in the request, construct a URL using the `params` keyword...
-```python
+
+
+```{ .python .httpx }
>>> params = {'key1': 'value1', 'key2': 'value2'}
>>> url = httpx.URL('https://httpbin.org/get', params=params)
>>> r = httpx.get(url)
```
+```{ .python .ahttpx .hidden }
+>>> params = {'key1': 'value1', 'key2': 'value2'}
+>>> url = ahttpx.URL('https://httpbin.org/get', params=params)
+>>> r = await ahttpx.get(url)
+```
+
You can also pass a list of items as a value...
-```python
+
+
+```{ .python .httpx }
>>> params = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> url = httpx.URL('https://httpbin.org/get', params=params)
>>> r = httpx.get(url)
```
+```{ .python .ahttpx .hidden }
+>>> params = {'key1': 'value1', 'key2': ['value2', 'value3']}
+>>> url = ahttpx.URL('https://httpbin.org/get', params=params)
+>>> r = await ahttpx.get(url)
+```
+
## Custom Headers
To include additional headers in the outgoing request, use the `headers` keyword argument...
-```python
+
+
+```{ .python .httpx }
>>> url = 'https://httpbin.org/headers'
>>> headers = {'User-Agent': 'my-app/0.0.1'}
>>> r = httpx.get(url, headers=headers)
```
+```{ .python .ahttpx .hidden }
+>>> url = 'https://httpbin.org/headers'
+>>> headers = {'User-Agent': 'my-app/0.0.1'}
+>>> r = await ahttpx.get(url, headers=headers)
+```
+
---
## Response Content
HTTPX will automatically handle decoding the response content into unicode text.
-```python
+
+
+```{ .python .httpx }
>>> r = httpx.get('https://www.example.org/')
->>> r.text
+>>> r.text()
+'\n\n\nExample Domain...'
+```
+
+```{ .python .ahttpx .hidden }
+>>> r = await ahttpx.get('https://www.example.org/')
+>>> await r.text()
'\n\n\nExample Domain...'
```
@@ -79,8 +146,15 @@ HTTPX will automatically handle decoding the response content into unicode text.
The response content can also be accessed as bytes, for non-text responses.
-```python
->>> r.body
+
+
+```{ .python .httpx }
+>>> r.body()
+b'\n\n\nExample Domain...'
+```
+
+```{ .python .ahttpx .hidden }
+>>> await r.body()
b'\n\n\nExample Domain...'
```
@@ -88,19 +162,29 @@ b'\n\n\nExample Domain...'
Often Web API responses will be encoded as JSON.
-```python
+
+
+```{ .python .httpx }
>>> r = httpx.get('https://httpbin.org/get')
>>> r.json()
{'args': {}, 'headers': {'Host': 'httpbin.org', 'User-Agent': 'dev', 'X-Amzn-Trace-Id': 'Root=1-679814d5-0f3d46b26686f5013e117085'}, 'origin': '21.35.60.128', 'url': 'https://httpbin.org/get'}
```
+```{ .python .ahttpx .hidden }
+>>> r = await ahttpx.get('https://httpbin.org/get')
+>>> await r.json()
+{'args': {}, 'headers': {'Host': 'httpbin.org', 'User-Agent': 'dev', 'X-Amzn-Trace-Id': 'Root=1-679814d5-0f3d46b26686f5013e117085'}, 'origin': '21.35.60.128', 'url': 'https://httpbin.org/get'}
+```
+
---
## Sending Form Encoded Data
Some types of HTTP requests, such as `POST` and `PUT` requests, can include data in the request body. One common way of including that is as form-encoded data, which is used for HTML forms.
-```python
+
+
+```{ .python .httpx }
>>> form = httpx.Form({'key1': 'value1', 'key2': 'value2'})
>>> r = httpx.post("https://httpbin.org/post", content=form)
>>> r.json()
@@ -114,9 +198,25 @@ Some types of HTTP requests, such as `POST` and `PUT` requests, can include data
}
```
+```{ .python .ahttpx .hidden }
+>>> form = ahttpx.Form({'key1': 'value1', 'key2': 'value2'})
+>>> r = await ahttpx.post("https://httpbin.org/post", content=form)
+>>> await r.json()
+{
+ ...
+ "form": {
+ "key2": "value2",
+ "key1": "value1"
+ },
+ ...
+}
+```
+
Form encoded data can also include multiple values from a given key.
-```python
+
+
+```{ .python .httpx }
>>> form = httpx.Form({'key1': ['value1', 'value2']})
>>> r = httpx.post("https://httpbin.org/post", content=form)
>>> r.json()
@@ -132,11 +232,29 @@ Form encoded data can also include multiple values from a given key.
}
```
+```{ .python .ahttpx .hidden }
+>>> form = ahttpx.Form({'key1': ['value1', 'value2']})
+>>> r = await ahttpx.post("https://httpbin.org/post", content=form)
+>>> await r.json()
+{
+ ...
+ "form": {
+ "key1": [
+ "value1",
+ "value2"
+ ]
+ },
+ ...
+}
+```
+
## Sending Multipart File Uploads
You can also upload files, using HTTP multipart encoding.
-```python
+
+
+```{ .python .httpx }
>>> files = httpx.Files({'upload': httpx.File('uploads/report.xls')})
>>> r = httpx.post("https://httpbin.org/post", content=files)
>>> r.json()
@@ -149,9 +267,24 @@ You can also upload files, using HTTP multipart encoding.
}
```
+```{ .python .ahttpx .hidden }
+>>> files = ahttpx.Files({'upload': httpx.File('uploads/report.xls')})
+>>> r = await ahttpx.post("https://httpbin.org/post", content=files)
+>>> await r.json()
+{
+ ...
+ "files": {
+ "upload": "<... binary content ...>"
+ },
+ ...
+}
+```
+
If you need to include non-file data fields in the multipart form, use the `data=...` parameter:
-```python
+
+
+```{ .python .httpx }
>>> form = {'message': 'Hello, world!'}
>>> files = {'upload': httpx.File('uploads/report.xls')}
>>> data = httpx.MultiPart(form=form, files=files)
@@ -169,12 +302,32 @@ If you need to include non-file data fields in the multipart form, use the `data
}
```
+```{ .python .ahttpx .hidden }
+>>> form = {'message': 'Hello, world!'}
+>>> files = {'upload': httpx.File('uploads/report.xls')}
+>>> data = ahttpx.MultiPart(form=form, files=files)
+>>> r = await ahttpx.post("https://httpbin.org/post", content=data)
+>>> await r.json()
+{
+ ...
+ "files": {
+ "upload": "<... binary content ...>"
+ },
+ "form": {
+ "message": "Hello, world!",
+ },
+ ...
+}
+```
+
## Sending JSON Encoded Data
Form encoded data is okay if all you need is a simple key-value data structure.
For more complicated data structures you'll often want to use JSON encoding instead.
-```python
+
+
+```{ .python .httpx }
>>> data = {'integer': 123, 'boolean': True, 'list': ['a', 'b', 'c']}
>>> r = httpx.post("https://httpbin.org/post", content=httpx.JSON(data))
>>> r.json()
@@ -193,16 +346,42 @@ For more complicated data structures you'll often want to use JSON encoding inst
}
```
+```{ .python .ahttpx .hidden }
+>>> data = {'integer': 123, 'boolean': True, 'list': ['a', 'b', 'c']}
+>>> r = await ahttpx.post("https://httpbin.org/post", content=httpx.JSON(data))
+>>> await r.json()
+{
+ ...
+ "json": {
+ "boolean": true,
+ "integer": 123,
+ "list": [
+ "a",
+ "b",
+ "c"
+ ]
+ },
+ ...
+}
+```
+
## Sending Binary Request Data
For other encodings, you should use the `content=...` parameter, passing
either a `bytes` type or a generator that yields `bytes`.
-```python
+
+
+```{ .python .httpx }
>>> content = b'Hello, world'
>>> r = httpx.post("https://httpbin.org/post", content=content)
```
+```{ .python .ahttpx .hidden }
+>>> content = b'Hello, world'
+>>> r = await ahttpx.post("https://httpbin.org/post", content=content)
+```
+
You may also want to set a custom `Content-Type` header when uploading
binary data.
@@ -212,9 +391,17 @@ binary data.
We can inspect the HTTP status code of the response:
-```python
+
+
+```{ .python .httpx }
>>> r = httpx.get('https://httpbin.org/get')
->>> r.code
+>>> r.status_code
+200
+```
+
+```{ .python .ahttpx .hidden }
+>>> r = await ahttpx.get('https://httpbin.org/get')
+>>> r.status_code
200
```
@@ -222,7 +409,21 @@ We can inspect the HTTP status code of the response:
The response headers are available as a dictionary-like interface.
-```python
+
+
+```{ .python .httpx }
+>>> r.headers
+
+```
+
+```{ .python .ahttpx .hidden }
>>> r.headers
httpx ahttpx
+
+```{ .python .httpx }
+>>> r.headers.get('Content-Type')
+'application/json'
+
+>>> r.headers.get('content-type')
+'application/json'
+```
+
+```{ .python .ahttpx .hidden }
>>> r.headers.get('Content-Type')
'application/json'
@@ -252,12 +463,20 @@ For large downloads you may want to use streaming responses that do not load the
You can stream the binary content of the response...
-```python
+
+
+```{ .python .httpx }
>>> with httpx.stream("GET", "https://www.example.com") as r:
... for data in r.stream:
... print(data)
```
+```{ .python .ahttpx .hidden }
+>>> async with ahttpx.stream("GET", "https://www.example.com") as r:
+... async for data in r.stream:
+... print(data)
+```
+
---
← [Home](index.md)
diff --git a/docs/requests.md b/docs/requests.md
index 16f6ae3..23c66b1 100644
--- a/docs/requests.md
+++ b/docs/requests.md
@@ -2,7 +2,9 @@
The core elements of an HTTP request are the `method`, `url`, `headers` and `body`.
-```python
+
+
+```{ .python .httpx }
>>> req = httpx.Request('GET', 'https://www.example.com/')
>>> req
@@ -12,7 +14,21 @@ The core elements of an HTTP request are the `method`, `url`, `headers` and `bod
>>> req.headers
->>> req.body
+>>> req.body()
+b''
+```
+
+```{ .python .ahttpx .hidden }
+>>> req = ahttpx.Request('GET', 'https://www.example.com/')
+>>> req
+
+>>> req.method
+'GET'
+>>> req.url
+
+>>> req.headers
+
+>>> await req.body()
b''
```
@@ -29,33 +45,66 @@ The following headers have automatic behavior with `Requests` instances...
Including binary data directly...
-```python
+
+
+```{ .python .httpx }
>>> headers = {'Content-Type': 'application/json'}
>>> content = json.dumps(...)
>>> httpx.Request('POST', 'https://echo.encode.io/', content=content)
```
+```{ .python .ahttpx .hidden }
+>>> headers = {'Content-Type': 'application/json'}
+>>> content = json.dumps(...)
+>>> ahttpx.Request('POST', 'https://echo.encode.io/', content=content)
+```
+
## Working with content types
Including JSON request content...
-```python
+
+
+```{ .python .httpx }
>>> data = httpx.JSON(...)
>>> httpx.Request('POST', 'https://echo.encode.io/', content=data)
```
+```{ .python .ahttpx .hidden }
+>>> data = ahttpx.JSON(...)
+>>> ahttpx.Request('POST', 'https://echo.encode.io/', content=data)
+```
+
Including form encoded request content...
-```python
+
+
+```{ .python .httpx }
>>> data = httpx.Form(...)
>>> httpx.Request('PUT', 'https://echo.encode.io/', content=data)
```
+```{ .python .ahttpx .hidden }
+>>> data = ahttpx.Form(...)
+>>> ahttpx.Request('PUT', 'https://echo.encode.io/', content=data)
+```
+
Including multipart file uploads...
-```python
+
+
+```{ .python .httpx }
>>> form = httpx.MultiPart(form={...}, files={...})
->>> with Request('POST', 'https://echo.encode.io/', content=form) as req:
+>>> with httpx.Request('POST', 'https://echo.encode.io/', content=form) as req:
+>>> req.headers
+{...}
+>>> req.stream
+
+```
+
+```{ .python .ahttpx .hidden }
+>>> form = ahttpx.MultiPart(form={...}, files={...})
+>>> async with ahttpx.Request('POST', 'https://echo.encode.io/', content=form) as req:
>>> req.headers
{...}
>>> req.stream
@@ -64,9 +113,20 @@ Including multipart file uploads...
Including direct file uploads...
-```python
+
+
+```{ .python .httpx }
>>> file = httpx.File('upload.json')
->>> with Request('POST', 'https://echo.encode.io/', content=file) as req:
+>>> with httpx.Request('POST', 'https://echo.encode.io/', content=file) as req:
+>>> req.headers
+{...}
+>>> req.stream
+
+```
+
+```{ .python .ahttpx .hidden }
+>>> file = ahttpx.File('upload.json')
+>>> async with ahttpx.Request('POST', 'https://echo.encode.io/', content=file) as req:
>>> req.headers
{...}
>>> req.stream
@@ -77,22 +137,40 @@ Including direct file uploads...
...
-```python
+
+
+```{ .python .httpx }
>>> data = request.json()
```
+```{ .python .ahttpx .hidden }
+>>> data = await request.json()
+```
+
...
-```python
+
+
+```{ .python .httpx }
>>> form = request.form()
```
+```{ .python .ahttpx .hidden }
+>>> form = await request.form()
+```
+
...
-```python
+
+
+```{ .python .httpx }
>>> files = request.files()
```
+```{ .python .ahttpx .hidden }
+>>> files = await request.files()
+```
+
---
← [Servers](servers.md)
diff --git a/docs/responses.md b/docs/responses.md
index cda0fb0..ba702f4 100644
--- a/docs/responses.md
+++ b/docs/responses.md
@@ -2,7 +2,9 @@
The core elements of an HTTP response are the `status_code`, `headers` and `body`.
-```python
+
+
+```{ .python .httpx }
>>> resp = httpx.Response(200, headers={'Content-Type': 'text/plain'}, content=b'hello, world')
>>> resp
@@ -10,7 +12,19 @@ The core elements of an HTTP response are the `status_code`, `headers` and `body
200
>>> resp.headers
->>> resp.body
+>>> resp.body()
+b'hello, world'
+```
+
+```{ .python .ahttpx .hidden }
+>>> resp = ahttpx.Response(200, headers={'Content-Type': 'text/plain'}, content=b'hello, world')
+>>> resp
+
+>>> resp.status_code
+200
+>>> resp.headers
+
+>>> await resp.body()
b'hello, world'
```
@@ -26,53 +40,102 @@ The following headers have automatic behavior with `Response` instances...
Including HTML content...
-```python
+
+
+```{ .python .httpx }
>>> content = httpx.HTML('......')
>>> response = httpx.Response(200, content=content)
```
+```{ .python .ahttpx .hidden }
+>>> content = ahttpx.HTML('......')
+>>> response = ahttpx.Response(200, content=content)
+```
+
Including plain text content...
-```python
+
+
+```{ .python .httpx }
>>> content = httpx.Text('hello, world')
>>> response = httpx.Response(200, content=content)
```
+```{ .python .ahttpx .hidden }
+>>> content = ahttpx.Text('hello, world')
+>>> response = ahttpx.Response(200, content=content)
+```
+
Including JSON data...
-```python
+
+
+```{ .python .httpx }
>>> content = httpx.JSON({'message': 'hello, world'})
>>> response = httpx.Response(200, content=content)
```
+```{ .python .ahttpx .hidden }
+>>> content = ahttpx.JSON({'message': 'hello, world'})
+>>> response = ahttpx.Response(200, content=content)
+```
+
Including content from a file...
-```python
+
+
+```{ .python .httpx }
>>> content = httpx.File('index.html')
->>> response = httpx.Response(200, content=content)
+>>> with httpx.Response(200, content=content) as response:
+... pass
+```
+
+```{ .python .ahttpx .hidden }
+>>> content = ahttpx.File('index.html')
+>>> async with ahttpx.Response(200, content=content) as response:
+... pass
```
## Accessing response content
...
+
+
+```{ .python .httpx }
+>>> content = response.body()
```
->>> content = response.body
+
+```{ .python .ahttpx .hidden }
+>>> content = await response.body()
```
...
-```
+
+
+```{ .python .httpx }
>>> text = response.text()
...
```
+```{ .python .ahttpx .hidden }
+>>> text = await response.text()
...
-
```
+
+...
+
+
+
+```{ .python .httpx }
>>> data = response.json()
```
+```{ .python .ahttpx .hidden }
+>>> data = await response.json()
+```
+
---
← [Requests](requests.md)
diff --git a/docs/servers.md b/docs/servers.md
index 54d503f..ab38959 100644
--- a/docs/servers.md
+++ b/docs/servers.md
@@ -5,7 +5,9 @@ This gives you a lightweight way to build web applications or APIs.
### `serve_http(endpoint, listeners=None)`
-```python
+
+
+```{ .python .httpx }
>>> def hello_world(request):
... content = httpx.HTML('hello, world.')
... return httpx.Response(code=200, content=content)
@@ -16,6 +18,17 @@ This gives you a lightweight way to build web applications or APIs.
Serving on http://127.0.0.1:8080/ (Press CTRL+C to quit)
```
+```{ .python .ahttpx .hidden }
+>>> async def hello_world(request):
+... content = httpx.HTML('hello, world.')
+... return httpx.Response(code=200, content=content)
+
+>>> async with httpx.serve_http(hello_world) as server:
+... print(f"Serving on {server.url} (Press CTRL+C to quit)")
+... await server.wait()
+Serving on http://127.0.0.1:8080/ (Press CTRL+C to quit)
+```
+
## HTTPServer
* `.wait()`
diff --git a/docs/urls.md b/docs/urls.md
index 6bd0924..ef56b18 100644
--- a/docs/urls.md
+++ b/docs/urls.md
@@ -2,33 +2,66 @@
The `URL` class handles URL validation and parsing.
-```python
+
+
+```{ .python .httpx }
>>> url = httpx.URL('https://www.example.com/')
>>> url
```
+```{ .python .ahttpx .hidden }
+>>> url = ahttpx.URL('https://www.example.com/')
+>>> url
+
+```
+
URL components are normalised, following the same rules as internet browsers.
-```python
+
+
+```{ .python .httpx }
>>> url = httpx.URL('https://www.EXAMPLE.com:443/path/../main')
>>> url
```
+```{ .python .ahttpx .hidden }
+>>> url = ahttpx.URL('https://www.EXAMPLE.com:443/path/../main')
+>>> url
+
+```
+
Both absolute and relative URLs are valid.
-```python
+
+
+```{ .python .httpx }
>>> url = httpx.URL('/README.md')
>>> url
```
+```{ .python .ahttpx .hidden }
+>>> url = ahttpx.URL('/README.md')
+>>> url
+
+```
+
Coercing a URL to a `str` will always result in a printable ASCII string.
-```python
->>> httpx.URL('https://example.com/path to here?search=🦋')
-
+
+
+```{ .python .httpx }
+>>> url = httpx.URL('https://example.com/path to here?search=🦋')
+>>> str(url)
+'https://example.com/path%20to%20here?search=%F0%9F%A6%8B'
+```
+
+```{ .python .ahttpx .hidden }
+>>> url = ahttpx.URL('https://example.com/path to here?search=🦋')
+>>> str(url)
+'https://example.com/path%20to%20here?search=%F0%9F%A6%8B'
```
### URL components
@@ -53,18 +86,32 @@ A parsed representation of the query parameters is accessible with the `.params`
URLs can be instantiated from their components...
-```python
+
+
+```{ .python .httpx }
>>> httpx.URL(scheme="https", host="example.com", path="/")
```
+```{ .python .ahttpx .hidden }
+>>> ahttpx.URL(scheme="https", host="example.com", path="/")
+
+```
+
Or using both the string form and query parameters...
-```python
+
+
+```{ .python .httpx }
>>> httpx.URL("https://example.com/", params={"search": "some text"})
```
+```{ .python .ahttpx .hidden }
+>>> ahttpx.URL("https://example.com/", params={"search": "some text"})
+
+```
+
### Modifying URLs
Instances of `URL` are immutable, meaning their value cannot be changed. Instead new modified instances may be created.
@@ -84,44 +131,84 @@ The `QueryParams` class provides an immutable multi-dict for accessing URL query
They can be instantiated from a dictionary.
-```python
+
+
+```{ .python .httpx }
>>> params = httpx.QueryParams({"color": "black", "size": "medium"})
>>> params
```
+```{ .python .ahttpx .hidden }
+>>> params = ahttpx.QueryParams({"color": "black", "size": "medium"})
+>>> params
+
+```
+
Multiple values for a single key are valid.
-```python
+
+
+```{ .python .httpx }
>>> params = httpx.QueryParams({"filter": ["60GHz", "75GHz", "100GHz"]})
>>> params
```
+```{ .python .ahttpx .hidden }
+>>> params = ahttpx.QueryParams({"filter": ["60GHz", "75GHz", "100GHz"]})
+>>> params
+
+```
+
They can also be instantiated directly from a query string.
-```python
+
+
+```{ .python .httpx }
>>> params = httpx.QueryParams("color=black&size=medium")
>>> params
```
+```{ .python .ahttpx .hidden }
+>>> params = ahttpx.QueryParams("color=black&size=medium")
+>>> params
+
+```
+
Keys and values are always represented as strings.
-```python
+
+
+```{ .python .httpx }
>>> params = httpx.QueryParams("sort_by=published&author=natalie")
>>> params["sort_by"]
'published'
```
+```{ .python .ahttpx .hidden }
+>>> params = ahttpx.QueryParams("sort_by=published&author=natalie")
+>>> params["sort_by"]
+'published'
+```
+
When coercing query parameters to strings you'll see the same escaping behavior as HTML form submissions. The result will always be a printable ASCII string.
-```python
+
+
+```{ .python .httpx }
>>> params = httpx.QueryParams({"email": "user@example.com", "search": "How HTTP works!"})
>>> str(params)
'email=user%40example.com&search=How+HTTP+works%21'
```
+```{ .python .ahttpx .hidden }
+>>> params = ahttpx.QueryParams({"email": "user@example.com", "search": "How HTTP works!"})
+>>> str(params)
+'email=user%40example.com&search=How+HTTP+works%21'
+```
+
### Accessing query parameters
Query parameters are accessed using a standard dictionary style interface...
diff --git a/src/ahttpx/_client.py b/src/ahttpx/_client.py
index 5f61c4f..78bf544 100644
--- a/src/ahttpx/_client.py
+++ b/src/ahttpx/_client.py
@@ -132,7 +132,7 @@ def __init__(self, transport: Transport) -> None:
def is_redirect(self, response: Response) -> bool:
return (
- response.code in (301, 302, 303, 307, 308)
+ response.status_code in (301, 302, 303, 307, 308)
and "Location" in response.headers
)
diff --git a/src/ahttpx/_headers.py b/src/ahttpx/_headers.py
index a22ea5b..a6d9f74 100644
--- a/src/ahttpx/_headers.py
+++ b/src/ahttpx/_headers.py
@@ -1,3 +1,4 @@
+import re
import typing
@@ -197,44 +198,46 @@ def __repr__(self) -> str:
return f""
-# def parse_content_type(header: str) -> tuple[str, dict[str, str]]:
-# # The Content-Type header is described in RFC 2616 'Content-Type'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-14.17
-#
-# # The 'type/subtype; parameter' format is described in RFC 2616 'Media Types'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-3.7
-#
-# # Parameter quoting is described in RFC 2616 'Transfer Codings'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-3.6
-#
-# header = header.strip()
-# content_type = ''
-# params = {}
-#
-# # Match the content type (up to the first semicolon or end)
-# match = re.match(r'^([^;]+)', header)
-# if match:
-# content_type = match.group(1).strip().lower()
-# rest = header[match.end():]
-# else:
-# return '', {}
-#
-# # Parse parameters, accounting for quoted strings
-# param_pattern = re.compile(r'''
-# ;\s* # Semicolon + optional whitespace
-# (?P[^=;\s]+) # Parameter key
-# = # Equal sign
-# (?P # Parameter value:
-# "(?:[^"\\]|\\.)*" # Quoted string with escapes
-# | # OR
-# [^;]* # Unquoted string (until semicolon)
-# )
-# ''', re.VERBOSE)
-#
-# for match in param_pattern.finditer(rest):
-# key = match.group('key').lower()
-# value = match.group('value').strip()
-# if value.startswith('"') and value.endswith('"'):
-# # Remove surrounding quotes and unescape
-# value = re.sub(r'\\(.)', r'\1', value[1:-1])
-# params[key] = value
\ No newline at end of file
+def parse_opts_header(header: str) -> tuple[str, dict[str, str]]:
+ # The Content-Type header is described in RFC 2616 'Content-Type'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-14.17
+
+ # The 'type/subtype; parameter' format is described in RFC 2616 'Media Types'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-3.7
+
+ # Parameter quoting is described in RFC 2616 'Transfer Codings'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-3.6
+
+ header = header.strip()
+ content_type = ''
+ params = {}
+
+ # Match the content type (up to the first semicolon or end)
+ match = re.match(r'^([^;]+)', header)
+ if match:
+ content_type = match.group(1).strip().lower()
+ rest = header[match.end():]
+ else:
+ return '', {}
+
+ # Parse parameters, accounting for quoted strings
+ param_pattern = re.compile(r'''
+ ;\s* # Semicolon + optional whitespace
+ (?P[^=;\s]+) # Parameter key
+ = # Equal sign
+ (?P # Parameter value:
+ "(?:[^"\\]|\\.)*" # Quoted string with escapes
+ | # OR
+ [^;]* # Unquoted string (until semicolon)
+ )
+ ''', re.VERBOSE)
+
+ for match in param_pattern.finditer(rest):
+ key = match.group('key').lower()
+ value = match.group('value').strip()
+ if value.startswith('"') and value.endswith('"'):
+ # Remove surrounding quotes and unescape
+ value = re.sub(r'\\(.)', r'\1', value[1:-1])
+ params[key] = value
+
+ return content_type, params
diff --git a/src/ahttpx/_response.py b/src/ahttpx/_response.py
index caa8326..be3dc10 100644
--- a/src/ahttpx/_response.py
+++ b/src/ahttpx/_response.py
@@ -2,7 +2,7 @@
from ._content import Content
from ._streams import ByteStream, Stream
-from ._headers import Headers
+from ._headers import Headers, parse_opts_header
__all__ = ["Response"]
@@ -78,12 +78,12 @@
class Response:
def __init__(
self,
- code: int,
+ status_code: int,
*,
headers: Headers | typing.Mapping[str, str] | None = None,
content: Content | Stream | bytes | None = None,
):
- self.code = code
+ self.status_code = status_code
self.headers = Headers(headers)
self.stream : Stream = ByteStream(b"")
@@ -106,7 +106,7 @@ def __init__(
# All 1xx (informational), 204 (no content), and 304 (not modified) responses
# MUST NOT include a message-body. All other responses do include a
# message-body, although it MAY be of zero length.
- if code >= 200 and code != 204 and code != 304:
+ if status_code >= 200 and status_code != 204 and status_code != 304:
content_length: int | None = self.stream.size
if content_length is None:
self.headers = self.headers.copy_set("Transfer-Encoding", "chunked")
@@ -115,11 +115,19 @@ def __init__(
@property
def reason_phrase(self):
- return _codes.get(self.code, "Unknown Status Code")
+ return _codes.get(self.status_code, "Unknown Status Code")
async def read(self):
self.body = b"".join([part async for part in self.stream])
self.stream = ByteStream(self.body)
+ @property
+ def text(self) -> str:
+ default_charset = 'utf-8'
+ header = self.headers.get('Content-Type', '')
+ _, opts = parse_opts_header(header)
+ charset = opts.get('charset', default_charset)
+ return self.body.decode(charset)
+
def __repr__(self):
- return f""
+ return f""
diff --git a/src/ahttpx/_server.py b/src/ahttpx/_server.py
index edafaef..39db319 100644
--- a/src/ahttpx/_server.py
+++ b/src/ahttpx/_server.py
@@ -51,7 +51,7 @@ async def handle_requests(self):
except Exception as exc:
logger.error("Internal Server Error", exc_info=True)
finally:
- status_line = f"{request.method} {request.url.target} [{response.code} {response.reason_phrase}]"
+ status_line = f"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]"
logger.info(status_line)
except ConnectionClosed:
pass
@@ -101,7 +101,7 @@ async def _recv_event(self) -> h11.Event | type[h11.PAUSED]:
# Return the response...
async def _send_head(self, response: Response):
event = h11.Response(
- status_code=response.code,
+ status_code=response.status_code,
headers=list(response.headers.items())
)
await self._send_event(event)
diff --git a/src/httpx/_client.py b/src/httpx/_client.py
index 7dcfd8d..56ac9b5 100644
--- a/src/httpx/_client.py
+++ b/src/httpx/_client.py
@@ -132,7 +132,7 @@ def __init__(self, transport: Transport) -> None:
def is_redirect(self, response: Response) -> bool:
return (
- response.code in (301, 302, 303, 307, 308)
+ response.status_code in (301, 302, 303, 307, 308)
and "Location" in response.headers
)
diff --git a/src/httpx/_headers.py b/src/httpx/_headers.py
index a22ea5b..a6d9f74 100644
--- a/src/httpx/_headers.py
+++ b/src/httpx/_headers.py
@@ -1,3 +1,4 @@
+import re
import typing
@@ -197,44 +198,46 @@ def __repr__(self) -> str:
return f""
-# def parse_content_type(header: str) -> tuple[str, dict[str, str]]:
-# # The Content-Type header is described in RFC 2616 'Content-Type'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-14.17
-#
-# # The 'type/subtype; parameter' format is described in RFC 2616 'Media Types'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-3.7
-#
-# # Parameter quoting is described in RFC 2616 'Transfer Codings'
-# # https://datatracker.ietf.org/doc/html/rfc2616#section-3.6
-#
-# header = header.strip()
-# content_type = ''
-# params = {}
-#
-# # Match the content type (up to the first semicolon or end)
-# match = re.match(r'^([^;]+)', header)
-# if match:
-# content_type = match.group(1).strip().lower()
-# rest = header[match.end():]
-# else:
-# return '', {}
-#
-# # Parse parameters, accounting for quoted strings
-# param_pattern = re.compile(r'''
-# ;\s* # Semicolon + optional whitespace
-# (?P[^=;\s]+) # Parameter key
-# = # Equal sign
-# (?P # Parameter value:
-# "(?:[^"\\]|\\.)*" # Quoted string with escapes
-# | # OR
-# [^;]* # Unquoted string (until semicolon)
-# )
-# ''', re.VERBOSE)
-#
-# for match in param_pattern.finditer(rest):
-# key = match.group('key').lower()
-# value = match.group('value').strip()
-# if value.startswith('"') and value.endswith('"'):
-# # Remove surrounding quotes and unescape
-# value = re.sub(r'\\(.)', r'\1', value[1:-1])
-# params[key] = value
\ No newline at end of file
+def parse_opts_header(header: str) -> tuple[str, dict[str, str]]:
+ # The Content-Type header is described in RFC 2616 'Content-Type'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-14.17
+
+ # The 'type/subtype; parameter' format is described in RFC 2616 'Media Types'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-3.7
+
+ # Parameter quoting is described in RFC 2616 'Transfer Codings'
+ # https://datatracker.ietf.org/doc/html/rfc2616#section-3.6
+
+ header = header.strip()
+ content_type = ''
+ params = {}
+
+ # Match the content type (up to the first semicolon or end)
+ match = re.match(r'^([^;]+)', header)
+ if match:
+ content_type = match.group(1).strip().lower()
+ rest = header[match.end():]
+ else:
+ return '', {}
+
+ # Parse parameters, accounting for quoted strings
+ param_pattern = re.compile(r'''
+ ;\s* # Semicolon + optional whitespace
+ (?P[^=;\s]+) # Parameter key
+ = # Equal sign
+ (?P # Parameter value:
+ "(?:[^"\\]|\\.)*" # Quoted string with escapes
+ | # OR
+ [^;]* # Unquoted string (until semicolon)
+ )
+ ''', re.VERBOSE)
+
+ for match in param_pattern.finditer(rest):
+ key = match.group('key').lower()
+ value = match.group('value').strip()
+ if value.startswith('"') and value.endswith('"'):
+ # Remove surrounding quotes and unescape
+ value = re.sub(r'\\(.)', r'\1', value[1:-1])
+ params[key] = value
+
+ return content_type, params
diff --git a/src/httpx/_response.py b/src/httpx/_response.py
index 28e7d1e..d1f9ba2 100644
--- a/src/httpx/_response.py
+++ b/src/httpx/_response.py
@@ -2,7 +2,7 @@
from ._content import Content
from ._streams import ByteStream, Stream
-from ._headers import Headers
+from ._headers import Headers, parse_opts_header
__all__ = ["Response"]
@@ -78,12 +78,12 @@
class Response:
def __init__(
self,
- code: int,
+ status_code: int,
*,
headers: Headers | typing.Mapping[str, str] | None = None,
content: Content | Stream | bytes | None = None,
):
- self.code = code
+ self.status_code = status_code
self.headers = Headers(headers)
self.stream : Stream = ByteStream(b"")
@@ -106,7 +106,7 @@ def __init__(
# All 1xx (informational), 204 (no content), and 304 (not modified) responses
# MUST NOT include a message-body. All other responses do include a
# message-body, although it MAY be of zero length.
- if code >= 200 and code != 204 and code != 304:
+ if status_code >= 200 and status_code != 204 and status_code != 304:
content_length: int | None = self.stream.size
if content_length is None:
self.headers = self.headers.copy_set("Transfer-Encoding", "chunked")
@@ -115,11 +115,19 @@ def __init__(
@property
def reason_phrase(self):
- return _codes.get(self.code, "Unknown Status Code")
+ return _codes.get(self.status_code, "Unknown Status Code")
def read(self):
self.body = b"".join([part for part in self.stream])
self.stream = ByteStream(self.body)
+ @property
+ def text(self) -> str:
+ default_charset = 'utf-8'
+ header = self.headers.get('Content-Type', '')
+ _, opts = parse_opts_header(header)
+ charset = opts.get('charset', default_charset)
+ return self.body.decode(charset)
+
def __repr__(self):
- return f""
+ return f""
diff --git a/src/httpx/_server.py b/src/httpx/_server.py
index 543829e..6f124d4 100644
--- a/src/httpx/_server.py
+++ b/src/httpx/_server.py
@@ -51,7 +51,7 @@ def handle_requests(self):
except Exception as exc:
logger.error("Internal Server Error", exc_info=True)
finally:
- status_line = f"{request.method} {request.url.target} [{response.code} {response.reason_phrase}]"
+ status_line = f"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]"
logger.info(status_line)
except ConnectionClosed:
pass
@@ -101,7 +101,7 @@ def _recv_event(self) -> h11.Event | type[h11.PAUSED]:
# Return the response...
def _send_head(self, response: Response):
event = h11.Response(
- status_code=response.code,
+ status_code=response.status_code,
headers=list(response.headers.items())
)
self._send_event(event)
@@ -147,7 +147,7 @@ def handler(stream):
)
backend = NetworkBackend()
- with backend.serve("127.0.0.1", 8080, handler) as server:
+ with backend.serve("127.0.0.1", 8081, handler) as server:
server = HTTPServer(server.host, server.port)
logger.info(f"Serving on {server.url}")
yield server
diff --git a/tests/test_client.py b/tests/test_client.py
index 6c84e8d..11dbbea 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -33,19 +33,20 @@ def test_client(client):
def test_get(client, server):
r = client.get(server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'GET',
'query-params': {},
'content-type': None,
'json': None,
}
+ assert r.text == '{"method":"GET","query-params":{},"content-type":null,"json":null}'
def test_post(client, server):
data = httpx.JSON({"data": 123})
r = client.post(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'POST',
'query-params': {},
@@ -57,7 +58,7 @@ def test_post(client, server):
def test_put(client, server):
data = httpx.JSON({"data": 123})
r = client.put(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'PUT',
'query-params': {},
@@ -69,7 +70,7 @@ def test_put(client, server):
def test_patch(client, server):
data = httpx.JSON({"data": 123})
r = client.patch(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'PATCH',
'query-params': {},
@@ -80,7 +81,7 @@ def test_patch(client, server):
def test_delete(client, server):
r = client.delete(server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'DELETE',
'query-params': {},
@@ -91,7 +92,7 @@ def test_delete(client, server):
def test_request(client, server):
r = client.request("GET", server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'GET',
'query-params': {},
@@ -102,7 +103,7 @@ def test_request(client, server):
def test_stream(client, server):
with client.stream("GET", server.url) as r:
- assert r.code == 200
+ assert r.status_code == 200
r.read()
assert json.loads(r.body) == {
'method': 'GET',
@@ -112,6 +113,6 @@ def test_stream(client, server):
}
-def test_get(client):
+def test_get_with_invalid_scheme(client):
with pytest.raises(ValueError):
client.get("nope://www.example.com")
diff --git a/tests/test_pool.py b/tests/test_pool.py
index 175c78c..d8effc5 100644
--- a/tests/test_pool.py
+++ b/tests/test_pool.py
@@ -20,7 +20,7 @@ def test_connection_pool_request(server):
r = pool.request("GET", server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert repr(pool) == ""
assert len(pool.connections) == 1
@@ -32,7 +32,7 @@ def test_connection_pool_connection_close(server):
r = pool.request("GET", server.url, headers={"Connection": "close"})
- assert r.code == 200
+ assert r.status_code == 200
assert repr(pool) == ""
assert len(pool.connections) == 0
@@ -43,7 +43,7 @@ def test_connection_pool_stream(server):
assert len(pool.connections) == 0
with pool.stream("GET", server.url) as r:
- assert r.code == 200
+ assert r.status_code == 200
assert repr(pool) == ""
assert len(pool.connections) == 1
r.read()
@@ -72,7 +72,7 @@ def test_connection_request(server):
r = conn.request("GET", "/")
- assert r.code == 200
+ assert r.status_code == 200
assert repr(conn) == f""
@@ -80,7 +80,7 @@ def test_connection_stream(server):
with httpx.open_connection(server.url) as conn:
assert repr(conn) == f""
with conn.stream("GET", "/") as r:
- assert r.code == 200
+ assert r.status_code == 200
assert repr(conn) == f""
r.read()
assert repr(conn) == f""
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 123ae86..7321213 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -23,7 +23,7 @@ def server():
def test_get(server):
r = httpx.get(server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'GET',
'query-params': {},
@@ -35,7 +35,7 @@ def test_get(server):
def test_post(server):
data = httpx.JSON({"data": 123})
r = httpx.post(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'POST',
'query-params': {},
@@ -47,7 +47,7 @@ def test_post(server):
def test_put(server):
data = httpx.JSON({"data": 123})
r = httpx.put(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'PUT',
'query-params': {},
@@ -59,7 +59,7 @@ def test_put(server):
def test_patch(server):
data = httpx.JSON({"data": 123})
r = httpx.patch(server.url, content=data)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'PATCH',
'query-params': {},
@@ -70,7 +70,7 @@ def test_patch(server):
def test_delete(server):
r = httpx.delete(server.url)
- assert r.code == 200
+ assert r.status_code == 200
assert json.loads(r.body) == {
'method': 'DELETE',
'query-params': {},
diff --git a/tests/test_response.py b/tests/test_response.py
index 271a49b..2db331c 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -10,7 +10,7 @@ def test_response():
r = httpx.Response(200)
assert repr(r) == ""
- assert r.code == 200
+ assert r.status_code == 200
assert r.headers == {'Content-Length': '0'}
assert r.stream == httpx.ByteStream(b"")
@@ -19,7 +19,7 @@ def test_response_204():
r = httpx.Response(204)
assert repr(r) == ""
- assert r.code == 204
+ assert r.status_code == 204
assert r.headers == {}
assert r.stream == httpx.ByteStream(b"")