diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 0000000000..09e33d32cd --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,62 @@ +name: Typecheck + +on: + pull_request: + +jobs: + typecheck: + name: Typecheck + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Parse .tool-versions + uses: wistia/parse-tool-versions@v2.1.1 + with: + filename: '.tool-versions' + uppercase: 'true' + prefix: 'tool_version_' + + - uses: pnpm/action-setup@v4 + with: + version: '${{ env.TOOL_VERSION_PNPM }}' + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '${{ env.TOOL_VERSION_NODEJS }}' + cache: pnpm + + - name: Configure pnpm + run: | + pnpm config set auto-install-peers true + pnpm config set exclude-links-from-lockfile true + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '${{ env.TOOL_VERSION_PYTHON }}' + + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + with: + version: '${{ env.TOOL_VERSION_POETRY }}' + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + + - name: Install Python dependencies + working-directory: packages/python-sdk + run: | + poetry install --with dev + + - name: Run typecheck + run: | + pnpm run typecheck diff --git a/package.json b/package.json index 801a51370d..c68c763931 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "pnpm-install-hack": "cd packages/js-sdk && sed -i '' 's/\"version\": \".*\"/\"version\": \"9.9.9\"/g' package.json && cd ../.. && pnpm i && git checkout -- packages/js-sdk/package.json", "generate-sdk-reference": "pnpm --if-present --recursive run generate-sdk-reference", "lint": "pnpm --if-present --recursive run lint", + "typecheck": "pnpm --if-present --recursive run typecheck", "format": "pnpm --if-present --recursive run format", "changeset": "pnpx @changesets/cli" }, diff --git a/packages/cli/package.json b/packages/cli/package.json index e7d0126b31..45ad119cf0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,6 +38,7 @@ "prepublishOnly": "pnpm build", "build": "tsc --noEmit --skipLibCheck && tsup --minify", "dev": "tsup --watch", + "typecheck": "tsc --noEmit --skipLibCheck", "lint": "eslint src", "format": "prettier --write src", "test:interactive": "pnpm build && ./dist/index.js", diff --git a/packages/js-sdk/package.json b/packages/js-sdk/package.json index d2ea47d29e..2503d9daa1 100644 --- a/packages/js-sdk/package.json +++ b/packages/js-sdk/package.json @@ -41,6 +41,7 @@ "test:bun": "bun test tests/runtimes/bun --env-file=.env", "test:deno": "deno test tests/runtimes/deno/ --allow-net --allow-read --allow-env --unstable-sloppy-imports --trace-leaks", "test:integration": "E2B_INTEGRATION_TEST=1 vitest run tests/integration/**", + "typecheck": "tsc --noEmit", "lint": "eslint src/ tests/", "format": "prettier --write src/ tests/ example.mts" }, diff --git a/packages/python-sdk/Makefile b/packages/python-sdk/Makefile index 271b0887a5..ae5f09ed9b 100644 --- a/packages/python-sdk/Makefile +++ b/packages/python-sdk/Makefile @@ -23,6 +23,9 @@ generate: generate-api generate-envd generate-mcp init: pip install openapi-python-client datamodel-code-generator +typecheck: + basedpyright + lint: ruff check . ruff format --check . diff --git a/packages/python-sdk/e2b/api/__init__.py b/packages/python-sdk/e2b/api/__init__.py index 4aed46582f..7f7cb6ae0f 100644 --- a/packages/python-sdk/e2b/api/__init__.py +++ b/packages/python-sdk/e2b/api/__init__.py @@ -31,7 +31,7 @@ class SandboxCreateResponse: sandbox_id: str sandbox_domain: Optional[str] envd_version: str - envd_access_token: str + envd_access_token: Optional[str] traffic_access_token: Optional[str] @@ -135,7 +135,7 @@ def __init__( "transport": transport, }, headers=headers, - token=token, + token=token or "", auth_header_name=auth_header_name, prefix=prefix, *args, diff --git a/packages/python-sdk/e2b/api/client_async/__init__.py b/packages/python-sdk/e2b/api/client_async/__init__.py index 5d5c51c28b..7c592ac68d 100644 --- a/packages/python-sdk/e2b/api/client_async/__init__.py +++ b/packages/python-sdk/e2b/api/client_async/__init__.py @@ -3,8 +3,6 @@ from typing import Optional -from typing_extensions import Self - from e2b.connection_config import ConnectionConfig from e2b.api import limits, AsyncApiClient @@ -21,7 +19,7 @@ def get_api_client(config: ConnectionConfig, **kwargs) -> AsyncApiClient: class AsyncTransportWithLogger(httpx.AsyncHTTPTransport): - singleton: Optional[Self] = None + singleton: Optional["AsyncTransportWithLogger"] = None async def handle_async_request(self, request): url = f"{request.url.scheme}://{request.url.host}{request.url.path}" diff --git a/packages/python-sdk/e2b/api/client_sync/__init__.py b/packages/python-sdk/e2b/api/client_sync/__init__.py index 029b3e4e9d..f3d62ca729 100644 --- a/packages/python-sdk/e2b/api/client_sync/__init__.py +++ b/packages/python-sdk/e2b/api/client_sync/__init__.py @@ -3,8 +3,6 @@ import httpx import logging -from typing_extensions import Self - from e2b.api import ApiClient, limits from e2b.connection_config import ConnectionConfig @@ -20,7 +18,7 @@ def get_api_client(config: ConnectionConfig, **kwargs) -> ApiClient: class TransportWithLogger(httpx.HTTPTransport): - singleton: Optional[Self] = None + singleton: Optional["TransportWithLogger"] = None def handle_request(self, request): url = f"{request.url.scheme}://{request.url.host}{request.url.path}" diff --git a/packages/python-sdk/e2b/connection_config.py b/packages/python-sdk/e2b/connection_config.py index adc4908591..d2ddb1a00c 100644 --- a/packages/python-sdk/e2b/connection_config.py +++ b/packages/python-sdk/e2b/connection_config.py @@ -69,7 +69,7 @@ def _api_url(): return os.getenv("E2B_API_URL") @staticmethod - def _sandbox_url(): + def _sandbox_url_env(): return os.getenv("E2B_SANDBOX_URL") @staticmethod @@ -117,7 +117,9 @@ def __init__( or ("http://localhost:3000" if self.debug else f"https://api.{self.domain}") ) - self._sandbox_url = sandbox_url or ConnectionConfig._sandbox_url() + self._sandbox_url: Optional[str] = ( + sandbox_url or ConnectionConfig._sandbox_url_env() + ) @staticmethod def _get_request_timeout( @@ -135,8 +137,9 @@ def get_request_timeout(self, request_timeout: Optional[float] = None): return self._get_request_timeout(self.request_timeout, request_timeout) def get_sandbox_url(self, sandbox_id: str, sandbox_domain: str) -> str: - if self._sandbox_url: - return self._sandbox_url + sandbox_url: Optional[str] = self._sandbox_url + if sandbox_url: + return sandbox_url return f"{'http' if self.debug else 'https'}://{self.get_host(sandbox_id, sandbox_domain, self.envd_port)}" diff --git a/packages/python-sdk/e2b/sandbox/main.py b/packages/python-sdk/e2b/sandbox/main.py index cdfc63a1b2..bba7f66627 100644 --- a/packages/python-sdk/e2b/sandbox/main.py +++ b/packages/python-sdk/e2b/sandbox/main.py @@ -14,7 +14,6 @@ class SandboxOpts(TypedDict): sandbox_domain: Optional[str] envd_version: Version envd_access_token: Optional[str] - sandbox_url: Optional[str] traffic_access_token: Optional[str] connection_config: ConnectionConfig @@ -73,7 +72,7 @@ def traffic_access_token(self) -> Optional[str]: return self.__traffic_access_token @property - def sandbox_domain(self) -> Optional[str]: + def sandbox_domain(self) -> str: return self.__sandbox_domain @property diff --git a/packages/python-sdk/e2b/sandbox/sandbox_api.py b/packages/python-sdk/e2b/sandbox/sandbox_api.py index 3245a512f8..961d853d0a 100644 --- a/packages/python-sdk/e2b/sandbox/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox/sandbox_api.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from datetime import datetime -from typing import Any, Dict, List, Optional, TypedDict, Union +from typing import Any, Dict, List, Optional, TypedDict, Union, cast from typing_extensions import NotRequired, Unpack @@ -117,7 +117,10 @@ def _from_sandbox_data( sandbox_id=sandbox.sandbox_id, template_id=sandbox.template_id, name=(sandbox.alias if isinstance(sandbox.alias, str) else None), - metadata=(sandbox.metadata if isinstance(sandbox.metadata, dict) else {}), + metadata=cast( + Dict[str, str], + sandbox.metadata if isinstance(sandbox.metadata, dict) else {}, + ), started_at=sandbox.started_at, end_at=sandbox.end_at, state=sandbox.state, diff --git a/packages/python-sdk/e2b/sandbox_async/commands/command.py b/packages/python-sdk/e2b/sandbox_async/commands/command.py index 7c94d85671..32b75fd26b 100644 --- a/packages/python-sdk/e2b/sandbox_async/commands/command.py +++ b/packages/python-sdk/e2b/sandbox_async/commands/command.py @@ -238,7 +238,7 @@ async def _start( self, cmd: str, envs: Optional[Dict[str, str]], - user: Username, + user: Optional[Username], cwd: Optional[str], timeout: Optional[float], request_timeout: Optional[float], diff --git a/packages/python-sdk/e2b/sandbox_async/main.py b/packages/python-sdk/e2b/sandbox_async/main.py index bc592f139e..d3f412c000 100644 --- a/packages/python-sdk/e2b/sandbox_async/main.py +++ b/packages/python-sdk/e2b/sandbox_async/main.py @@ -242,13 +242,12 @@ async def connect( ... @overload - @classmethod + @staticmethod async def connect( - cls, sandbox_id: str, timeout: Optional[int] = None, **opts: Unpack[ApiParams], - ) -> Self: + ) -> "AsyncSandbox": """ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed. Sandbox must be either running or be paused. @@ -271,7 +270,7 @@ async def connect( """ ... - @class_method_variant("_cls_connect") + @class_method_variant("_cls_connect_sandbox") async def connect( self, timeout: Optional[int] = None, @@ -643,7 +642,7 @@ async def get_mcp_token(self) -> Optional[str]: return self._mcp_token @classmethod - async def _cls_connect( + async def _cls_connect_sandbox( cls, sandbox_id: str, timeout: Optional[int] = None, @@ -667,10 +666,16 @@ async def _cls_connect( return cls( sandbox_id=sandbox.sandbox_id, - sandbox_domain=sandbox.domain, + sandbox_domain=sandbox.domain + if not isinstance(sandbox.domain, Unset) + else None, envd_version=Version(sandbox.envd_version), - envd_access_token=envd_access_token, - traffic_access_token=sandbox.traffic_access_token, + envd_access_token=envd_access_token + if not isinstance(envd_access_token, Unset) + else None, + traffic_access_token=sandbox.traffic_access_token + if not isinstance(sandbox.traffic_access_token, Unset) + else None, connection_config=connection_config, ) diff --git a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py index aabbd709c3..a7e73a201a 100644 --- a/packages/python-sdk/e2b/sandbox_async/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_async/sandbox_api.py @@ -1,5 +1,5 @@ import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional, cast from packaging.version import Version from typing_extensions import Unpack @@ -171,7 +171,7 @@ async def _create_sandbox( metadata=metadata or {}, timeout=timeout, env_vars=env_vars or {}, - mcp=mcp or UNSET, + mcp=cast(Any, mcp) if mcp is not None else UNSET, secure=secure, allow_internet_access=allow_internet_access, network=SandboxNetworkConfig(**network) if network else UNSET, @@ -195,12 +195,24 @@ async def _create_sandbox( "You can do this by running `e2b template build` in the directory with the template." ) + domain = res.parsed.domain if isinstance(res.parsed.domain, str) else None + envd_token = ( + res.parsed.envd_access_token + if isinstance(res.parsed.envd_access_token, str) + else None + ) + traffic_token = ( + res.parsed.traffic_access_token + if isinstance(res.parsed.traffic_access_token, str) + else None + ) + return SandboxCreateResponse( sandbox_id=res.parsed.sandbox_id, - sandbox_domain=res.parsed.domain, + sandbox_domain=domain, envd_version=res.parsed.envd_version, - envd_access_token=res.parsed.envd_access_token, - traffic_access_token=res.parsed.traffic_access_token, + envd_access_token=envd_token, + traffic_access_token=traffic_token, ) @classmethod @@ -322,4 +334,7 @@ async def _cls_connect( if isinstance(res.parsed, Error): raise SandboxException(f"{res.parsed.message}: Request failed") + if res.parsed is None: + raise SandboxException("Body of the request is None") + return res.parsed diff --git a/packages/python-sdk/e2b/sandbox_sync/commands/command.py b/packages/python-sdk/e2b/sandbox_sync/commands/command.py index 79c1b951c8..512b7d9923 100644 --- a/packages/python-sdk/e2b/sandbox_sync/commands/command.py +++ b/packages/python-sdk/e2b/sandbox_sync/commands/command.py @@ -240,7 +240,7 @@ def _start( self, cmd: str, envs: Optional[Dict[str, str]], - user: Username, + user: Optional[Username], cwd: Optional[str], stdin: bool, timeout: Optional[float], diff --git a/packages/python-sdk/e2b/sandbox_sync/main.py b/packages/python-sdk/e2b/sandbox_sync/main.py index 366def2234..be9f7253c5 100644 --- a/packages/python-sdk/e2b/sandbox_sync/main.py +++ b/packages/python-sdk/e2b/sandbox_sync/main.py @@ -241,13 +241,12 @@ def connect( ... @overload - @classmethod + @staticmethod def connect( - cls, sandbox_id: str, timeout: Optional[int] = None, **opts: Unpack[ApiParams], - ) -> Self: + ) -> "Sandbox": """ Connect to a sandbox. If the sandbox is paused, it will be automatically resumed. Sandbox must be either running or be paused. @@ -270,7 +269,7 @@ def connect( """ ... - @class_method_variant("_cls_connect") + @class_method_variant("_cls_connect_sandbox") def connect( self, timeout: Optional[int] = None, @@ -595,9 +594,8 @@ def beta_pause( ... @overload - @classmethod + @staticmethod def beta_pause( - cls, sandbox_id: str, **opts: Unpack[ApiParams], ) -> None: @@ -639,7 +637,7 @@ def get_mcp_token(self) -> Optional[str]: return self._mcp_token @classmethod - def _cls_connect( + def _cls_connect_sandbox( cls, sandbox_id: str, timeout: Optional[int] = None, @@ -659,11 +657,17 @@ def _cls_connect( return cls( sandbox_id=sandbox_id, - sandbox_domain=sandbox.domain, + sandbox_domain=sandbox.domain + if not isinstance(sandbox.domain, Unset) + else None, connection_config=connection_config, envd_version=Version(sandbox.envd_version), - envd_access_token=envd_access_token, - traffic_access_token=sandbox.traffic_access_token, + envd_access_token=envd_access_token + if not isinstance(envd_access_token, Unset) + else None, + traffic_access_token=sandbox.traffic_access_token + if not isinstance(sandbox.traffic_access_token, Unset) + else None, ) @classmethod diff --git a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py index 18a3af798d..a58f87c6e0 100644 --- a/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py +++ b/packages/python-sdk/e2b/sandbox_sync/sandbox_api.py @@ -1,5 +1,5 @@ import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional, cast from packaging.version import Version from typing_extensions import Unpack @@ -170,7 +170,7 @@ def _create_sandbox( metadata=metadata or {}, timeout=timeout, env_vars=env_vars or {}, - mcp=mcp or UNSET, + mcp=cast(Any, mcp) if mcp is not None else UNSET, secure=secure, allow_internet_access=allow_internet_access, network=SandboxNetworkConfig(**network) if network else UNSET, @@ -194,12 +194,24 @@ def _create_sandbox( "You can do this by running `e2b template build` in the directory with the template." ) + domain = res.parsed.domain if isinstance(res.parsed.domain, str) else None + envd_token = ( + res.parsed.envd_access_token + if isinstance(res.parsed.envd_access_token, str) + else None + ) + traffic_token = ( + res.parsed.traffic_access_token + if isinstance(res.parsed.traffic_access_token, str) + else None + ) + return SandboxCreateResponse( sandbox_id=res.parsed.sandbox_id, - sandbox_domain=res.parsed.domain, + sandbox_domain=domain, envd_version=res.parsed.envd_version, - envd_access_token=res.parsed.envd_access_token, - traffic_access_token=res.parsed.traffic_access_token, + envd_access_token=envd_token, + traffic_access_token=traffic_token, ) @classmethod @@ -219,8 +231,8 @@ def _cls_get_metrics( api_client = get_api_client(config) res = get_sandboxes_sandbox_id_metrics.sync_detailed( sandbox_id, - start=int(start.timestamp()) if start else None, - end=int(end.timestamp()) if end else None, + start=int(start.timestamp()) if start else UNSET, + end=int(end.timestamp()) if end else UNSET, client=api_client, ) @@ -280,6 +292,9 @@ def _cls_connect( if isinstance(res.parsed, Error): raise SandboxException(f"{res.parsed.message}: Request failed") + if res.parsed is None: + raise SandboxException("Body of the request is None") + return res.parsed @classmethod diff --git a/packages/python-sdk/e2b/template/dockerfile_parser.py b/packages/python-sdk/e2b/template/dockerfile_parser.py index c03b164478..7c5c34ce47 100644 --- a/packages/python-sdk/e2b/template/dockerfile_parser.py +++ b/packages/python-sdk/e2b/template/dockerfile_parser.py @@ -5,7 +5,6 @@ from typing import Dict, List, Optional, Protocol, Union, Literal from dockerfile_parse import DockerfileParser -from e2b.template.types import CopyItem class DockerfFileFinalParserInterface(Protocol): @@ -23,12 +22,12 @@ def run_cmd( def copy( self, - src: Union[str, List[CopyItem]], - dest: Optional[str] = None, + src: str, + dest: str, force_upload: Optional[Literal[True]] = None, - resolve_symlinks: Optional[bool] = None, user: Optional[str] = None, mode: Optional[int] = None, + resolve_symlinks: Optional[bool] = None, ) -> "DockerfileParserInterface": """Handle COPY instruction.""" ... diff --git a/packages/python-sdk/e2b/template_async/main.py b/packages/python-sdk/e2b/template_async/main.py index a459af2cc9..c57c4d5fee 100644 --- a/packages/python-sdk/e2b/template_async/main.py +++ b/packages/python-sdk/e2b/template_async/main.py @@ -99,7 +99,7 @@ async def _build( src = args[0] if len(args) > 0 else None force_upload = file_upload.get("forceUpload") files_hash = file_upload.get("filesHash", None) - resolve_symlinks = file_upload.get("resolveSymlinks", RESOLVE_SYMLINKS) + resolve_symlinks = file_upload.get("resolveSymlinks") or RESOLVE_SYMLINKS if src is None or files_hash is None: raise ValueError("Source path and files hash are required") diff --git a/packages/python-sdk/e2b/template_sync/main.py b/packages/python-sdk/e2b/template_sync/main.py index 1278e4d0c6..ba93951095 100644 --- a/packages/python-sdk/e2b/template_sync/main.py +++ b/packages/python-sdk/e2b/template_sync/main.py @@ -99,7 +99,7 @@ def _build( src = args[0] if len(args) > 0 else None force_upload = file_upload.get("forceUpload") files_hash = file_upload.get("filesHash", None) - resolve_symlinks = file_upload.get("resolveSymlinks", RESOLVE_SYMLINKS) + resolve_symlinks = file_upload.get("resolveSymlinks") or RESOLVE_SYMLINKS if src is None or files_hash is None: raise ValueError("Source path and files hash are required") diff --git a/packages/python-sdk/package.json b/packages/python-sdk/package.json index 9cdc28f93d..212409a7f8 100644 --- a/packages/python-sdk/package.json +++ b/packages/python-sdk/package.json @@ -9,6 +9,7 @@ "postPublish": "poetry build && poetry config pypi-token.pypi ${PYPI_TOKEN} && poetry publish --skip-existing", "pretest": "poetry install", "generate-ref": "poetry install && ./scripts/generate_sdk_ref.sh", + "typecheck": "poetry run make typecheck", "lint": "poetry run make lint", "format": "poetry run make format" } diff --git a/packages/python-sdk/poetry.lock b/packages/python-sdk/poetry.lock index 3c180a9120..505c677df0 100644 --- a/packages/python-sdk/poetry.lock +++ b/packages/python-sdk/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -60,6 +60,21 @@ files = [ {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] +[[package]] +name = "basedpyright" +version = "1.37.4" +description = "static type checking for Python (but based)" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "basedpyright-1.37.4-py3-none-any.whl", hash = "sha256:bcf61d7d8dbd4570f346008fa591585bd605ce47a0561509899c276f2e53a450"}, + {file = "basedpyright-1.37.4.tar.gz", hash = "sha256:f818d8b56c1e7f639dfbdaf875aa6b0bd53eef08204389959027d3d7fb2017ed"}, +] + +[package.dependencies] +nodejs-wheel-binaries = ">=20.13.1" + [[package]] name = "black" version = "25.9.0" @@ -277,7 +292,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -449,7 +464,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -792,6 +807,25 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] +[[package]] +name = "nodejs-wheel-binaries" +version = "24.13.0" +description = "unoffical Node.js package" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:356654baa37bfd894e447e7e00268db403ea1d223863963459a0fbcaaa1d9d48"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:92fdef7376120e575f8b397789bafcb13bbd22a1b4d21b060d200b14910f22a5"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3f619ac140e039ecd25f2f71d6e83ad1414017a24608531851b7c31dc140cdfd"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:dfb31ebc2c129538192ddb5bedd3d63d6de5d271437cd39ea26bf3fe229ba430"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fdd720d7b378d5bb9b2710457bbc880d4c4d1270a94f13fbe257198ac707f358"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ad6383613f3485a75b054647a09f1cd56d12380d7459184eebcf4a5d403f35c"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-win_amd64.whl", hash = "sha256:605be4763e3ef427a3385a55da5a1bcf0a659aa2716eebbf23f332926d7e5f23"}, + {file = "nodejs_wheel_binaries-24.13.0-py2.py3-none-win_arm64.whl", hash = "sha256:2e3431d869d6b2dbeef1d469ad0090babbdcc8baaa72c01dd3cc2c6121c96af5"}, + {file = "nodejs_wheel_binaries-24.13.0.tar.gz", hash = "sha256:766aed076e900061b83d3e76ad48bfec32a035ef0d41bd09c55e832eb93ef7a4"}, +] + [[package]] name = "nr-date" version = "2.1.0" @@ -1726,4 +1760,4 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "95c546174e29cc21728f79280a8bd0c33d363455ab215ada6f007d0174002297" +content-hash = "fb1c50ac2a8179907917516f507ebe42cbbd03b9d1c471f45892ef1e15370cc9" diff --git a/packages/python-sdk/pyproject.toml b/packages/python-sdk/pyproject.toml index 198cc982e1..5cc69ea221 100644 --- a/packages/python-sdk/pyproject.toml +++ b/packages/python-sdk/pyproject.toml @@ -32,6 +32,7 @@ pydoc-markdown = "^4.8.2" datamodel-code-generator = "^0.34.0" ruff = "^0.11.12" pytest-timeout = "^2.4.0" +basedpyright = "^1.29.0" [build-system] requires = ["poetry-core"] @@ -40,6 +41,16 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.urls] "Bug Tracker" = "https://github.com/e2b-dev/e2b/issues" +[tool.basedpyright] +typeCheckingMode = "standard" +reportInconsistentOverload = false +reportIncompatibleMethodOverride = false +exclude = [ + "e2b/api/client/models", + "e2b/envd", + "tests/bugs", +] + [tool.ruff] exclude = [ "e2b/envd/filesystem/filesystem_pb2.py" diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py index bc876b1068..12daab0174 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_files_list.py @@ -1,4 +1,5 @@ import uuid +from typing import Any from e2b import AsyncSandbox, FileType @@ -16,7 +17,7 @@ async def test_list_directory(async_sandbox: AsyncSandbox): await async_sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") await async_sandbox.files.write(f"{parent_dir_name}/file1.txt", "Hello, world!") - test_cases = [ + test_cases: list[dict[str, Any]] = [ { "name": "default depth (1)", "depth": None, diff --git a/packages/python-sdk/tests/async/sandbox_async/files/test_write.py b/packages/python-sdk/tests/async/sandbox_async/files/test_write.py index 13925ed64b..e740c756b9 100644 --- a/packages/python-sdk/tests/async/sandbox_async/files/test_write.py +++ b/packages/python-sdk/tests/async/sandbox_async/files/test_write.py @@ -69,6 +69,7 @@ async def test_write_multiple_files(async_sandbox: AsyncSandbox, debug): # Attempt to write with multiple files in array files = [] + path = "" for i in range(num_test_files): path = f"test_write_{i}.txt" content = f"This is a test file {i}." diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py index f4fbfebbbd..a3622c2c3d 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_files_list.py @@ -1,4 +1,5 @@ import uuid +from typing import Any from e2b import Sandbox, FileType @@ -16,7 +17,7 @@ def test_list_directory(sandbox: Sandbox): sandbox.files.make_dir(f"{parent_dir_name}/subdir2/subdir2_2") sandbox.files.write(f"{parent_dir_name}/file1.txt", "Hello, world!") - test_cases = [ + test_cases: list[dict[str, Any]] = [ { "name": "default depth (1)", "depth": None, diff --git a/packages/python-sdk/tests/sync/sandbox_sync/files/test_write.py b/packages/python-sdk/tests/sync/sandbox_sync/files/test_write.py index f5c5a43f7f..86435279b8 100644 --- a/packages/python-sdk/tests/sync/sandbox_sync/files/test_write.py +++ b/packages/python-sdk/tests/sync/sandbox_sync/files/test_write.py @@ -70,6 +70,7 @@ def test_write_multiple_files(sandbox, debug): # Attempt to write with multiple files in array files = [] + path = "" for i in range(num_test_files): path = f"test_write_{i}.txt" content = f"This is a test file {i}."