Skip to content

Commit c38a181

Browse files
mishushakovclaudegithub-advanced-security[bot]
authored
Fix Python SDK type issues with ty type checker (#1122)
## Summary - Resolved 43 type diagnostics reported by ty (Astral's Python type checker) - Fixed Self type issues on class singletons - Added explicit type annotations for shadowed attributes - Replaced None with UNSET for auto-generated API parameters - Fixed method signature alignment for protocol matching - Added targeted type: ignore suppressions for pattern-based limitations All checks pass: ty check, ruff format, ruff check. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Mostly typing/CI changes, but some adjustments affect sandbox connect/pause overload dispatch and API response/parameter handling (`UNSET` vs `None`), which could alter edge-case runtime behavior. > > **Overview** > Fixes Python SDK static typing issues for Astral’s `ty` checker and wires typechecking into CI. > > Adds a new `Typecheck` GitHub Action plus workspace `typecheck` scripts (TS packages via `tsc`, Python SDK via `make typecheck` running `ty`), and publishes a patch changeset for `@e2b/python-sdk`. > > Across the Python SDK, adjusts type annotations and overloads (e.g., `Self`/singleton typing, `connect` overloads, optional `user`/token/domain handling), tightens API model parsing with `cast`/`Optional` checks and `UNSET` usage, and adds a few targeted `ty` ignore comments in tests/protocols to silence checker limitations. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit f664028. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 9d19e2d commit c38a181

26 files changed

Lines changed: 204 additions & 52 deletions

File tree

.changeset/tangy-kiwis-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@e2b/python-sdk': patch
3+
---
4+
5+
fixes type errors when using with ty

.github/workflows/typecheck.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Typecheck
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
typecheck:
8+
name: Typecheck
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
13+
steps:
14+
- name: Checkout Repo
15+
uses: actions/checkout@v4
16+
17+
- name: Parse .tool-versions
18+
uses: wistia/parse-tool-versions@v2.1.1
19+
with:
20+
filename: '.tool-versions'
21+
uppercase: 'true'
22+
prefix: 'tool_version_'
23+
24+
- uses: pnpm/action-setup@v4
25+
with:
26+
version: '${{ env.TOOL_VERSION_PNPM }}'
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: '${{ env.TOOL_VERSION_NODEJS }}'
32+
cache: pnpm
33+
34+
- name: Configure pnpm
35+
run: |
36+
pnpm config set auto-install-peers true
37+
pnpm config set exclude-links-from-lockfile true
38+
39+
- name: Install dependencies
40+
run: pnpm install --frozen-lockfile
41+
42+
- name: Set up Python
43+
uses: actions/setup-python@v4
44+
with:
45+
python-version: '${{ env.TOOL_VERSION_PYTHON }}'
46+
47+
- name: Install and configure Poetry
48+
uses: snok/install-poetry@v1
49+
with:
50+
version: '${{ env.TOOL_VERSION_POETRY }}'
51+
virtualenvs-create: true
52+
virtualenvs-in-project: true
53+
installer-parallel: true
54+
55+
- name: Install Python dependencies
56+
working-directory: packages/python-sdk
57+
run: |
58+
poetry install --with dev
59+
60+
- name: Run typecheck
61+
run: |
62+
pnpm run typecheck

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"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",
1313
"generate-sdk-reference": "pnpm --if-present --recursive run generate-sdk-reference",
1414
"lint": "pnpm --if-present --recursive run lint",
15+
"typecheck": "pnpm --if-present --recursive run typecheck",
1516
"format": "pnpm --if-present --recursive run format",
1617
"changeset": "pnpx @changesets/cli"
1718
},

packages/cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"prepublishOnly": "pnpm build",
3939
"build": "tsc --noEmit --skipLibCheck && tsup --minify",
4040
"dev": "tsup --watch",
41+
"typecheck": "tsc --noEmit --skipLibCheck",
4142
"lint": "eslint src",
4243
"format": "prettier --write src",
4344
"test:interactive": "pnpm build && ./dist/index.js",

packages/js-sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"test:bun": "bun test tests/runtimes/bun --env-file=.env",
4242
"test:deno": "deno test tests/runtimes/deno/ --allow-net --allow-read --allow-env --unstable-sloppy-imports --trace-leaks",
4343
"test:integration": "E2B_INTEGRATION_TEST=1 vitest run tests/integration/**",
44+
"typecheck": "tsc --noEmit",
4445
"lint": "eslint src/ tests/",
4546
"format": "prettier --write src/ tests/ example.mts"
4647
},

packages/python-sdk/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ generate: generate-api generate-envd generate-mcp
2323
init:
2424
pip install openapi-python-client datamodel-code-generator
2525

26+
typecheck:
27+
ty check
28+
2629
lint:
2730
ruff check .
2831
ruff format --check .

packages/python-sdk/e2b/api/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class SandboxCreateResponse:
3131
sandbox_id: str
3232
sandbox_domain: Optional[str]
3333
envd_version: str
34-
envd_access_token: str
34+
envd_access_token: Optional[str]
3535
traffic_access_token: Optional[str]
3636

3737

@@ -135,7 +135,7 @@ def __init__(
135135
"transport": transport,
136136
},
137137
headers=headers,
138-
token=token,
138+
token=token or "",
139139
auth_header_name=auth_header_name,
140140
prefix=prefix,
141141
*args,

packages/python-sdk/e2b/api/client_async/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
from typing import Optional
55

6-
from typing_extensions import Self
7-
86
from e2b.connection_config import ConnectionConfig
97
from e2b.api import limits, AsyncApiClient
108

@@ -21,7 +19,7 @@ def get_api_client(config: ConnectionConfig, **kwargs) -> AsyncApiClient:
2119

2220

2321
class AsyncTransportWithLogger(httpx.AsyncHTTPTransport):
24-
singleton: Optional[Self] = None
22+
singleton: Optional["AsyncTransportWithLogger"] = None
2523

2624
async def handle_async_request(self, request):
2725
url = f"{request.url.scheme}://{request.url.host}{request.url.path}"

packages/python-sdk/e2b/api/client_sync/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import httpx
44
import logging
55

6-
from typing_extensions import Self
7-
86
from e2b.api import ApiClient, limits
97
from e2b.connection_config import ConnectionConfig
108

@@ -20,7 +18,7 @@ def get_api_client(config: ConnectionConfig, **kwargs) -> ApiClient:
2018

2119

2220
class TransportWithLogger(httpx.HTTPTransport):
23-
singleton: Optional[Self] = None
21+
singleton: Optional["TransportWithLogger"] = None
2422

2523
def handle_request(self, request):
2624
url = f"{request.url.scheme}://{request.url.host}{request.url.path}"

packages/python-sdk/e2b/connection_config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ def __init__(
117117
or ("http://localhost:3000" if self.debug else f"https://api.{self.domain}")
118118
)
119119

120-
self._sandbox_url = sandbox_url or ConnectionConfig._sandbox_url()
120+
self._sandbox_url: Optional[str] = (
121+
sandbox_url or ConnectionConfig._sandbox_url()
122+
)
121123

122124
@staticmethod
123125
def _get_request_timeout(
@@ -136,7 +138,7 @@ def get_request_timeout(self, request_timeout: Optional[float] = None):
136138

137139
def get_sandbox_url(self, sandbox_id: str, sandbox_domain: str) -> str:
138140
if self._sandbox_url:
139-
return self._sandbox_url
141+
return self._sandbox_url # type: ignore[return-value]
140142

141143
return f"{'http' if self.debug else 'https'}://{self.get_host(sandbox_id, sandbox_domain, self.envd_port)}"
142144

0 commit comments

Comments
 (0)