From 295b3316ece0198d9c96166e2b650cd2958d65df Mon Sep 17 00:00:00 2001 From: Albert Li Date: Tue, 3 Feb 2026 17:49:44 -0800 Subject: [PATCH 1/2] Mark legacy tunnel methods deprecated --- .../resources/devboxes/devboxes.py | 24 ++++++++----- src/runloop_api_client/sdk/async_devbox.py | 16 +++++++-- src/runloop_api_client/sdk/devbox.py | 16 +++++++-- tests/sdk/async_devbox/test_interfaces.py | 34 +++++++++--------- tests/sdk/devbox/test_interfaces.py | 35 ++++++++++--------- tests/smoketests/sdk/test_async_devbox.py | 6 ++-- tests/smoketests/sdk/test_devbox.py | 6 ++-- 7 files changed, 89 insertions(+), 48 deletions(-) diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 524e0cc74..08f5a9cb0 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -652,7 +652,9 @@ def create_ssh_key( cast_to=DevboxCreateSSHKeyResponse, ) - @typing_extensions.deprecated("deprecated") + @typing_extensions.deprecated( + "create_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + ) def create_tunnel( self, id: str, @@ -666,7 +668,7 @@ def create_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> DevboxTunnelView: - """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel instead. + """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel or devbox creation instead. This endpoint creates a legacy tunnel. The new enable_tunnel endpoint provides improved tunnel @@ -1270,7 +1272,9 @@ def read_file_contents( cast_to=str, ) - @typing_extensions.deprecated("deprecated") + @typing_extensions.deprecated( + "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + ) def remove_tunnel( self, id: str, @@ -1284,7 +1288,7 @@ def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] Tunnels remain active until devbox is shutdown. + """[Deprecated] Use enable_tunnel or devbox creation instead. This endpoint removes a legacy tunnel. @@ -2261,7 +2265,9 @@ async def create_ssh_key( cast_to=DevboxCreateSSHKeyResponse, ) - @typing_extensions.deprecated("deprecated") + @typing_extensions.deprecated( + "create_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + ) async def create_tunnel( self, id: str, @@ -2275,7 +2281,7 @@ async def create_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> DevboxTunnelView: - """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel instead. + """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel or devbox creation instead. This endpoint creates a legacy tunnel. The new enable_tunnel endpoint provides improved tunnel @@ -2882,7 +2888,9 @@ async def read_file_contents( cast_to=str, ) - @typing_extensions.deprecated("deprecated") + @typing_extensions.deprecated( + "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + ) async def remove_tunnel( self, id: str, @@ -2896,7 +2904,7 @@ async def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] Tunnels remain active until devbox is shutdown. + """[Deprecated] Use enable_tunnel or devbox creation instead. This endpoint removes a legacy tunnel. diff --git a/src/runloop_api_client/sdk/async_devbox.py b/src/runloop_api_client/sdk/async_devbox.py index 321d3c82e..073c0922e 100644 --- a/src/runloop_api_client/sdk/async_devbox.py +++ b/src/runloop_api_client/sdk/async_devbox.py @@ -779,7 +779,7 @@ async def create_tunnel( ) -> DevboxTunnelView: """[Deprecated] Create a legacy tunnel to expose a devbox port publicly. - Use :meth:`enable_tunnel` instead for the V2 tunnel API. + Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxCreateTunnelParams` for available parameters :return: Details about the public endpoint @@ -789,6 +789,11 @@ async def create_tunnel( >>> tunnel = await devbox.net.create_tunnel(port=8080) >>> print(f"Public URL: {tunnel.url}") """ + warnings.warn( + "create_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + DeprecationWarning, + stacklevel=2, + ) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) return await self._devbox._client.devboxes.create_tunnel( # type: ignore[deprecated] @@ -824,7 +829,9 @@ async def remove_tunnel( self, **params: Unpack[SDKDevboxRemoveTunnelParams], ) -> object: - """Remove a network tunnel, disabling public access to the port. + """[Deprecated] Remove a legacy network tunnel. + + Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxRemoveTunnelParams` for available parameters :return: Response confirming the tunnel removal @@ -833,6 +840,11 @@ async def remove_tunnel( Example: >>> await devbox.net.remove_tunnel(port=8080) """ + warnings.warn( + "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + DeprecationWarning, + stacklevel=2, + ) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) return await self._devbox._client.devboxes.remove_tunnel( # type: ignore[deprecated] diff --git a/src/runloop_api_client/sdk/devbox.py b/src/runloop_api_client/sdk/devbox.py index 65ede0fef..567d57b1d 100644 --- a/src/runloop_api_client/sdk/devbox.py +++ b/src/runloop_api_client/sdk/devbox.py @@ -782,7 +782,7 @@ def create_tunnel( ) -> DevboxTunnelView: """[Deprecated] Create a legacy tunnel to expose a devbox port publicly. - Use :meth:`enable_tunnel` instead for the V2 tunnel API. + Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxCreateTunnelParams` for available parameters :return: Details about the public endpoint @@ -792,6 +792,11 @@ def create_tunnel( >>> tunnel = devbox.net.create_tunnel(port=8080) >>> print(f"Public URL: {tunnel.url}") """ + warnings.warn( + "create_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + DeprecationWarning, + stacklevel=2, + ) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) return self._devbox._client.devboxes.create_tunnel( # type: ignore[deprecated] @@ -827,7 +832,9 @@ def remove_tunnel( self, **params: Unpack[SDKDevboxRemoveTunnelParams], ) -> object: - """Remove a network tunnel, disabling public access to the port. + """[Deprecated] Remove a legacy network tunnel. + + Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxRemoveTunnelParams` for available parameters :return: Response confirming the tunnel removal @@ -836,6 +843,11 @@ def remove_tunnel( Example: >>> devbox.net.remove_tunnel(port=8080) """ + warnings.warn( + "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + DeprecationWarning, + stacklevel=2, + ) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) return self._devbox._client.devboxes.remove_tunnel( # type: ignore[deprecated] diff --git a/tests/sdk/async_devbox/test_interfaces.py b/tests/sdk/async_devbox/test_interfaces.py index 52c439c22..49e07c279 100644 --- a/tests/sdk/async_devbox/test_interfaces.py +++ b/tests/sdk/async_devbox/test_interfaces.py @@ -185,14 +185,15 @@ async def test_create_tunnel(self, mock_async_client: AsyncMock) -> None: mock_async_client.devboxes.create_tunnel = AsyncMock(return_value=tunnel_view) devbox = AsyncDevbox(mock_async_client, "dbx_123") - result = await devbox.net.create_tunnel( - port=8080, - extra_headers={"X-Custom": "value"}, - extra_query={"param": "value"}, - extra_body={"key": "value"}, - timeout=30.0, - idempotency_key="key-123", - ) + with pytest.warns(DeprecationWarning, match="create_tunnel is deprecated"): + result = await devbox.net.create_tunnel( + port=8080, + extra_headers={"X-Custom": "value"}, + extra_query={"param": "value"}, + extra_body={"key": "value"}, + timeout=30.0, + idempotency_key="key-123", + ) assert result == tunnel_view mock_async_client.devboxes.create_tunnel.assert_called_once() @@ -203,14 +204,15 @@ async def test_remove_tunnel(self, mock_async_client: AsyncMock) -> None: mock_async_client.devboxes.remove_tunnel = AsyncMock(return_value=object()) devbox = AsyncDevbox(mock_async_client, "dbx_123") - result = await devbox.net.remove_tunnel( - port=8080, - extra_headers={"X-Custom": "value"}, - extra_query={"param": "value"}, - extra_body={"key": "value"}, - timeout=30.0, - idempotency_key="key-123", - ) + with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): + result = await devbox.net.remove_tunnel( + port=8080, + extra_headers={"X-Custom": "value"}, + extra_query={"param": "value"}, + extra_body={"key": "value"}, + timeout=30.0, + idempotency_key="key-123", + ) assert result is not None # Verify return value is propagated mock_async_client.devboxes.remove_tunnel.assert_called_once() diff --git a/tests/sdk/devbox/test_interfaces.py b/tests/sdk/devbox/test_interfaces.py index 66ef8fa7b..bca5eada1 100644 --- a/tests/sdk/devbox/test_interfaces.py +++ b/tests/sdk/devbox/test_interfaces.py @@ -11,6 +11,7 @@ from unittest.mock import Mock import httpx +import pytest from tests.sdk.conftest import MockExecutionView from runloop_api_client.sdk import Devbox @@ -285,14 +286,15 @@ def test_create_tunnel(self, mock_client: Mock) -> None: mock_client.devboxes.create_tunnel.return_value = tunnel_view devbox = Devbox(mock_client, "dbx_123") - result = devbox.net.create_tunnel( - port=8080, - extra_headers={"X-Custom": "value"}, - extra_query={"param": "value"}, - extra_body={"key": "value"}, - timeout=30.0, - idempotency_key="key-123", - ) + with pytest.warns(DeprecationWarning, match="create_tunnel is deprecated"): + result = devbox.net.create_tunnel( + port=8080, + extra_headers={"X-Custom": "value"}, + extra_query={"param": "value"}, + extra_body={"key": "value"}, + timeout=30.0, + idempotency_key="key-123", + ) assert result == tunnel_view mock_client.devboxes.create_tunnel.assert_called_once_with( @@ -310,14 +312,15 @@ def test_remove_tunnel(self, mock_client: Mock) -> None: mock_client.devboxes.remove_tunnel.return_value = object() devbox = Devbox(mock_client, "dbx_123") - result = devbox.net.remove_tunnel( - port=8080, - extra_headers={"X-Custom": "value"}, - extra_query={"param": "value"}, - extra_body={"key": "value"}, - timeout=30.0, - idempotency_key="key-123", - ) + with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): + result = devbox.net.remove_tunnel( + port=8080, + extra_headers={"X-Custom": "value"}, + extra_query={"param": "value"}, + extra_body={"key": "value"}, + timeout=30.0, + idempotency_key="key-123", + ) assert result is not None # Verify return value is propagated mock_client.devboxes.remove_tunnel.assert_called_once_with( diff --git a/tests/smoketests/sdk/test_async_devbox.py b/tests/smoketests/sdk/test_async_devbox.py index dec172301..860502cd7 100644 --- a/tests/smoketests/sdk/test_async_devbox.py +++ b/tests/smoketests/sdk/test_async_devbox.py @@ -447,14 +447,16 @@ async def test_create_and_remove_tunnel(self, async_sdk_client: AsyncRunloopSDK) try: # Create tunnel - tunnel = await devbox.net.create_tunnel(port=8080) + with pytest.warns(DeprecationWarning, match="create_tunnel is deprecated"): + tunnel = await devbox.net.create_tunnel(port=8080) assert tunnel is not None assert tunnel.url is not None assert tunnel.port == 8080 assert tunnel.devbox_id == devbox.id # Remove tunnel - await devbox.net.remove_tunnel(port=8080) + with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): + await devbox.net.remove_tunnel(port=8080) finally: await devbox.shutdown() diff --git a/tests/smoketests/sdk/test_devbox.py b/tests/smoketests/sdk/test_devbox.py index 3923694fb..196fb312e 100644 --- a/tests/smoketests/sdk/test_devbox.py +++ b/tests/smoketests/sdk/test_devbox.py @@ -444,14 +444,16 @@ def test_create_and_remove_tunnel(self, sdk_client: RunloopSDK) -> None: try: # Create tunnel - tunnel = devbox.net.create_tunnel(port=8080) + with pytest.warns(DeprecationWarning, match="create_tunnel is deprecated"): + tunnel = devbox.net.create_tunnel(port=8080) assert tunnel is not None assert tunnel.url is not None assert tunnel.port == 8080 assert tunnel.devbox_id == devbox.id # Remove tunnel - devbox.net.remove_tunnel(port=8080) + with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): + devbox.net.remove_tunnel(port=8080) finally: devbox.shutdown() From a5f52be8e369d50b6a4d9b0c0074f6e8f42e0f43 Mon Sep 17 00:00:00 2001 From: Albert Li Date: Tue, 3 Feb 2026 18:17:11 -0800 Subject: [PATCH 2/2] Resolve feedback --- .../resources/devboxes/devboxes.py | 12 ++++++------ src/runloop_api_client/sdk/async_devbox.py | 6 ++---- src/runloop_api_client/sdk/devbox.py | 6 ++---- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 08f5a9cb0..37c4b994a 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -668,7 +668,7 @@ def create_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> DevboxTunnelView: - """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel or devbox creation instead. + """[Deprecated] Use enable_tunnel or configure a tunnel at devbox creation instead. This endpoint creates a legacy tunnel. The new enable_tunnel endpoint provides improved tunnel @@ -1273,7 +1273,7 @@ def read_file_contents( ) @typing_extensions.deprecated( - "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown." ) def remove_tunnel( self, @@ -1288,7 +1288,7 @@ def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] Use enable_tunnel or devbox creation instead. + """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. This endpoint removes a legacy tunnel. @@ -2281,7 +2281,7 @@ async def create_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> DevboxTunnelView: - """[Deprecated] Use POST /v1/devboxes/{id}/enable_tunnel or devbox creation instead. + """[Deprecated] Use enable_tunnel or configure a tunnel at devbox creation instead. This endpoint creates a legacy tunnel. The new enable_tunnel endpoint provides improved tunnel @@ -2889,7 +2889,7 @@ async def read_file_contents( ) @typing_extensions.deprecated( - "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation." + "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown." ) async def remove_tunnel( self, @@ -2904,7 +2904,7 @@ async def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] Use enable_tunnel or devbox creation instead. + """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. This endpoint removes a legacy tunnel. diff --git a/src/runloop_api_client/sdk/async_devbox.py b/src/runloop_api_client/sdk/async_devbox.py index 073c0922e..81d602eb3 100644 --- a/src/runloop_api_client/sdk/async_devbox.py +++ b/src/runloop_api_client/sdk/async_devbox.py @@ -829,9 +829,7 @@ async def remove_tunnel( self, **params: Unpack[SDKDevboxRemoveTunnelParams], ) -> object: - """[Deprecated] Remove a legacy network tunnel. - - Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. + """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxRemoveTunnelParams` for available parameters :return: Response confirming the tunnel removal @@ -841,7 +839,7 @@ async def remove_tunnel( >>> await devbox.net.remove_tunnel(port=8080) """ warnings.warn( - "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown.", DeprecationWarning, stacklevel=2, ) diff --git a/src/runloop_api_client/sdk/devbox.py b/src/runloop_api_client/sdk/devbox.py index 567d57b1d..ff541ce92 100644 --- a/src/runloop_api_client/sdk/devbox.py +++ b/src/runloop_api_client/sdk/devbox.py @@ -832,9 +832,7 @@ def remove_tunnel( self, **params: Unpack[SDKDevboxRemoveTunnelParams], ) -> object: - """[Deprecated] Remove a legacy network tunnel. - - Use :meth:`enable_tunnel` or configure a tunnel during devbox creation instead. + """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKDevboxRemoveTunnelParams` for available parameters :return: Response confirming the tunnel removal @@ -844,7 +842,7 @@ def remove_tunnel( >>> devbox.net.remove_tunnel(port=8080) """ warnings.warn( - "remove_tunnel is deprecated; use enable_tunnel or configure a tunnel at devbox creation.", + "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown.", DeprecationWarning, stacklevel=2, )