From 683b0aa4790a3ea3545538e59373151c2a74ffb1 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Tue, 30 Jun 2026 06:22:38 -0700 Subject: [PATCH 1/2] chore: Update `.codegen.json` with commit hash of `codegen` and `openapi` spec [skip ci] --- .codegen.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codegen.json b/.codegen.json index 1947d1b9..e0d71777 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "6f9492d", "specHash": "131c54a", "version": "10.12.0" } +{ "engineHash": "9c2d390", "specHash": "131c54a", "version": "10.12.0" } From 003efa1da6e1aa3634f3600ab7bb9b6023ee9248 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Tue, 30 Jun 2026 06:24:25 -0700 Subject: [PATCH 2/2] feat: Setup common default timeout (box/box-codegen#965) --- .codegen.json | 2 +- box_sdk_gen/networking/box_network_client.py | 30 +++++++++----------- box_sdk_gen/networking/network.py | 2 +- docs/client.md | 7 ++++- docs/configuration.md | 14 ++++++--- test/box_network_client.py | 14 ++++----- 6 files changed, 39 insertions(+), 30 deletions(-) diff --git a/.codegen.json b/.codegen.json index e0d71777..ba584f4c 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "9c2d390", "specHash": "131c54a", "version": "10.12.0" } +{ "engineHash": "ed5236c", "specHash": "131c54a", "version": "10.12.0" } diff --git a/box_sdk_gen/networking/box_network_client.py b/box_sdk_gen/networking/box_network_client.py index ddb8af4a..5bd7d595 100644 --- a/box_sdk_gen/networking/box_network_client.py +++ b/box_sdk_gen/networking/box_network_client.py @@ -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 @@ -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( diff --git a/box_sdk_gen/networking/network.py b/box_sdk_gen/networking/network.py index a3ed2fb2..7431d991 100644 --- a/box_sdk_gen/networking/network.py +++ b/box_sdk_gen/networking/network.py @@ -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 diff --git a/docs/client.md b/docs/client.md index 7fa552e2..9f9c148e 100644 --- a/docs/client.md +++ b/docs/client.md @@ -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) ``` diff --git a/docs/configuration.md b/docs/configuration.md index 9a0ae0b9..f19cdaec 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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) @@ -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. diff --git a/test/box_network_client.py b/test/box_network_client.py index ecda14ae..5b295566 100644 --- a/test/box_network_client.py +++ b/test/box_network_client.py @@ -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 @@ -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 @@ -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), ) @@ -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", @@ -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), ), ], ) @@ -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), ) @@ -1244,5 +1244,5 @@ def test_disable_follow_redirects( data=None, stream=True, allow_redirects=False, - timeout=(5, 60), + timeout=(10, 60), )