diff --git a/.changeset/python-timeout-zero.md b/.changeset/python-timeout-zero.md new file mode 100644 index 0000000000..62bb6c9491 --- /dev/null +++ b/.changeset/python-timeout-zero.md @@ -0,0 +1,5 @@ +--- +'@e2b/python-sdk': patch +--- + +Keep an explicit `timeout=0` on sandbox create and connect instead of substituting the 300s default. diff --git a/packages/python-sdk/e2b/sandbox_async/main.py b/packages/python-sdk/e2b/sandbox_async/main.py index 7b1d7253d0..d037b5e7a5 100644 --- a/packages/python-sdk/e2b/sandbox_async/main.py +++ b/packages/python-sdk/e2b/sandbox_async/main.py @@ -1158,7 +1158,9 @@ async def _create( else: response = await SandboxApi._create_sandbox( template=template or cls.default_template, - timeout=timeout or cls.default_sandbox_timeout, + timeout=( + timeout if timeout is not None else cls.default_sandbox_timeout + ), metadata=metadata, env_vars=envs, secure=secure, diff --git a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py index b4171327f3..3a7f78f88b 100644 --- a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py @@ -531,7 +531,9 @@ async def _cls_connect( on_resume: SandboxOnResume = "restore", **opts: Unpack[ApiParams], ) -> SandboxCreateResponse: - timeout = timeout or SandboxBase.default_sandbox_timeout + timeout = ( + timeout if timeout is not None else SandboxBase.default_sandbox_timeout + ) # Sandbox is not running, resume it config = ConnectionConfig(logger=logger, **cls._resolve_api_params(**opts)) diff --git a/packages/python-sdk/e2b/sandbox_sync/main.py b/packages/python-sdk/e2b/sandbox_sync/main.py index 80b4ff1e97..0be427834c 100644 --- a/packages/python-sdk/e2b/sandbox_sync/main.py +++ b/packages/python-sdk/e2b/sandbox_sync/main.py @@ -1154,7 +1154,9 @@ def _create( else: response = SandboxApi._create_sandbox( template=template or cls.default_template, - timeout=timeout or cls.default_sandbox_timeout, + timeout=( + timeout if timeout is not None else cls.default_sandbox_timeout + ), metadata=metadata, env_vars=envs, secure=secure, diff --git a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py index d0d9d1656b..1d18694d34 100644 --- a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py @@ -346,7 +346,9 @@ def _cls_connect( on_resume: SandboxOnResume = "restore", **opts: Unpack[ApiParams], ) -> SandboxCreateResponse: - timeout = timeout or SandboxBase.default_sandbox_timeout + timeout = ( + timeout if timeout is not None else SandboxBase.default_sandbox_timeout + ) config = ConnectionConfig(logger=logger, **cls._resolve_api_params(**opts)) diff --git a/packages/python-sdk/tests/shared/sandbox/test_timeout.py b/packages/python-sdk/tests/shared/sandbox/test_timeout.py new file mode 100644 index 0000000000..3c394e5c47 --- /dev/null +++ b/packages/python-sdk/tests/shared/sandbox/test_timeout.py @@ -0,0 +1,83 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock, Mock + +from e2b import AsyncSandbox, Sandbox +from e2b.api.client.api.sandboxes import ( + post_sandboxes, + post_sandboxes_sandbox_id_connect, +) +from e2b.api.client.models import Sandbox as SandboxModel +from e2b.sandbox.main import SandboxBase + + +def _created_sandbox(): + return SimpleNamespace( + status_code=200, + parsed=SandboxModel( + client_id="client-id", + envd_version="0.2.4", + sandbox_id="sbx-test", + template_id="template-id", + ), + ) + + +def test_create_sends_timeout_zero(monkeypatch, test_api_key): + # timeout=0 is a value. `timeout or default` used to send 300 instead. + request = Mock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes, "sync_detailed", request) + + Sandbox.create(api_key=test_api_key, timeout=0) + + assert request.call_args.kwargs["body"].to_dict()["timeout"] == 0 + + +def test_create_omitted_timeout_uses_default(monkeypatch, test_api_key): + request = Mock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes, "sync_detailed", request) + + Sandbox.create(api_key=test_api_key) + + assert ( + request.call_args.kwargs["body"].to_dict()["timeout"] + == SandboxBase.default_sandbox_timeout + ) + + +async def test_async_create_sends_timeout_zero(monkeypatch, test_api_key): + request = AsyncMock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes, "asyncio_detailed", request) + + await AsyncSandbox.create(api_key=test_api_key, timeout=0) + + assert request.call_args.kwargs["body"].to_dict()["timeout"] == 0 + + +async def test_async_create_omitted_timeout_uses_default(monkeypatch, test_api_key): + request = AsyncMock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes, "asyncio_detailed", request) + + await AsyncSandbox.create(api_key=test_api_key) + + assert ( + request.call_args.kwargs["body"].to_dict()["timeout"] + == SandboxBase.default_sandbox_timeout + ) + + +def test_connect_sends_timeout_zero(monkeypatch, test_api_key): + request = Mock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes_sandbox_id_connect, "sync_detailed", request) + + Sandbox.connect("sbx-test", timeout=0, api_key=test_api_key) + + assert request.call_args.kwargs["body"].to_dict()["timeout"] == 0 + + +async def test_async_connect_sends_timeout_zero(monkeypatch, test_api_key): + request = AsyncMock(return_value=_created_sandbox()) + monkeypatch.setattr(post_sandboxes_sandbox_id_connect, "asyncio_detailed", request) + + await AsyncSandbox.connect("sbx-test", timeout=0, api_key=test_api_key) + + assert request.call_args.kwargs["body"].to_dict()["timeout"] == 0