diff --git a/.changeset/cuddly-buckets-tie.md b/.changeset/cuddly-buckets-tie.md new file mode 100644 index 0000000000..8fa129e8a0 --- /dev/null +++ b/.changeset/cuddly-buckets-tie.md @@ -0,0 +1,5 @@ +--- +'@e2b/python-sdk': patch +--- + +Add support for passing proxy diff --git a/packages/python-sdk/e2b/__init__.py b/packages/python-sdk/e2b/__init__.py index 8aedd80811..7f50036757 100644 --- a/packages/python-sdk/e2b/__init__.py +++ b/packages/python-sdk/e2b/__init__.py @@ -31,6 +31,7 @@ ) from .connection_config import ( ConnectionConfig, + ProxyTypes, ) from .exceptions import ( SandboxException, diff --git a/packages/python-sdk/e2b/api/__init__.py b/packages/python-sdk/e2b/api/__init__.py index 49530b1148..7f21c3da55 100644 --- a/packages/python-sdk/e2b/api/__init__.py +++ b/packages/python-sdk/e2b/api/__init__.py @@ -1,9 +1,9 @@ import json import logging +from typing import Optional +from httpx import Limits from dataclasses import dataclass -from typing import Optional, Union -from httpx import HTTPTransport, AsyncHTTPTransport from e2b.api.client.client import AuthenticatedClient from e2b.connection_config import ConnectionConfig @@ -50,7 +50,7 @@ def __init__( config: ConnectionConfig, require_api_key: bool = True, require_access_token: bool = False, - transport: Optional[Union[HTTPTransport, AsyncHTTPTransport]] = None, + limits: Optional[Limits] = None, *args, **kwargs, ): @@ -97,7 +97,8 @@ def __init__( "request": [self._log_request], "response": [self._log_response], }, - "transport": transport, + "proxy": config.proxy, + "limits": limits, }, headers=headers, token=token, diff --git a/packages/python-sdk/e2b/connection_config.py b/packages/python-sdk/e2b/connection_config.py index 0a2f64816f..6ac6b9829d 100644 --- a/packages/python-sdk/e2b/connection_config.py +++ b/packages/python-sdk/e2b/connection_config.py @@ -1,6 +1,7 @@ import os from typing import Literal, Optional, Dict +from httpx._types import ProxyTypes REQUEST_TIMEOUT: float = 30.0 # 30 seconds @@ -37,12 +38,14 @@ def __init__( access_token: Optional[str] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ): self.domain = domain or ConnectionConfig._domain() self.debug = debug or ConnectionConfig._debug() self.api_key = api_key or ConnectionConfig._api_key() self.access_token = access_token or ConnectionConfig._access_token() self.headers = headers + self.proxy = proxy self.request_timeout = ConnectionConfig._get_request_timeout( REQUEST_TIMEOUT, diff --git a/packages/python-sdk/e2b/sandbox_async/main.py b/packages/python-sdk/e2b/sandbox_async/main.py index 3db4ccd312..4ee18a5d7e 100644 --- a/packages/python-sdk/e2b/sandbox_async/main.py +++ b/packages/python-sdk/e2b/sandbox_async/main.py @@ -4,7 +4,7 @@ from typing import Dict, Optional, TypedDict, overload from typing_extensions import Unpack -from e2b.connection_config import ConnectionConfig +from e2b.connection_config import ConnectionConfig, ProxyTypes from e2b.envd.api import ENVD_API_HEALTH_ROUTE, ahandle_envd_api_exception from e2b.exceptions import format_request_timeout_error from e2b.sandbox.main import SandboxSetup @@ -106,7 +106,9 @@ def __init__(self, **opts: Unpack[AsyncSandboxOpts]): self._envd_api_url = f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(self.envd_port)}" self._envd_version = opts["envd_version"] - self._transport = AsyncTransportWithLogger(limits=self._limits) + self._transport = AsyncTransportWithLogger( + limits=self._limits, proxy=self._connection_config.proxy + ) self._envd_api = httpx.AsyncClient( base_url=self.envd_api_url, transport=self._transport, @@ -177,6 +179,7 @@ async def create( domain: Optional[str] = None, debug: Optional[bool] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ): """ Create a new sandbox. @@ -189,6 +192,7 @@ async def create( :param envs: Custom environment variables for the sandbox :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox** :return: sandbox instance for the new sandbox @@ -199,6 +203,7 @@ async def create( domain=domain, debug=debug, request_timeout=request_timeout, + proxy=proxy, ) if connection_config.debug: @@ -214,6 +219,7 @@ async def create( debug=debug, request_timeout=request_timeout, env_vars=envs, + proxy=proxy, ) sandbox_id = response.sandbox_id envd_version = response.envd_version @@ -231,6 +237,7 @@ async def connect( api_key: Optional[str] = None, domain: Optional[str] = None, debug: Optional[bool] = None, + proxy: Optional[ProxyTypes] = None, ): """ Connect to an existing sandbox. @@ -238,6 +245,7 @@ async def connect( :param sandbox_id: Sandbox ID :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable + :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox** :return: sandbox instance for the existing sandbox @@ -253,6 +261,7 @@ async def connect( api_key=api_key, domain=domain, debug=debug, + proxy=proxy, ) return cls( @@ -286,6 +295,7 @@ async def kill( domain: Optional[str] = None, debug: Optional[bool] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ) -> bool: """ Kill the sandbox specified by sandbox ID. @@ -293,13 +303,17 @@ async def kill( :param sandbox_id: Sandbox ID :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request :return: `True` if the sandbox was killed, `False` if the sandbox was not found """ ... @class_method_variant("_cls_kill") - async def kill(self, request_timeout: Optional[float] = None) -> bool: # type: ignore + async def kill( + self, + request_timeout: Optional[float] = None, + ) -> bool: # type: ignore config_dict = self.connection_config.__dict__ config_dict.pop("access_token", None) config_dict.pop("api_url", None) @@ -309,7 +323,7 @@ async def kill(self, request_timeout: Optional[float] = None) -> bool: # type: await SandboxApi._cls_kill( sandbox_id=self.sandbox_id, - **self.connection_config.__dict__, + **config_dict, ) @overload @@ -339,6 +353,7 @@ async def set_timeout( domain: Optional[str] = None, debug: Optional[bool] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ) -> None: """ Set the timeout of the specified sandbox. @@ -350,6 +365,7 @@ async def set_timeout( :param sandbox_id: Sandbox ID :param timeout: Timeout for the sandbox in **seconds** :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request """ ... @@ -369,7 +385,7 @@ async def set_timeout( # type: ignore await SandboxApi._cls_set_timeout( sandbox_id=self.sandbox_id, timeout=timeout, - **self.connection_config.__dict__, + **config_dict, ) async def get_info( # type: ignore @@ -391,5 +407,5 @@ async def get_info( # type: ignore return await SandboxApi.get_info( sandbox_id=self.sandbox_id, - **self.connection_config.__dict__, + **config_dict, ) diff --git a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py index 8c235e94fa..2c49044f01 100644 --- a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py @@ -1,7 +1,9 @@ import urllib.parse + from typing import Optional, Dict, List from packaging.version import Version + from e2b.sandbox.sandbox_api import SandboxInfo, SandboxApiBase, SandboxQuery from e2b.exceptions import TemplateException from e2b.api import AsyncApiClient, SandboxCreateResponse @@ -13,7 +15,7 @@ delete_sandboxes_sandbox_id, post_sandboxes, ) -from e2b.connection_config import ConnectionConfig +from e2b.connection_config import ConnectionConfig, ProxyTypes from e2b.api import handle_api_exception @@ -27,6 +29,7 @@ async def list( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> List[SandboxInfo]: """ List all running sandboxes. @@ -37,6 +40,7 @@ async def list( :param debug: Enable debug mode, all requested are then sent to localhost :param request_timeout: Timeout for the request in **seconds** :param headers: Additional headers to send with the request + :param proxy: Proxy to use for the request :return: List of running sandboxes """ @@ -46,6 +50,7 @@ async def list( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) # Convert filters to the format expected by the API @@ -58,7 +63,10 @@ async def list( } metadata = urllib.parse.urlencode(quoted_metadata) - async with AsyncApiClient(config) as api_client: + async with AsyncApiClient( + config, + limits=SandboxApiBase._limits, + ) as api_client: res = await get_sandboxes.asyncio_detailed( client=api_client, metadata=metadata, @@ -96,6 +104,7 @@ async def get_info( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> SandboxInfo: """ Get the sandbox info. @@ -105,6 +114,7 @@ async def get_info( :param debug: Debug mode, defaults to `E2B_DEBUG` environment variable :param request_timeout: Timeout for the request in **seconds** :param headers: Additional headers to send with the request + :param proxy: Proxy to use for the request :return: Sandbox info """ @@ -114,9 +124,13 @@ async def get_info( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) - async with AsyncApiClient(config) as api_client: + async with AsyncApiClient( + config, + limits=SandboxApiBase._limits, + ) as api_client: res = await get_sandboxes_sandbox_id.asyncio_detailed( sandbox_id, client=api_client, @@ -151,6 +165,7 @@ async def _cls_kill( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> bool: config = ConnectionConfig( api_key=api_key, @@ -158,13 +173,17 @@ async def _cls_kill( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) if config.debug: # Skip killing the sandbox in debug mode return True - async with AsyncApiClient(config) as api_client: + async with AsyncApiClient( + config, + limits=SandboxApiBase._limits, + ) as api_client: res = await delete_sandboxes_sandbox_id.asyncio_detailed( sandbox_id, client=api_client, @@ -188,6 +207,7 @@ async def _cls_set_timeout( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> None: config = ConnectionConfig( api_key=api_key, @@ -195,13 +215,17 @@ async def _cls_set_timeout( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) if config.debug: # Skip setting the timeout in debug mode return - async with AsyncApiClient(config) as api_client: + async with AsyncApiClient( + config, + limits=SandboxApiBase._limits, + ) as api_client: res = await post_sandboxes_sandbox_id_timeout.asyncio_detailed( sandbox_id, client=api_client, @@ -223,6 +247,7 @@ async def _create_sandbox( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> SandboxCreateResponse: config = ConnectionConfig( api_key=api_key, @@ -230,9 +255,13 @@ async def _create_sandbox( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) - async with AsyncApiClient(config) as api_client: + async with AsyncApiClient( + config, + limits=SandboxApiBase._limits, + ) as api_client: res = await post_sandboxes.asyncio_detailed( body=NewSandbox( template_id=template, @@ -268,7 +297,3 @@ async def _create_sandbox( ), envd_version=res.parsed.envd_version, ) - - @staticmethod - def _get_sandbox_id(sandbox_id: str, client_id: str) -> str: - return f"{sandbox_id}-{client_id}" diff --git a/packages/python-sdk/e2b/sandbox_sync/main.py b/packages/python-sdk/e2b/sandbox_sync/main.py index 3425bce15d..24d8896f40 100644 --- a/packages/python-sdk/e2b/sandbox_sync/main.py +++ b/packages/python-sdk/e2b/sandbox_sync/main.py @@ -1,8 +1,9 @@ import logging +import httpx + from typing import Dict, Optional, overload -import httpx -from e2b.connection_config import ConnectionConfig +from e2b.connection_config import ConnectionConfig, ProxyTypes from e2b.envd.api import ENVD_API_HEALTH_ROUTE, handle_envd_api_exception from e2b.exceptions import SandboxException, format_request_timeout_error from e2b.sandbox.main import SandboxSetup @@ -97,6 +98,7 @@ def __init__( debug: Optional[bool] = None, sandbox_id: Optional[str] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ): """ Create a new sandbox. @@ -109,6 +111,7 @@ def __init__( :param envs: Custom environment variables for the sandbox :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox** :return: sandbox instance for the new sandbox """ @@ -125,6 +128,7 @@ def __init__( domain=domain, debug=debug, request_timeout=request_timeout, + proxy=proxy, ) if self.connection_config.debug: @@ -145,13 +149,14 @@ def __init__( domain=domain, debug=debug, request_timeout=request_timeout, + proxy=proxy, ) self._sandbox_id = response.sandbox_id self._envd_version = response.envd_version self._envd_api_url = f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(self.envd_port)}" - self._transport = TransportWithLogger(limits=self._limits) + self._transport = TransportWithLogger(limits=self._limits, proxy=proxy) self._envd_api = httpx.Client( base_url=self.envd_api_url, transport=self._transport, @@ -218,6 +223,7 @@ def connect( api_key: Optional[str] = None, domain: Optional[str] = None, debug: Optional[bool] = None, + proxy: Optional[ProxyTypes] = None, ): """ Connects to an existing Sandbox. @@ -225,6 +231,7 @@ def connect( :param sandbox_id: Sandbox ID :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable + :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox** :return: sandbox instance for the existing sandbox @@ -242,6 +249,7 @@ def connect( api_key=api_key, domain=domain, debug=debug, + proxy=proxy, ) def __enter__(self): @@ -269,6 +277,7 @@ def kill( domain: Optional[str] = None, debug: Optional[bool] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ) -> bool: """ Kill the sandbox specified by sandbox ID. @@ -276,6 +285,7 @@ def kill( :param sandbox_id: Sandbox ID :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request :return: `True` if the sandbox was killed, `False` if the sandbox was not found """ @@ -298,7 +308,7 @@ def kill(self, request_timeout: Optional[float] = None) -> bool: # type: ignore SandboxApi._cls_kill( sandbox_id=self.sandbox_id, - **self.connection_config.__dict__, + **config_dict, ) @overload @@ -328,6 +338,7 @@ def set_timeout( domain: Optional[str] = None, debug: Optional[bool] = None, request_timeout: Optional[float] = None, + proxy: Optional[ProxyTypes] = None, ) -> None: """ Set the timeout of the sandbox specified by sandbox ID. @@ -340,6 +351,7 @@ def set_timeout( :param timeout: Timeout for the sandbox in **seconds** :param api_key: E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable :param request_timeout: Timeout for the request in **seconds** + :param proxy: Proxy to use for the request """ ... @@ -359,7 +371,7 @@ def set_timeout( # type: ignore SandboxApi._cls_set_timeout( sandbox_id=self.sandbox_id, timeout=timeout, - **self.connection_config.__dict__, + **config_dict, ) def get_info( # type: ignore @@ -380,5 +392,5 @@ def get_info( # type: ignore return SandboxApi.get_info( sandbox_id=self.sandbox_id, - **self.connection_config.__dict__, + **config_dict, ) diff --git a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py index 3e8e7fdf6a..bf43487bd2 100644 --- a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py @@ -1,7 +1,6 @@ import urllib.parse -from httpx import HTTPTransport -from typing import Optional, Dict, List, Tuple +from typing import Optional, Dict, List from packaging.version import Version from e2b.sandbox.sandbox_api import SandboxInfo, SandboxApiBase, SandboxQuery @@ -15,7 +14,7 @@ delete_sandboxes_sandbox_id, post_sandboxes, ) -from e2b.connection_config import ConnectionConfig +from e2b.connection_config import ConnectionConfig, ProxyTypes from e2b.api import handle_api_exception @@ -29,6 +28,7 @@ def list( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> List[SandboxInfo]: """ List all running sandboxes. @@ -39,6 +39,7 @@ def list( :param debug: Enable debug mode, all requested are then sent to localhost :param request_timeout: Timeout for the request in **seconds** :param headers: Additional headers to send with the request + :param proxy: Proxy to use for the request :return: List of running sandboxes """ @@ -48,6 +49,7 @@ def list( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) # Convert filters to the format expected by the API @@ -61,7 +63,8 @@ def list( metadata = urllib.parse.urlencode(quoted_metadata) with ApiClient( - config, transport=HTTPTransport(limits=SandboxApiBase._limits) + config, + limits=SandboxApiBase._limits, ) as api_client: res = get_sandboxes.sync_detailed(client=api_client, metadata=metadata) @@ -97,6 +100,7 @@ def get_info( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> SandboxInfo: """ Get the sandbox info. @@ -115,10 +119,12 @@ def get_info( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) with ApiClient( - config, transport=HTTPTransport(limits=SandboxApiBase._limits) + config, + limits=SandboxApiBase._limits, ) as api_client: res = get_sandboxes_sandbox_id.sync_detailed( sandbox_id, @@ -154,6 +160,7 @@ def _cls_kill( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> bool: config = ConnectionConfig( api_key=api_key, @@ -161,6 +168,7 @@ def _cls_kill( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) if config.debug: @@ -168,7 +176,8 @@ def _cls_kill( return True with ApiClient( - config, transport=HTTPTransport(limits=SandboxApiBase._limits) + config, + limits=SandboxApiBase._limits, ) as api_client: res = delete_sandboxes_sandbox_id.sync_detailed( sandbox_id, @@ -193,6 +202,7 @@ def _cls_set_timeout( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> None: config = ConnectionConfig( api_key=api_key, @@ -200,6 +210,7 @@ def _cls_set_timeout( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) if config.debug: @@ -207,7 +218,8 @@ def _cls_set_timeout( return with ApiClient( - config, transport=HTTPTransport(limits=SandboxApiBase._limits) + config, + limits=SandboxApiBase._limits, ) as api_client: res = post_sandboxes_sandbox_id_timeout.sync_detailed( sandbox_id, @@ -230,6 +242,7 @@ def _create_sandbox( debug: Optional[bool] = None, request_timeout: Optional[float] = None, headers: Optional[Dict[str, str]] = None, + proxy: Optional[ProxyTypes] = None, ) -> SandboxCreateResponse: config = ConnectionConfig( api_key=api_key, @@ -237,10 +250,12 @@ def _create_sandbox( debug=debug, request_timeout=request_timeout, headers=headers, + proxy=proxy, ) with ApiClient( - config, transport=HTTPTransport(limits=SandboxApiBase._limits) + config, + limits=SandboxApiBase._limits ) as api_client: res = post_sandboxes.sync_detailed( body=NewSandbox(