Skip to content
2 changes: 1 addition & 1 deletion .codegen.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "engineHash": "6f9492d", "specHash": "131c54a", "version": "10.12.0" }
{ "engineHash": "ed5236c", "specHash": "131c54a", "version": "10.12.0" }
30 changes: 14 additions & 16 deletions box_sdk_gen/networking/box_network_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class APIRequest:
data: Optional[Union[str, ByteStream, MultipartEncoder]]
content_type: Optional[str] = None
allow_redirects: bool = True
timeout: Optional[Tuple[Optional[float], Optional[float]]] = None
timeout: Optional[Union[float, Tuple[Optional[float], Optional[float]]]] = None


@dataclass
Expand Down Expand Up @@ -189,36 +189,34 @@ def _prepare_request(
@staticmethod
def _get_request_timeout(
options: 'FetchOptions',
) -> Optional[Tuple[Optional[float], Optional[float]]]:
) -> Optional[Union[float, Tuple[Optional[float], Optional[float]]]]:
"""
Derive requests timeout tuple (connect, read) in seconds.
Derive requests timeout in seconds.

Uses `options.network_session.timeout_config` when present.
The timeout config values are expected to be in milliseconds.

Returns a tuple (connect, read) when both timeouts are specified,
a single float when only one is set, or None to use no timeout.
"""
network_session = options.network_session
timeout_config = network_session.timeout_config if network_session else None
if timeout_config is None:
return None

connection_timeout_ms, read_timeout_ms = (
timeout_config.connection_timeout_ms,
timeout_config.read_timeout_ms,
)
connection_timeout_ms = timeout_config.connection_timeout_ms
read_timeout_ms = timeout_config.read_timeout_ms

if connection_timeout_ms is None and read_timeout_ms is None:
return None

connection_timeout_sec = (
connection_timeout_ms / 1000.0
if connection_timeout_ms is not None
else None
)
read_timeout_sec = (
read_timeout_ms / 1000.0 if read_timeout_ms is not None else None
)
if connection_timeout_ms is not None and read_timeout_ms is not None:
return (connection_timeout_ms / 1000.0, read_timeout_ms / 1000.0)

if connection_timeout_ms is not None:
return connection_timeout_ms / 1000.0

return (connection_timeout_sec, read_timeout_sec)
return read_timeout_ms / 1000.0

@staticmethod
def _prepare_headers(
Expand Down
2 changes: 1 addition & 1 deletion box_sdk_gen/networking/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(
data_sanitizer = DataSanitizer()
if timeout_config is None:
timeout_config = TimeoutConfig(
connection_timeout_ms=5000,
connection_timeout_ms=10000,
read_timeout_ms=60000,
)
self.additional_headers = additional_headers
Expand Down
7 changes: 6 additions & 1 deletion docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,13 @@ new_client = client.with_custom_base_urls(

In order to configure timeout for API calls, calling the `client.with_timeouts(config)` method creates a new client with timeout settings, leaving the original client unmodified.

All timeout values are in milliseconds.

```python
timeout_config = TimeoutConfig(connection_timeout_ms=10000, read_timeout_ms=30000)
timeout_config = TimeoutConfig(
connection_timeout_ms=5000,
read_timeout_ms=30000,
)
new_client = client.with_timeouts(timeout_config)
```

Expand Down
14 changes: 10 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,19 @@ client = BoxClient(auth=auth, network_session=network_session)
## Timeouts

You can configure network timeouts with `TimeoutConfig` on `NetworkSession`.
Python SDK supports separate connection and read timeout values in milliseconds.
The SDK supports two timeout values, both in milliseconds:

| Parameter | Description |
| ----------------------- | ------------------------------------------------------------------ |
| `connection_timeout_ms` | Maximum time to wait for the TCP connection to be established. |
| `read_timeout_ms` | Maximum idle time between data packets while reading the response. |

```python
from box_sdk_gen import BoxClient, BoxDeveloperTokenAuth, NetworkSession, TimeoutConfig

auth = BoxDeveloperTokenAuth(token="DEVELOPER_TOKEN_GOES_HERE")
timeout_config = TimeoutConfig(
connection_timeout_ms=10000,
connection_timeout_ms=5000,
read_timeout_ms=30000,
)
network_session = NetworkSession(timeout_config=timeout_config)
Expand All @@ -211,9 +216,10 @@ client = BoxClient(auth=auth, network_session=network_session)
How timeout handling works:

- Timeout values are configured in milliseconds and converted to seconds internally for HTTP requests.
- If timeout config is not provided, the SDK uses default timeouts: `connection_timeout_ms=5000` (5 seconds) and `read_timeout_ms=60000` (60 seconds).
- If timeout config is not provided, the SDK uses default timeouts: `connection_timeout_ms=10000` (10 seconds) and `read_timeout_ms=60000` (60 seconds).
- When both `connection_timeout_ms` and `read_timeout_ms` are set, the SDK passes them as a `(connect, read)` tuple to the underlying `requests` library.
- When only one timeout is set, it applies as a single timeout value for the request.
- To disable all SDK timeouts, pass `TimeoutConfig(connection_timeout_ms=None, read_timeout_ms=None)` explicitly to `NetworkSession`.
- You can also disable only one timeout by setting one value to `None` (for example, `connection_timeout_ms=None` or `read_timeout_ms=None`). If you provide only the other value (for example, `read_timeout_ms=30000`) and leave one unspecified, the unspecified field remains `None` and that timeout stays disabled.
- Timeout failures are treated as network exceptions, and retry behavior is controlled by the configured retry strategy.
- Timeout applies to a single HTTP request attempt to the Box API (not the total time across all retries).
- If retries are exhausted, the SDK raises `BoxSDKError` with the underlying request exception.
14 changes: 7 additions & 7 deletions test/box_network_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def network_session_mock():
def test_network_session_uses_default_timeout_config_values():
network_session = NetworkSession()

assert network_session.timeout_config.connection_timeout_ms == 5000
assert network_session.timeout_config.connection_timeout_ms == 10000
assert network_session.timeout_config.read_timeout_ms == 60000


Expand All @@ -191,7 +191,7 @@ def test_prepare_request_uses_default_network_session_timeouts(network_client):

api_request = network_client._prepare_request(options=options)

assert api_request.timeout == (5, 60)
assert api_request.timeout == (10, 60)


@pytest.fixture
Expand Down Expand Up @@ -339,7 +339,7 @@ def test_prepare_json_request(network_client, network_session_mock):
params={"param": "value"},
data='{"key": "value"}',
content_type="application/json",
timeout=(5, 60),
timeout=(10, 60),
)


Expand Down Expand Up @@ -727,7 +727,7 @@ def test_retrying_401_response_with_new_token_and_auth_provided(
data=None,
stream=True,
allow_redirects=True,
timeout=(5, 60),
timeout=(10, 60),
),
mock.call(
method="GET",
Expand All @@ -742,7 +742,7 @@ def test_retrying_401_response_with_new_token_and_auth_provided(
data=None,
stream=True,
allow_redirects=True,
timeout=(5, 60),
timeout=(10, 60),
),
],
)
Expand Down Expand Up @@ -782,7 +782,7 @@ def test_not_retrying_401_when_auth_not_provided(
data=None,
stream=True,
allow_redirects=True,
timeout=(5, 60),
timeout=(10, 60),
)


Expand Down Expand Up @@ -1244,5 +1244,5 @@ def test_disable_follow_redirects(
data=None,
stream=True,
allow_redirects=False,
timeout=(5, 60),
timeout=(10, 60),
)
Loading