Skip to content

Commit 019d887

Browse files
mishushakovclaude
andcommitted
fix: address PR review comments for Volume SDK
- Re-export VolumeInfo from API schema types instead of custom definitions - Add res.data undefined check in Volume.create() - Remove readonly cast hack, use optional name constructor param - Fix vol implicit any type in Volume.list() (CI error) - Throw NotFoundError on 404 in Volume.destroy() - Simplify volumeMounts type to only require name (not volumeId) - Remove ternary undefined check for volumeMounts - Move SandboxVolumeMount import to top-level (not inline) - Change AsyncVolume/Volume → VolumeInfo in volume_mounts type annotations - Fix volume_mounts type to List[SandboxVolumeMount] - Export SandboxVolumeMount from package Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9b1f20d commit 019d887

File tree

10 files changed

+43
-72
lines changed

10 files changed

+43
-72
lines changed

packages/js-sdk/src/sandbox/sandboxApi.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export interface SandboxOpts extends ConnectionOpts {
138138
*
139139
* @default undefined
140140
*/
141-
volumeMounts?: Record<string, { volumeId: string; name: string } | string>
141+
volumeMounts?: Record<string, { name: string } | string>
142142

143143
/**
144144
* Sandbox URL. Used for local development
@@ -559,12 +559,12 @@ export class SandboxApi {
559559
secure: opts?.secure ?? true,
560560
allow_internet_access: opts?.allowInternetAccess ?? true,
561561
network: opts?.network,
562-
volumeMounts: opts?.volumeMounts
563-
? Object.entries(opts.volumeMounts).map(([mountPath, vol]) => ({
564-
name: typeof vol === 'string' ? vol : vol.name,
565-
path: mountPath,
566-
}))
567-
: undefined,
562+
volumeMounts: Object.entries(opts?.volumeMounts ?? {}).map(
563+
([mountPath, vol]) => ({
564+
name: typeof vol === 'string' ? vol : vol.name,
565+
path: mountPath,
566+
})
567+
),
568568
},
569569
signal: config.getSignal(opts?.requestTimeoutMs),
570570
})

packages/js-sdk/src/volume.ts

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
1-
import { ApiClient, handleApiError } from './api'
1+
import { ApiClient, handleApiError, components } from './api'
22
import { ConnectionConfig, ConnectionOpts } from './connectionConfig'
33
import { NotFoundError } from './errors'
44

55
/**
66
* Information about a volume.
77
*/
8-
export interface VolumeInfo {
9-
/**
10-
* Volume ID.
11-
*/
12-
volumeId: string
13-
14-
/**
15-
* Volume name.
16-
*/
17-
name: string
18-
}
8+
export type VolumeInfo = components['schemas']['Volume']
199

2010
/**
2111
* Options for request to the Volume API.
@@ -49,10 +39,11 @@ export class Volume {
4939
* Create a local Volume instance with no API call.
5040
*
5141
* @param volumeId volume ID.
42+
* @param name volume name (defaults to volumeId).
5243
*/
53-
constructor(volumeId: string) {
44+
constructor(volumeId: string, name?: string) {
5445
this.volumeId = volumeId
55-
this.name = volumeId
46+
this.name = name ?? volumeId
5647
}
5748

5849
/**
@@ -79,11 +70,11 @@ export class Volume {
7970
throw err
8071
}
8172

82-
const vol = new Volume(res.data!.id)
83-
// Override name with the value returned from the API
84-
;(vol as { name: string }).name = res.data!.name
73+
if (!res.data) {
74+
throw new Error('Body of the response is empty')
75+
}
8576

86-
return vol
77+
return new Volume(res.data.id, res.data.name)
8778
}
8879

8980
/**
@@ -119,10 +110,7 @@ export class Volume {
119110
throw err
120111
}
121112

122-
return {
123-
volumeId: res.data!.id,
124-
name: res.data!.name,
125-
}
113+
return res.data!
126114
}
127115

128116
/**
@@ -145,10 +133,7 @@ export class Volume {
145133
throw err
146134
}
147135

148-
return (res.data ?? []).map((vol) => ({
149-
volumeId: vol.id,
150-
name: vol.name,
151-
}))
136+
return res.data ?? []
152137
}
153138

154139
/**
@@ -171,7 +156,7 @@ export class Volume {
171156
})
172157

173158
if (res.error?.code === 404) {
174-
return
159+
throw new NotFoundError(`Volume ${volumeId} not found`)
175160
}
176161

177162
const err = handleApiError(res)

packages/python-sdk/e2b/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
)
108108
from .template_async.main import AsyncTemplate
109109
from .template_sync.main import Template
110+
from .api.client.models import SandboxVolumeMount
110111
from .volume_info import VolumeInfo
111112
from .volume_sync import Volume
112113
from .volume_async import AsyncVolume
@@ -197,4 +198,5 @@
197198
"Volume",
198199
"AsyncVolume",
199200
"VolumeInfo",
201+
"SandboxVolumeMount",
200202
]

packages/python-sdk/e2b/sandbox_async/main.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
import json
33
import logging
44
import uuid
5-
from typing import TYPE_CHECKING, Dict, List, Optional, Union, overload
5+
from typing import Dict, List, Optional, Union, overload
66

77
import httpx
88
from packaging.version import Version
99
from typing_extensions import Self, Unpack
1010

11-
if TYPE_CHECKING:
12-
from e2b.volume_async import AsyncVolume
13-
11+
from e2b.api.client.models import SandboxVolumeMount
1412
from e2b.api.client.types import Unset
1513
from e2b.api.client_async import get_transport
1614
from e2b.connection_config import ApiParams, ConnectionConfig
@@ -20,6 +18,7 @@
2018
from e2b.sandbox.main import SandboxOpts
2119
from e2b.sandbox.sandbox_api import McpServer, SandboxMetrics, SandboxNetworkOpts
2220
from e2b.sandbox.utils import class_method_variant
21+
from e2b.volume_info import VolumeInfo
2322
from e2b.sandbox_async.commands.command import Commands
2423
from e2b.sandbox_async.commands.pty import Pty
2524
from e2b.sandbox_async.filesystem.filesystem import Filesystem
@@ -165,7 +164,7 @@ async def create(
165164
allow_internet_access: bool = True,
166165
mcp: Optional[McpServer] = None,
167166
network: Optional[SandboxNetworkOpts] = None,
168-
volume_mounts: Optional[Dict[str, Union["AsyncVolume", str]]] = None,
167+
volume_mounts: Optional[Dict[str, Union[VolumeInfo, str]]] = None,
169168
**opts: Unpack[ApiParams],
170169
) -> Self:
171170
"""
@@ -187,8 +186,6 @@ async def create(
187186
188187
Use this method instead of using the constructor to create a new sandbox.
189188
"""
190-
from e2b.api.client.models import SandboxVolumeMount
191-
192189
if not template and mcp is not None:
193190
template = cls.default_mcp_template
194191
elif not template:
@@ -545,7 +542,7 @@ async def beta_create(
545542
secure: bool = True,
546543
allow_internet_access: bool = True,
547544
mcp: Optional[McpServer] = None,
548-
volume_mounts: Optional[Dict[str, Union["AsyncVolume", str]]] = None,
545+
volume_mounts: Optional[Dict[str, Union[VolumeInfo, str]]] = None,
549546
**opts: Unpack[ApiParams],
550547
) -> Self:
551548
"""
@@ -569,8 +566,6 @@ async def beta_create(
569566
570567
Use this method instead of using the constructor to create a new sandbox.
571568
"""
572-
from e2b.api.client.models import SandboxVolumeMount
573-
574569
if not template and mcp is not None:
575570
template = cls.default_mcp_template
576571
elif not template:
@@ -718,7 +713,7 @@ async def _create(
718713
secure: bool,
719714
mcp: Optional[McpServer] = None,
720715
network: Optional[SandboxNetworkOpts] = None,
721-
volume_mounts: Optional[list] = None,
716+
volume_mounts: Optional[List[SandboxVolumeMount]] = None,
722717
**opts: Unpack[ApiParams],
723718
) -> Self:
724719
extra_sandbox_headers = {}

packages/python-sdk/e2b/sandbox_async/sandbox_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
PostSandboxesSandboxIDTimeoutBody,
2222
Sandbox,
2323
SandboxNetworkConfig,
24+
SandboxVolumeMount,
2425
)
2526
from e2b.api.client.types import UNSET
2627
from e2b.api.client_async import get_api_client
@@ -159,7 +160,7 @@ async def _create_sandbox(
159160
secure: bool,
160161
mcp: Optional[McpServer] = None,
161162
network: Optional[SandboxNetworkOpts] = None,
162-
volume_mounts: Optional[list] = None,
163+
volume_mounts: Optional[List[SandboxVolumeMount]] = None,
163164
**opts: Unpack[ApiParams],
164165
) -> SandboxCreateResponse:
165166
config = ConnectionConfig(**opts)

packages/python-sdk/e2b/sandbox_sync/main.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
import json
33
import logging
44
import uuid
5-
from typing import TYPE_CHECKING, Dict, List, Optional, Union, overload
5+
from typing import Dict, List, Optional, Union, overload
66

77
import httpx
88
from packaging.version import Version
99
from typing_extensions import Self, Unpack
1010

11-
if TYPE_CHECKING:
12-
from e2b.volume_sync import Volume
13-
11+
from e2b.api.client.models import SandboxVolumeMount
1412
from e2b.api.client.types import Unset
1513
from e2b.api.client_sync import get_transport
1614
from e2b.connection_config import ApiParams, ConnectionConfig
@@ -20,6 +18,7 @@
2018
from e2b.sandbox.main import SandboxOpts
2119
from e2b.sandbox.sandbox_api import McpServer, SandboxMetrics, SandboxNetworkOpts
2220
from e2b.sandbox.utils import class_method_variant
21+
from e2b.volume_info import VolumeInfo
2322
from e2b.sandbox_sync.commands.command import Commands
2423
from e2b.sandbox_sync.commands.pty import Pty
2524
from e2b.sandbox_sync.filesystem.filesystem import Filesystem
@@ -163,7 +162,7 @@ def create(
163162
allow_internet_access: bool = True,
164163
mcp: Optional[McpServer] = None,
165164
network: Optional[SandboxNetworkOpts] = None,
166-
volume_mounts: Optional[Dict[str, Union["Volume", str]]] = None,
165+
volume_mounts: Optional[Dict[str, Union[VolumeInfo, str]]] = None,
167166
**opts: Unpack[ApiParams],
168167
) -> Self:
169168
"""
@@ -185,8 +184,6 @@ def create(
185184
186185
Use this method instead of using the constructor to create a new sandbox.
187186
"""
188-
from e2b.api.client.models import SandboxVolumeMount
189-
190187
if not template and mcp is not None:
191188
template = cls.default_mcp_template
192189
elif not template:
@@ -546,7 +543,7 @@ def beta_create(
546543
secure: bool = True,
547544
allow_internet_access: bool = True,
548545
mcp: Optional[McpServer] = None,
549-
volume_mounts: Optional[Dict[str, Union["Volume", str]]] = None,
546+
volume_mounts: Optional[Dict[str, Union[VolumeInfo, str]]] = None,
550547
**opts: Unpack[ApiParams],
551548
) -> Self:
552549
"""
@@ -570,8 +567,6 @@ def beta_create(
570567
571568
Use this method instead of using the constructor to create a new sandbox.
572569
"""
573-
from e2b.api.client.models import SandboxVolumeMount
574-
575570
if not template and mcp is not None:
576571
template = cls.default_mcp_template
577572
elif not template:
@@ -710,7 +705,7 @@ def _create(
710705
allow_internet_access: bool,
711706
mcp: Optional[McpServer] = None,
712707
network: Optional[SandboxNetworkOpts] = None,
713-
volume_mounts: Optional[list] = None,
708+
volume_mounts: Optional[List[SandboxVolumeMount]] = None,
714709
**opts: Unpack[ApiParams],
715710
) -> Self:
716711
extra_sandbox_headers = {}

packages/python-sdk/e2b/sandbox_sync/sandbox_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
PostSandboxesSandboxIDTimeoutBody,
2222
Sandbox,
2323
SandboxNetworkConfig,
24+
SandboxVolumeMount,
2425
)
2526
from e2b.api.client.types import UNSET
2627
from e2b.connection_config import ApiParams, ConnectionConfig
@@ -158,7 +159,7 @@ def _create_sandbox(
158159
secure: bool,
159160
mcp: Optional[McpServer] = None,
160161
network: Optional[SandboxNetworkOpts] = None,
161-
volume_mounts: Optional[list] = None,
162+
volume_mounts: Optional[List[SandboxVolumeMount]] = None,
162163
**opts: Unpack[ApiParams],
163164
) -> SandboxCreateResponse:
164165
config = ConnectionConfig(**opts)

packages/python-sdk/e2b/volume_async.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def get_info(volume_id: str, **opts: Unpack[ApiParams]) -> VolumeInfo:
9090
if isinstance(res.parsed, Error):
9191
raise Exception(f"{res.parsed.message}: Request failed")
9292

93-
return VolumeInfo(volume_id=res.parsed.id, name=res.parsed.name)
93+
return res.parsed
9494

9595
@staticmethod
9696
async def list(**opts: Unpack[ApiParams]) -> List[VolumeInfo]:
@@ -115,7 +115,7 @@ async def list(**opts: Unpack[ApiParams]) -> List[VolumeInfo]:
115115
if isinstance(res.parsed, Error):
116116
raise Exception(f"{res.parsed.message}: Request failed")
117117

118-
return [VolumeInfo(volume_id=v.id, name=v.name) for v in res.parsed]
118+
return list(res.parsed)
119119

120120
@staticmethod
121121
async def destroy(volume_id: str, **opts: Unpack[ApiParams]) -> None:
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
from dataclasses import dataclass
1+
from e2b.api.client.models import Volume as VolumeInfo
22

3-
4-
@dataclass
5-
class VolumeInfo:
6-
"""Information about a volume."""
7-
8-
volume_id: str
9-
"""Volume ID."""
10-
name: str
11-
"""Volume name."""
3+
__all__ = ["VolumeInfo"]

packages/python-sdk/e2b/volume_sync.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def get_info(volume_id: str, **opts: Unpack[ApiParams]) -> VolumeInfo:
9090
if isinstance(res.parsed, Error):
9191
raise Exception(f"{res.parsed.message}: Request failed")
9292

93-
return VolumeInfo(volume_id=res.parsed.id, name=res.parsed.name)
93+
return res.parsed
9494

9595
@staticmethod
9696
def list(**opts: Unpack[ApiParams]) -> List[VolumeInfo]:
@@ -115,7 +115,7 @@ def list(**opts: Unpack[ApiParams]) -> List[VolumeInfo]:
115115
if isinstance(res.parsed, Error):
116116
raise Exception(f"{res.parsed.message}: Request failed")
117117

118-
return [VolumeInfo(volume_id=v.id, name=v.name) for v in res.parsed]
118+
return list(res.parsed)
119119

120120
@staticmethod
121121
def destroy(volume_id: str, **opts: Unpack[ApiParams]) -> None:

0 commit comments

Comments
 (0)