...
```
+## Monitoring download progress
+
+If you need to monitor download progress of large responses, you can use response streaming and inspect the `response.num_bytes_downloaded` property.
+
+This interface is required for properly determining download progress, because the total number of bytes returned by `response.content` or `response.iter_content()` will not always correspond with the raw content length of the response if HTTP response compression is being used.
+
+For example, showing a progress bar using the [`tqdm`](https://github.com/tqdm/tqdm) library while a response is being downloaded could be done like this…
+
+```python
+import tempfile
+
+import httpx
+from tqdm import tqdm
+
+with tempfile.NamedTemporaryFile() as download_file:
+ url = "https://speed.hetzner.de/100MB.bin"
+ with httpx.stream("GET", url) as response:
+ total = int(response.headers["Content-Length"])
+
+ with tqdm(total=total, unit_scale=True, unit_divisor=1024, unit="B") as progress:
+ num_bytes_downloaded = response.num_bytes_downloaded
+ for chunk in response.iter_bytes():
+ download_file.write(chunk)
+ progress.update(response.num_bytes_downloaded - num_bytes_downloaded)
+ num_bytes_downloaded = response.num_bytes_downloaded
+ print(f"The total download size is {response.num_bytes_downloaded} bytes")
+```
+
## .netrc Support
HTTPX supports .netrc file. In `trust_env=True` cases, if auth parameter is
self._raw_stream = ByteStream(body=content or b"")
self.read()
+ self._num_bytes_downloaded = 0
+
@property
def elapsed(self) -> datetime.timedelta:
"""
ldict[key] = link
return ldict
+ @property
+ def num_bytes_downloaded(self) -> int:
+ return self._num_bytes_downloaded
+
def __repr__(self) -> str:
return f"<Response [{self.status_code} {self.reason_phrase}]>"
raise ResponseClosed()
self.is_stream_consumed = True
+ self._num_bytes_downloaded = 0
with map_exceptions(HTTPCORE_EXC_MAP, request=self._request):
for part in self._raw_stream:
+ self._num_bytes_downloaded += len(part)
yield part
self.close()
raise ResponseClosed()
self.is_stream_consumed = True
+ self._num_bytes_downloaded = 0
with map_exceptions(HTTPCORE_EXC_MAP, request=self._request):
async for part in self._raw_stream:
+ self._num_bytes_downloaded += len(part)
yield part
await self.aclose()
assert raw == b"Hello, world!"
+def test_iter_raw_increments_updates_counter():
+ stream = IteratorStream(iterator=streaming_body())
+
+ response = httpx.Response(
+ 200,
+ stream=stream,
+ )
+
+ num_downloaded = response.num_bytes_downloaded
+ for part in response.iter_raw():
+ assert len(part) == (response.num_bytes_downloaded - num_downloaded)
+ num_downloaded = response.num_bytes_downloaded
+
+
@pytest.mark.asyncio
async def test_aiter_raw():
stream = AsyncIteratorStream(aiterator=async_streaming_body())
assert raw == b"Hello, world!"
+@pytest.mark.asyncio
+async def test_aiter_raw_increments_updates_counter():
+ stream = AsyncIteratorStream(aiterator=async_streaming_body())
+
+ response = httpx.Response(
+ 200,
+ stream=stream,
+ )
+
+ num_downloaded = response.num_bytes_downloaded
+ async for part in response.aiter_raw():
+ assert len(part) == (response.num_bytes_downloaded - num_downloaded)
+ num_downloaded = response.num_bytes_downloaded
+
+
def test_iter_bytes():
response = httpx.Response(
200,