Skip to content

Commit 9c8cd81

Browse files
authored
Merge branch 'encode:master' into drop-python37-support
2 parents b1da3e3 + e99e294 commit 9c8cd81

File tree

10 files changed

+66
-20
lines changed

10 files changed

+66
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1313
### Added
1414

1515
* Add `socket_options` argument to `httpx.HTTPTransport` and `httpx.AsyncHTTPTransport` classes. (#2716)
16+
* The `Response.raise_for_status()` method now returns the response instance. For example: `data = httpx.get('...').raise_for_status().json()`. (#2776)
1617

1718
### Fixed
1819

docs/advanced.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,38 @@ with tempfile.NamedTemporaryFile() as download_file:
426426

427427
![rich progress bar](img/rich-progress.gif)
428428

429+
## Monitoring upload progress
430+
431+
If you need to monitor upload progress of large responses, you can use request content generator streaming.
432+
433+
For example, showing a progress bar using the [`tqdm`](https://github.com/tqdm/tqdm) library.
434+
435+
```python
436+
import io
437+
import random
438+
439+
import httpx
440+
from tqdm import tqdm
441+
442+
443+
def gen():
444+
"""
445+
this is a complete example with generated random bytes.
446+
you can replace `io.BytesIO` with real file object.
447+
"""
448+
total = 32 * 1024 * 1024 # 32m
449+
with tqdm(ascii=True, unit_scale=True, unit='B', unit_divisor=1024, total=total) as bar:
450+
with io.BytesIO(random.randbytes(total)) as f:
451+
while data := f.read(1024):
452+
yield data
453+
bar.update(len(data))
454+
455+
456+
httpx.post("https://httpbin.org/post", content=gen())
457+
```
458+
459+
![tqdm progress bar](img/tqdm-progress.gif)
460+
429461
## .netrc Support
430462

431463
HTTPX can be configured to use [a `.netrc` config file](https://everything.curl.dev/usingcurl/netrc) for authentication.

docs/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
* The amount of time elapsed between sending the request and calling `close()` on the corresponding response received for that request.
7171
[total_seconds()](https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds) to correctly get
7272
the total elapsed seconds.
73-
* `def .raise_for_status()` - **None**
73+
* `def .raise_for_status()` - **Response**
7474
* `def .json()` - **Any**
7575
* `def .read()` - **bytes**
7676
* `def .iter_raw([chunk_size])` - **bytes iterator**

docs/quickstart.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,15 +285,22 @@ Traceback (most recent call last):
285285
File "/Users/tomchristie/GitHub/encode/httpcore/httpx/models.py", line 837, in raise_for_status
286286
raise HTTPStatusError(message, response=self)
287287
httpx._exceptions.HTTPStatusError: 404 Client Error: Not Found for url: https://httpbin.org/status/404
288-
For more information check: https://httpstatuses.com/404
288+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
289289
```
290290

291-
Any successful response codes will simply return `None` rather than raising an exception.
291+
Any successful response codes will return the `Response` instance rather than raising an exception.
292292

293293
```pycon
294294
>>> r.raise_for_status()
295295
```
296296

297+
The method returns the response instance, allowing you to use it inline. For example:
298+
299+
```pycon
300+
>>> r = httpx.get('...').raise_for_status()
301+
>>> data = httpx.get('...').raise_for_status().json()
302+
```
303+
297304
## Response Headers
298305

299306
The response headers are available as a dictionary-like interface.
@@ -367,7 +374,7 @@ If you're using streaming responses in any of these ways then the `response.cont
367374

368375
```pycon
369376
>>> with httpx.stream("GET", "https://www.example.com") as r:
370-
... if r.headers['Content-Length'] < TOO_LONG:
377+
... if int(r.headers['Content-Length']) < TOO_LONG:
371378
... r.read()
372379
... print(r.text)
373380
```

docs/third_party_packages.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ As HTTPX usage grows, there is an expanding community of developers building too
66

77
<!-- NOTE: this list is in alphabetical order. -->
88

9+
### Hishel
10+
11+
[GitHub](https://github.com/karosis88/hishel) - [Documentation](https://karosis88.github.io/hishel/)
12+
13+
An elegant HTTP Cache implementation for HTTPX and HTTP Core.
14+
915
### Authlib
1016

1117
[GitHub](https://github.com/lepture/authlib) - [Documentation](https://docs.authlib.org/en/latest/)

httpx/_models.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ def has_redirect_location(self) -> bool:
711711
and "Location" in self.headers
712712
)
713713

714-
def raise_for_status(self) -> None:
714+
def raise_for_status(self) -> "Response":
715715
"""
716716
Raise the `HTTPStatusError` if one occurred.
717717
"""
@@ -723,18 +723,18 @@ def raise_for_status(self) -> None:
723723
)
724724

725725
if self.is_success:
726-
return
726+
return self
727727

728728
if self.has_redirect_location:
729729
message = (
730730
"{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
731731
"Redirect location: '{0.headers[location]}'\n"
732-
"For more information check: https://httpstatuses.com/{0.status_code}"
732+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
733733
)
734734
else:
735735
message = (
736736
"{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
737-
"For more information check: https://httpstatuses.com/{0.status_code}"
737+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
738738
)
739739

740740
status_class = self.status_code // 100

requirements.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,20 @@ types-chardet==5.0.4.5
1212
# Documentation
1313
mkdocs==1.4.3
1414
mkautodoc==0.2.0
15-
mkdocs-material==9.1.15
15+
mkdocs-material==9.1.17
1616

1717
# Packaging
1818
build==0.10.0
1919
twine==4.0.2
2020

2121
# Tests & Linting
2222
black==23.3.0
23-
coverage[toml]==7.2.2
24-
cryptography==41.0.0
25-
mypy==1.3.0
23+
coverage[toml]==7.2.7
24+
cryptography==41.0.3
25+
mypy==1.4.1
2626
types-certifi==2021.10.8.2
27-
pytest==7.3.1
28-
ruff==0.0.260
27+
pytest==7.4.0
28+
ruff==0.0.275
2929
trio==0.22.0
3030
trio-typing==0.8.0
3131
trustme==1.0.0

tests/client/test_async_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ async def test_raise_for_status(server):
122122
response.raise_for_status()
123123
assert exc_info.value.response == response
124124
else:
125-
assert response.raise_for_status() is None # type: ignore
125+
assert response.raise_for_status() is response
126126

127127

128128
@pytest.mark.anyio

tests/client/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def test_raise_for_status(server):
141141
assert exc_info.value.response == response
142142
assert exc_info.value.request.url.path == f"/status/{status_code}"
143143
else:
144-
assert response.raise_for_status() is None # type: ignore
144+
assert response.raise_for_status() is response
145145

146146

147147
def test_options(server):

tests/models/test_responses.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_raise_for_status():
102102
response.raise_for_status()
103103
assert str(exc_info.value) == (
104104
"Informational response '101 Switching Protocols' for url 'https://example.org'\n"
105-
"For more information check: https://httpstatuses.com/101"
105+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101"
106106
)
107107

108108
# 3xx status codes are redirections.
@@ -114,7 +114,7 @@ def test_raise_for_status():
114114
assert str(exc_info.value) == (
115115
"Redirect response '303 See Other' for url 'https://example.org'\n"
116116
"Redirect location: 'https://other.org'\n"
117-
"For more information check: https://httpstatuses.com/303"
117+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303"
118118
)
119119

120120
# 4xx status codes are a client error.
@@ -125,7 +125,7 @@ def test_raise_for_status():
125125
response.raise_for_status()
126126
assert str(exc_info.value) == (
127127
"Client error '403 Forbidden' for url 'https://example.org'\n"
128-
"For more information check: https://httpstatuses.com/403"
128+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403"
129129
)
130130

131131
# 5xx status codes are a server error.
@@ -136,7 +136,7 @@ def test_raise_for_status():
136136
response.raise_for_status()
137137
assert str(exc_info.value) == (
138138
"Server error '500 Internal Server Error' for url 'https://example.org'\n"
139-
"For more information check: https://httpstatuses.com/500"
139+
"For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500"
140140
)
141141

142142
# Calling .raise_for_status without setting a request instance is

0 commit comments

Comments
 (0)