code-sandboxes 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_sandboxes/__init__.py +141 -0
- code_sandboxes/__version__.py +6 -0
- code_sandboxes/base.py +572 -0
- code_sandboxes/commands.py +452 -0
- code_sandboxes/exceptions.py +101 -0
- code_sandboxes/filesystem.py +500 -0
- code_sandboxes/local/__init__.py +9 -0
- code_sandboxes/local/eval_sandbox.py +309 -0
- code_sandboxes/models.py +392 -0
- code_sandboxes/remote/__init__.py +9 -0
- code_sandboxes/remote/datalayer_sandbox.py +627 -0
- code_sandboxes-0.0.2.dist-info/METADATA +299 -0
- code_sandboxes-0.0.2.dist-info/RECORD +15 -0
- code_sandboxes-0.0.2.dist-info/WHEEL +4 -0
- code_sandboxes-0.0.2.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
2
|
+
#
|
|
3
|
+
# BSD 3-Clause License
|
|
4
|
+
|
|
5
|
+
"""Command execution for sandboxes.
|
|
6
|
+
|
|
7
|
+
Inspired by E2B Commands and Modal exec APIs.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import TYPE_CHECKING, Iterator, Optional, Union
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .base import Sandbox
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class CommandResult:
|
|
20
|
+
"""Result of a command execution.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
exit_code: The command's exit code.
|
|
24
|
+
stdout: Standard output content.
|
|
25
|
+
stderr: Standard error content.
|
|
26
|
+
duration: Execution duration in seconds.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
exit_code: int = 0
|
|
30
|
+
stdout: str = ""
|
|
31
|
+
stderr: str = ""
|
|
32
|
+
duration: float = 0.0
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def success(self) -> bool:
|
|
36
|
+
"""Whether the command succeeded (exit code 0)."""
|
|
37
|
+
return self.exit_code == 0
|
|
38
|
+
|
|
39
|
+
def __repr__(self) -> str:
|
|
40
|
+
return f"CommandResult(exit_code={self.exit_code}, stdout_len={len(self.stdout)}, stderr_len={len(self.stderr)})"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class ProcessHandle:
|
|
45
|
+
"""Handle for a running process.
|
|
46
|
+
|
|
47
|
+
Similar to Modal's ContainerProcess interface.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
pid: Process ID (if available).
|
|
51
|
+
command: The command being executed.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
sandbox: "Sandbox"
|
|
55
|
+
command: str
|
|
56
|
+
pid: Optional[int] = None
|
|
57
|
+
_process_var: str = ""
|
|
58
|
+
_completed: bool = False
|
|
59
|
+
_exit_code: Optional[int] = None
|
|
60
|
+
_stdout_buffer: list[str] = field(default_factory=list)
|
|
61
|
+
_stderr_buffer: list[str] = field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
def __post_init__(self):
|
|
64
|
+
self._process_var = f"__proc_{id(self)}__"
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def stdout(self) -> Iterator[str]:
|
|
68
|
+
"""Stream stdout lines.
|
|
69
|
+
|
|
70
|
+
Yields:
|
|
71
|
+
Lines from stdout as they become available.
|
|
72
|
+
"""
|
|
73
|
+
# For synchronous implementation, return buffered output
|
|
74
|
+
for line in self._stdout_buffer:
|
|
75
|
+
yield line
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def stderr(self) -> Iterator[str]:
|
|
79
|
+
"""Stream stderr lines.
|
|
80
|
+
|
|
81
|
+
Yields:
|
|
82
|
+
Lines from stderr as they become available.
|
|
83
|
+
"""
|
|
84
|
+
for line in self._stderr_buffer:
|
|
85
|
+
yield line
|
|
86
|
+
|
|
87
|
+
def read_stdout(self) -> str:
|
|
88
|
+
"""Read all stdout content.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
All stdout content as a string.
|
|
92
|
+
"""
|
|
93
|
+
return "\n".join(self._stdout_buffer)
|
|
94
|
+
|
|
95
|
+
def read_stderr(self) -> str:
|
|
96
|
+
"""Read all stderr content.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
All stderr content as a string.
|
|
100
|
+
"""
|
|
101
|
+
return "\n".join(self._stderr_buffer)
|
|
102
|
+
|
|
103
|
+
def wait(self, timeout: Optional[float] = None) -> int:
|
|
104
|
+
"""Wait for the process to complete.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
timeout: Maximum time to wait in seconds.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The exit code.
|
|
111
|
+
"""
|
|
112
|
+
if self._completed:
|
|
113
|
+
return self._exit_code or 0
|
|
114
|
+
|
|
115
|
+
self.sandbox.run_code(f"""
|
|
116
|
+
{self._process_var}.wait()
|
|
117
|
+
__exit_code__ = {self._process_var}.returncode
|
|
118
|
+
""")
|
|
119
|
+
self._exit_code = self.sandbox.get_variable("__exit_code__")
|
|
120
|
+
self._completed = True
|
|
121
|
+
return self._exit_code
|
|
122
|
+
|
|
123
|
+
def poll(self) -> Optional[int]:
|
|
124
|
+
"""Check if the process has completed.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Exit code if completed, None otherwise.
|
|
128
|
+
"""
|
|
129
|
+
if self._completed:
|
|
130
|
+
return self._exit_code
|
|
131
|
+
|
|
132
|
+
self.sandbox.run_code(f"""
|
|
133
|
+
__poll_result__ = {self._process_var}.poll()
|
|
134
|
+
""")
|
|
135
|
+
result = self.sandbox.get_variable("__poll_result__")
|
|
136
|
+
if result is not None:
|
|
137
|
+
self._exit_code = result
|
|
138
|
+
self._completed = True
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
def terminate(self) -> None:
|
|
142
|
+
"""Terminate the process."""
|
|
143
|
+
self.sandbox.run_code(f"""
|
|
144
|
+
try:
|
|
145
|
+
{self._process_var}.terminate()
|
|
146
|
+
except:
|
|
147
|
+
pass
|
|
148
|
+
""")
|
|
149
|
+
self._completed = True
|
|
150
|
+
|
|
151
|
+
def kill(self) -> None:
|
|
152
|
+
"""Kill the process forcefully."""
|
|
153
|
+
self.sandbox.run_code(f"""
|
|
154
|
+
try:
|
|
155
|
+
{self._process_var}.kill()
|
|
156
|
+
except:
|
|
157
|
+
pass
|
|
158
|
+
""")
|
|
159
|
+
self._completed = True
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def returncode(self) -> Optional[int]:
|
|
163
|
+
"""Get the return code if process has completed."""
|
|
164
|
+
if self._completed:
|
|
165
|
+
return self._exit_code
|
|
166
|
+
return self.poll()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class SandboxCommands:
|
|
170
|
+
"""Command execution for a sandbox.
|
|
171
|
+
|
|
172
|
+
Provides terminal command execution similar to E2B and Modal.
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
with Sandbox.create() as sandbox:
|
|
176
|
+
# Run a simple command
|
|
177
|
+
result = sandbox.commands.run("ls -la")
|
|
178
|
+
print(result.stdout)
|
|
179
|
+
|
|
180
|
+
# Run with streaming
|
|
181
|
+
process = sandbox.commands.exec("python", "-c", "print('hello')")
|
|
182
|
+
for line in process.stdout:
|
|
183
|
+
print(line)
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def __init__(self, sandbox: "Sandbox"):
|
|
187
|
+
"""Initialize command operations for a sandbox.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
sandbox: The sandbox instance.
|
|
191
|
+
"""
|
|
192
|
+
self._sandbox = sandbox
|
|
193
|
+
|
|
194
|
+
def run(
|
|
195
|
+
self,
|
|
196
|
+
command: str,
|
|
197
|
+
cwd: Optional[str] = None,
|
|
198
|
+
env: Optional[dict[str, str]] = None,
|
|
199
|
+
timeout: Optional[float] = None,
|
|
200
|
+
shell: bool = True,
|
|
201
|
+
) -> CommandResult:
|
|
202
|
+
"""Run a command and wait for completion.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
command: The command to run.
|
|
206
|
+
cwd: Working directory.
|
|
207
|
+
env: Environment variables.
|
|
208
|
+
timeout: Timeout in seconds.
|
|
209
|
+
shell: Whether to run through shell.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
CommandResult with exit code, stdout, stderr.
|
|
213
|
+
"""
|
|
214
|
+
start_time = time.time()
|
|
215
|
+
|
|
216
|
+
# Build the subprocess code
|
|
217
|
+
env_str = f", env={{**os.environ, **{env!r}}}" if env else ""
|
|
218
|
+
cwd_str = f", cwd={cwd!r}" if cwd else ""
|
|
219
|
+
timeout_str = f", timeout={timeout}" if timeout else ""
|
|
220
|
+
|
|
221
|
+
code = f"""
|
|
222
|
+
import subprocess
|
|
223
|
+
import os
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
__cmd_result__ = subprocess.run(
|
|
227
|
+
{command!r},
|
|
228
|
+
shell={shell},
|
|
229
|
+
capture_output=True,
|
|
230
|
+
text=True{cwd_str}{env_str}{timeout_str}
|
|
231
|
+
)
|
|
232
|
+
__cmd_output__ = {{
|
|
233
|
+
'exit_code': __cmd_result__.returncode,
|
|
234
|
+
'stdout': __cmd_result__.stdout,
|
|
235
|
+
'stderr': __cmd_result__.stderr,
|
|
236
|
+
}}
|
|
237
|
+
except subprocess.TimeoutExpired:
|
|
238
|
+
__cmd_output__ = {{
|
|
239
|
+
'exit_code': -1,
|
|
240
|
+
'stdout': '',
|
|
241
|
+
'stderr': 'Command timed out',
|
|
242
|
+
}}
|
|
243
|
+
except Exception as e:
|
|
244
|
+
__cmd_output__ = {{
|
|
245
|
+
'exit_code': -1,
|
|
246
|
+
'stdout': '',
|
|
247
|
+
'stderr': str(e),
|
|
248
|
+
}}
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
execution = self._sandbox.run_code(code, timeout=timeout)
|
|
252
|
+
|
|
253
|
+
if execution.error:
|
|
254
|
+
return CommandResult(
|
|
255
|
+
exit_code=-1,
|
|
256
|
+
stdout="",
|
|
257
|
+
stderr=str(execution.error),
|
|
258
|
+
duration=time.time() - start_time,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
result = self._sandbox.get_variable("__cmd_output__")
|
|
263
|
+
return CommandResult(
|
|
264
|
+
exit_code=result["exit_code"],
|
|
265
|
+
stdout=result["stdout"],
|
|
266
|
+
stderr=result["stderr"],
|
|
267
|
+
duration=time.time() - start_time,
|
|
268
|
+
)
|
|
269
|
+
except Exception as e:
|
|
270
|
+
return CommandResult(
|
|
271
|
+
exit_code=-1,
|
|
272
|
+
stdout="",
|
|
273
|
+
stderr=str(e),
|
|
274
|
+
duration=time.time() - start_time,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def exec(
|
|
278
|
+
self,
|
|
279
|
+
*args: str,
|
|
280
|
+
cwd: Optional[str] = None,
|
|
281
|
+
env: Optional[dict[str, str]] = None,
|
|
282
|
+
timeout: Optional[float] = None,
|
|
283
|
+
) -> ProcessHandle:
|
|
284
|
+
"""Execute a command with streaming output.
|
|
285
|
+
|
|
286
|
+
Unlike `run()`, this returns immediately with a ProcessHandle
|
|
287
|
+
that can be used to stream output and wait for completion.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
*args: Command and arguments.
|
|
291
|
+
cwd: Working directory.
|
|
292
|
+
env: Environment variables.
|
|
293
|
+
timeout: Timeout in seconds.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
ProcessHandle for the running process.
|
|
297
|
+
"""
|
|
298
|
+
command = " ".join(args)
|
|
299
|
+
process = ProcessHandle(sandbox=self._sandbox, command=command)
|
|
300
|
+
|
|
301
|
+
# Start the subprocess
|
|
302
|
+
env_str = f", env={{**os.environ, **{env!r}}}" if env else ""
|
|
303
|
+
cwd_str = f", cwd={cwd!r}" if cwd else ""
|
|
304
|
+
|
|
305
|
+
code = f"""
|
|
306
|
+
import subprocess
|
|
307
|
+
import os
|
|
308
|
+
|
|
309
|
+
{process._process_var} = subprocess.Popen(
|
|
310
|
+
{list(args)!r},
|
|
311
|
+
stdout=subprocess.PIPE,
|
|
312
|
+
stderr=subprocess.PIPE,
|
|
313
|
+
text=True{cwd_str}{env_str}
|
|
314
|
+
)
|
|
315
|
+
__proc_pid__ = {process._process_var}.pid
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
self._sandbox.run_code(code)
|
|
319
|
+
try:
|
|
320
|
+
process.pid = self._sandbox.get_variable("__proc_pid__")
|
|
321
|
+
except Exception:
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
return process
|
|
325
|
+
|
|
326
|
+
def spawn(
|
|
327
|
+
self,
|
|
328
|
+
command: str,
|
|
329
|
+
cwd: Optional[str] = None,
|
|
330
|
+
env: Optional[dict[str, str]] = None,
|
|
331
|
+
) -> ProcessHandle:
|
|
332
|
+
"""Spawn a background process.
|
|
333
|
+
|
|
334
|
+
The process runs in the background and doesn't block.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
command: The command to run.
|
|
338
|
+
cwd: Working directory.
|
|
339
|
+
env: Environment variables.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
ProcessHandle for the background process.
|
|
343
|
+
"""
|
|
344
|
+
return self.exec(*command.split(), cwd=cwd, env=env)
|
|
345
|
+
|
|
346
|
+
def run_script(
|
|
347
|
+
self,
|
|
348
|
+
script: str,
|
|
349
|
+
interpreter: str = "/bin/bash",
|
|
350
|
+
cwd: Optional[str] = None,
|
|
351
|
+
env: Optional[dict[str, str]] = None,
|
|
352
|
+
timeout: Optional[float] = None,
|
|
353
|
+
) -> CommandResult:
|
|
354
|
+
"""Run a script with the specified interpreter.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
script: The script content.
|
|
358
|
+
interpreter: Script interpreter path.
|
|
359
|
+
cwd: Working directory.
|
|
360
|
+
env: Environment variables.
|
|
361
|
+
timeout: Timeout in seconds.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
CommandResult with exit code, stdout, stderr.
|
|
365
|
+
"""
|
|
366
|
+
start_time = time.time()
|
|
367
|
+
|
|
368
|
+
env_str = f", env={{**os.environ, **{env!r}}}" if env else ""
|
|
369
|
+
cwd_str = f", cwd={cwd!r}" if cwd else ""
|
|
370
|
+
timeout_str = f", timeout={timeout}" if timeout else ""
|
|
371
|
+
|
|
372
|
+
code = f"""
|
|
373
|
+
import subprocess
|
|
374
|
+
import os
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
__script_result__ = subprocess.run(
|
|
378
|
+
[{interpreter!r}, '-c', {script!r}],
|
|
379
|
+
capture_output=True,
|
|
380
|
+
text=True{cwd_str}{env_str}{timeout_str}
|
|
381
|
+
)
|
|
382
|
+
__script_output__ = {{
|
|
383
|
+
'exit_code': __script_result__.returncode,
|
|
384
|
+
'stdout': __script_result__.stdout,
|
|
385
|
+
'stderr': __script_result__.stderr,
|
|
386
|
+
}}
|
|
387
|
+
except subprocess.TimeoutExpired:
|
|
388
|
+
__script_output__ = {{
|
|
389
|
+
'exit_code': -1,
|
|
390
|
+
'stdout': '',
|
|
391
|
+
'stderr': 'Script execution timed out',
|
|
392
|
+
}}
|
|
393
|
+
except Exception as e:
|
|
394
|
+
__script_output__ = {{
|
|
395
|
+
'exit_code': -1,
|
|
396
|
+
'stdout': '',
|
|
397
|
+
'stderr': str(e),
|
|
398
|
+
}}
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
execution = self._sandbox.run_code(code, timeout=timeout)
|
|
402
|
+
|
|
403
|
+
if execution.error:
|
|
404
|
+
return CommandResult(
|
|
405
|
+
exit_code=-1,
|
|
406
|
+
stdout="",
|
|
407
|
+
stderr=str(execution.error),
|
|
408
|
+
duration=time.time() - start_time,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
result = self._sandbox.get_variable("__script_output__")
|
|
413
|
+
return CommandResult(
|
|
414
|
+
exit_code=result["exit_code"],
|
|
415
|
+
stdout=result["stdout"],
|
|
416
|
+
stderr=result["stderr"],
|
|
417
|
+
duration=time.time() - start_time,
|
|
418
|
+
)
|
|
419
|
+
except Exception as e:
|
|
420
|
+
return CommandResult(
|
|
421
|
+
exit_code=-1,
|
|
422
|
+
stdout="",
|
|
423
|
+
stderr=str(e),
|
|
424
|
+
duration=time.time() - start_time,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
def install_system_packages(
|
|
428
|
+
self,
|
|
429
|
+
packages: list[str],
|
|
430
|
+
package_manager: str = "apt-get",
|
|
431
|
+
timeout: float = 300,
|
|
432
|
+
) -> CommandResult:
|
|
433
|
+
"""Install system packages.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
packages: List of package names.
|
|
437
|
+
package_manager: Package manager to use (apt-get, yum, etc.).
|
|
438
|
+
timeout: Timeout in seconds.
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
CommandResult from the installation.
|
|
442
|
+
"""
|
|
443
|
+
if package_manager == "apt-get":
|
|
444
|
+
cmd = f"apt-get update && apt-get install -y {' '.join(packages)}"
|
|
445
|
+
elif package_manager == "yum":
|
|
446
|
+
cmd = f"yum install -y {' '.join(packages)}"
|
|
447
|
+
elif package_manager == "apk":
|
|
448
|
+
cmd = f"apk add {' '.join(packages)}"
|
|
449
|
+
else:
|
|
450
|
+
cmd = f"{package_manager} install {' '.join(packages)}"
|
|
451
|
+
|
|
452
|
+
return self.run(cmd, timeout=timeout)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
2
|
+
#
|
|
3
|
+
# BSD 3-Clause License
|
|
4
|
+
|
|
5
|
+
"""Custom exceptions for code sandboxes."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SandboxError(Exception):
|
|
9
|
+
"""Base exception for sandbox errors."""
|
|
10
|
+
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SandboxTimeoutError(SandboxError):
|
|
15
|
+
"""Raised when code execution times out."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, timeout: float, message: str = None):
|
|
18
|
+
self.timeout = timeout
|
|
19
|
+
super().__init__(message or f"Code execution timed out after {timeout} seconds")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SandboxExecutionError(SandboxError):
|
|
23
|
+
"""Raised when code execution fails."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, error_name: str, error_value: str, traceback: str = ""):
|
|
26
|
+
self.error_name = error_name
|
|
27
|
+
self.error_value = error_value
|
|
28
|
+
self.traceback = traceback
|
|
29
|
+
super().__init__(f"{error_name}: {error_value}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SandboxNotStartedError(SandboxError):
|
|
33
|
+
"""Raised when trying to use a sandbox that hasn't been started."""
|
|
34
|
+
|
|
35
|
+
def __init__(self):
|
|
36
|
+
super().__init__("Sandbox has not been started. Use 'with' context or call start()")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SandboxConnectionError(SandboxError):
|
|
40
|
+
"""Raised when connection to remote sandbox fails."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, url: str, message: str = None):
|
|
43
|
+
self.url = url
|
|
44
|
+
super().__init__(message or f"Failed to connect to sandbox at {url}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SandboxConfigurationError(SandboxError):
|
|
48
|
+
"""Raised when sandbox configuration is invalid."""
|
|
49
|
+
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ContextNotFoundError(SandboxError):
|
|
54
|
+
"""Raised when a requested context does not exist."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, context_id: str):
|
|
57
|
+
self.context_id = context_id
|
|
58
|
+
super().__init__(f"Context '{context_id}' not found")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class VariableNotFoundError(SandboxError):
|
|
62
|
+
"""Raised when a requested variable does not exist."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, variable_name: str):
|
|
65
|
+
self.variable_name = variable_name
|
|
66
|
+
super().__init__(f"Variable '{variable_name}' not found in sandbox")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class SandboxSnapshotError(SandboxError):
|
|
70
|
+
"""Raised when snapshot operations fail."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, operation: str, message: str = None):
|
|
73
|
+
self.operation = operation
|
|
74
|
+
super().__init__(message or f"Snapshot operation '{operation}' failed")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SandboxResourceError(SandboxError):
|
|
78
|
+
"""Raised when resource allocation fails (CPU, GPU, memory)."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, resource_type: str, message: str = None):
|
|
81
|
+
self.resource_type = resource_type
|
|
82
|
+
super().__init__(message or f"Failed to allocate resource: {resource_type}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class SandboxAuthenticationError(SandboxError):
|
|
86
|
+
"""Raised when authentication with the sandbox provider fails."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, message: str = None):
|
|
89
|
+
super().__init__(message or "Authentication failed")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SandboxQuotaExceededError(SandboxError):
|
|
93
|
+
"""Raised when sandbox quota or limits are exceeded."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, limit_type: str, limit_value: str = None):
|
|
96
|
+
self.limit_type = limit_type
|
|
97
|
+
self.limit_value = limit_value
|
|
98
|
+
msg = f"Quota exceeded for {limit_type}"
|
|
99
|
+
if limit_value:
|
|
100
|
+
msg += f": {limit_value}"
|
|
101
|
+
super().__init__(msg)
|