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
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 @@ -77,7 +77,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
4 changes: 2 additions & 2 deletions docs/source/guide/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,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