Skip to content

Commit d0130d0

Browse files
fix(client): raise ReadTimeout, not a bare Timeout, on a read timeout
httpx.ReadTimeout was landing in the TimeoutException catch-all and coming back out as requests.Timeout. Callers that catch requests.ReadTimeout by name stopped matching. The translation table test used pytest.raises, which is subclass-tolerant and passed either way; it now asserts the exact class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014f9oEEYspPH4fmPULTnLkJ
1 parent 04d997d commit d0130d0

2 files changed

Lines changed: 10 additions & 5 deletions

File tree

src/unstract/api_deployments/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# `requests` remains a dependency for its exception classes. Downstream code
2020
# catches ConnectionError and Timeout by name around these calls, and the httpx
2121
# equivalents are not subclasses, so they are translated at the transport seam.
22-
from requests.exceptions import ConnectionError, ConnectTimeout, Timeout
22+
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout, Timeout
2323
from tenacity import (
2424
RetryCallState,
2525
Retrying,
@@ -51,6 +51,8 @@ def _translate_transport_errors(fn, *args, **kwargs):
5151
# ConnectTimeout is both a ConnectionError and a Timeout; the plain
5252
# Timeout httpx implies would stop matching half the callers.
5353
raise ConnectTimeout(str(e)) from e
54+
except httpx.ReadTimeout as e:
55+
raise ReadTimeout(str(e)) from e
5456
except httpx.TimeoutException as e:
5557
raise Timeout(str(e)) from e
5658
except httpx.ConnectError as e:

tests/test_compat.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import httpx
2020
import pytest
2121
import requests
22-
from requests.exceptions import ConnectionError, ConnectTimeout, Timeout
22+
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout, Timeout
2323

2424
from unstract.api_deployments.client import (
2525
_EXECUTE_SEND_ONLY,
@@ -104,7 +104,7 @@ def _requests_response(status_code=200, json_data=None, text=None):
104104
("raised", "expected"),
105105
[
106106
(httpx.ConnectTimeout("connect timed out"), ConnectTimeout),
107-
(httpx.ReadTimeout("read timed out"), Timeout),
107+
(httpx.ReadTimeout("read timed out"), ReadTimeout),
108108
(httpx.WriteTimeout("write timed out"), Timeout),
109109
(httpx.PoolTimeout("pool timed out"), Timeout),
110110
(httpx.ConnectError("refused"), ConnectionError),
@@ -119,14 +119,17 @@ def test_transport_errors_are_translated(raised, expected):
119119
120120
``ConnectTimeout`` is the case that makes ordering load-bearing, and it is
121121
also both a ``ConnectionError`` and a ``Timeout`` — the plain ``Timeout``
122-
that httpx's hierarchy implies would stop matching half the callers.
122+
that httpx's hierarchy implies would stop matching half the callers. The
123+
exact class matters too: a caller catching ``ReadTimeout`` sees nothing if
124+
a broader ``Timeout`` is raised in its place.
123125
"""
124126
client = _client()
125127
with patch.object(
126128
client._transport.get_httpx_client(), "request", side_effect=raised
127129
):
128-
with pytest.raises(expected):
130+
with pytest.raises(expected) as caught:
129131
client._send("get", "/anything")
132+
assert type(caught.value) is expected
130133

131134

132135
def test_a_connect_timeout_is_still_a_connection_error():

0 commit comments

Comments
 (0)