Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,10 @@ jobs:
python-version: 3.x
architecture: ${{ matrix.platform.python-architecture || 'x64' }}
- name: Install NASM
run: |
choco install nasm -y
shell: cmd
uses: ilammy/setup-nasm@v1
with:
version: "2.16.01"
from-source: "false"
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ tikv-jemallocator = { version = "0.7.0", default-features = false, features = [
# Keep the latest emulation profiles and their required TLS support in sync.
[patch.crates-io]
wreq-util = { git = "https://github.com/0x676e67/wreq-util", rev = "5715529002738f46eea86a7271f918f9374d3582" }
wreq = { git = "https://github.com/0x676e67/wreq", rev = "54fdc7c9e0c1290e2f70f38d271ba954e3d86cc2" }
wreq = { git = "https://github.com/0x676e67/wreq", rev = "4b481baa15facf369bda2ae5f31a271e2b853802" }
btls = { git = "https://github.com/0x676e67/btls", branch = "main" }
btls-sys = { git = "https://github.com/0x676e67/btls", branch = "main" }
tokio-btls = { git = "https://github.com/0x676e67/btls", branch = "main" }
Expand Down
5 changes: 5 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
- Unknown keyword arguments now raise `TypeError` when calling async or blocking client constructors, HTTP or WebSocket request methods and functions, `Http1Options`, `Http2Options`, `TlsOptions`, or `Proxy` factories. These arguments were previously ignored.
- Set client-only options such as `connect_timeout` and `proxies` on `Client`; passing them to a request now raises `TypeError`. Use `proxy` to select a proxy for one request.
- Async request calls check keyword names before returning a coroutine. Values for supported request keywords are still converted on first `await` or entry with `async with`.
- Every wreq exception now derives from `wreq.exceptions.Error`. `ConnectionError`, `ProxyConnectionError`, `ConnectionResetError` and `TimeoutError` derive from `RequestError`, and `UpgradeError` from `WebSocketError`. `ConnectionError`, `ConnectionResetError` and `TimeoutError` also derive from the builtins of the same name, so `except TimeoutError` and `except OSError` now catch them.
- A timeout raises `TimeoutError` even while streaming a body, where it raised `BodyError`. A connection reset raises `ConnectionResetError` even while streaming a body or a WebSocket, a WebSocket read failure raises `WebSocketError` rather than `BodyError`, and an error wreq leaves unclassified raises `Error` rather than `UpgradeError`.
- Invalid header names or values and form or JSON bodies that cannot be encoded raise `BuilderError`, and a disconnected WebSocket raises `WebSocketError`; all raised `RuntimeError`. A multipart file that cannot be opened raises the matching `OSError` subclass, such as `FileNotFoundError`.
- Error messages no longer include the request URL; read it from `error.url`. Message text is unspecified, so match on exception classes and `is_*` methods instead.
- `RustPanic`, which was never raised, is removed.

## [0.11.0](https://github.com/0x676e67/wreq-python/compare/v0.10.2..v0.11.0) - 2026-04-13

Expand Down
4 changes: 2 additions & 2 deletions docs/source/guide/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ except (ConnectionError, TimeoutError) as exc:
```

Other failures, including TLS and decoding errors, have their own
[exception classes](../api/exceptions.md). They do not all inherit from
`RequestError`, so catch the types relevant to your application.
[exception classes](../api/exceptions.md). All of them derive from
`wreq.exceptions.Error`, so catch it to handle any other failure.

## Read response metadata and bodies

Expand Down
35 changes: 28 additions & 7 deletions docs/source/guide/redirect-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ async def main():
async with client.get("https://httpbin.org/status/404") as response:
print(await response.text())
except exceptions.StatusError as error:
print("HTTP error:", error)
print("HTTP error:", error.status, error.url)


asyncio.run(main())
Expand Down Expand Up @@ -127,18 +127,39 @@ async def main():
print(await response.text())
except exceptions.TimeoutError as error:
print("Request timed out:", error)
except (exceptions.ConnectionError, exceptions.TlsError) as error:
except exceptions.ConnectionError as error:
print("Connection failed:", error)
except exceptions.Error as error:
print("Request failed:", error)


asyncio.run(main())
```

Catch the failures your application can handle. Other exception types include
`ProxyConnectionError`, `ConnectionResetError`, `BodyError`, `DecodingError`,
`BuilderError`, and `WebSocketError`; see the
[exception reference](../api/exceptions.md). `RequestError` is not a common
base class for all wreq errors.
Every wreq exception derives from `exceptions.Error`, so catch the failures
your application can handle and let `Error` cover the rest. `RequestError`
groups transport failures: `ConnectionError` (including a failed TLS handshake,
`ProxyConnectionError` and `ConnectionResetError`) and `TimeoutError`.
`ConnectionError`, `ConnectionResetError` and `TimeoutError` also derive from
the builtins of the same name. See the
[exception reference](../api/exceptions.md) for the full hierarchy.

The class names the main cause; `is_*` methods on `Error` report every detail
wreq found. A connect timeout raises `TimeoutError` with both `is_timeout()` and
`is_connect()`, and a DNS failure raises `ConnectionError` with `is_dns()`.
The message leaves out the request URL because it may hold credentials; read it
from `error.url` instead.

```python
except exceptions.TimeoutError as error:
stage = "connecting" if error.is_connect() else "waiting for a response"
print(f"Timed out while {stage}:", error.url)
```

Predicates reflect what wreq, its protocol libraries and the operating system
report, so the ones a failure matches may change between releases, and message
text is unspecified. Choose handlers by exception class, and use predicates to
refine them or for diagnostics.

Asynchronous cancellation uses `asyncio.CancelledError`. Allow it to propagate
when a caller cancels the task. The same request options and wreq exception
Expand Down
59 changes: 13 additions & 46 deletions examples/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,25 @@
import asyncio
import datetime
import wreq
import asyncio
import wreq.exceptions as exceptions

wreq_errors = (
exceptions.BodyError,
exceptions.BuilderError,
exceptions.ConnectionError,
exceptions.ConnectionResetError,
exceptions.DecodingError,
exceptions.RedirectError,
exceptions.TimeoutError,
exceptions.StatusError,
exceptions.RequestError,
exceptions.UpgradeError,
)


async def test_bad_builder():
print("\n--- BuilderError (bad builder) ---")
try:
await wreq.get("htt://httpbin.org/status/404")
except wreq_errors as e:
print(f"Caught: {type(e).__name__}: {e}")
except Exception as e:
print(f"Other error: {type(e).__name__}: {e}")


async def test_timeout_error():
print("\n--- TimeoutError (timeout) ---")
try:
await wreq.get(
"https://httpbin.io/delay/10", timeout=datetime.timedelta(seconds=1)
)
except wreq_errors as e:
print(f"Caught: {type(e).__name__}: {e}")
except Exception as e:
print(f"Other error: {type(e).__name__}: {e}")


async def test_connection_error():
print("\n--- ConnectionError (refused) ---")
async def fetch(label, url, **kwargs):
print(f"\n--- {label} ---")
try:
await wreq.get("http://127.0.0.1:9999")
except wreq_errors as e:
await wreq.get(url, **kwargs)
except wreq.Error as e:
# Every wreq error derives from `wreq.Error`.
print(f"Caught: {type(e).__name__}: {e}")
except Exception as e:
print(f"Other error: {type(e).__name__}: {e}")


async def main():
await test_bad_builder()
await test_timeout_error()
await test_connection_error()
await fetch("BuilderError (bad URL)", "htt://httpbin.org/status/404")
await fetch(
"TimeoutError (timeout)",
"https://httpbin.io/delay/10",
timeout=datetime.timedelta(seconds=1),
)
await fetch("ConnectionError (refused)", "http://127.0.0.1:9999")


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This directory contains Python type hints (Type Stubs) files for the `wreq` pack
- `cookie.py` - Cookie management
- `dns.py` - DNS resolver options
- `emulation.py` - Browser emulation
- `exceptions.py` - Exception types
- `exceptions.py` - Exception hierarchy; unlike the stubs, the runtime classes live here
- `header.py` - HTTP header handling
- `http1.py` - HTTP/1.x options
- `http2.py` - HTTP/2 options
Expand Down
Loading
Loading