scalebox-sdk 0.1.4__py3-none-any.whl → 0.1.25__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.
- scalebox/__init__.py +1 -1
- scalebox/api/__init__.py +128 -128
- scalebox/api/client/__init__.py +8 -8
- scalebox/api/client/api/sandboxes/get_sandboxes.py +5 -3
- scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +2 -2
- scalebox/api/client/api/sandboxes/post_sandboxes.py +2 -2
- scalebox/api/client/client.py +288 -288
- scalebox/api/client/models/listed_sandbox.py +11 -9
- scalebox/api/client/models/new_sandbox.py +1 -1
- scalebox/api/client/models/sandbox.py +125 -125
- scalebox/api/client/models/sandbox_state.py +1 -0
- scalebox/api/client/types.py +46 -46
- scalebox/code_interpreter/code_interpreter_async.py +370 -369
- scalebox/code_interpreter/code_interpreter_sync.py +318 -317
- scalebox/connection_config.py +92 -92
- scalebox/csx_desktop/main.py +12 -12
- scalebox/generated/api_pb2_connect.py +17 -66
- scalebox/sandbox_async/commands/command.py +307 -307
- scalebox/sandbox_async/commands/command_handle.py +187 -187
- scalebox/sandbox_async/commands/pty.py +187 -187
- scalebox/sandbox_async/filesystem/filesystem.py +557 -557
- scalebox/sandbox_async/filesystem/watch_handle.py +61 -61
- scalebox/sandbox_async/main.py +647 -646
- scalebox/sandbox_async/sandbox_api.py +365 -365
- scalebox/sandbox_async/utils.py +7 -7
- scalebox/sandbox_sync/__init__.py +2 -2
- scalebox/sandbox_sync/commands/command.py +300 -300
- scalebox/sandbox_sync/commands/command_handle.py +150 -150
- scalebox/sandbox_sync/commands/pty.py +181 -181
- scalebox/sandbox_sync/filesystem/filesystem.py +543 -543
- scalebox/sandbox_sync/filesystem/watch_handle.py +66 -66
- scalebox/sandbox_sync/main.py +789 -790
- scalebox/sandbox_sync/sandbox_api.py +356 -356
- scalebox/test/CODE_INTERPRETER_TESTS_READY.md +256 -256
- scalebox/test/README.md +164 -164
- scalebox/test/aclient.py +72 -72
- scalebox/test/code_interpreter_centext.py +21 -21
- scalebox/test/code_interpreter_centext_sync.py +21 -21
- scalebox/test/code_interpreter_test.py +1 -1
- scalebox/test/code_interpreter_test_sync.py +1 -1
- scalebox/test/run_all_validation_tests.py +334 -334
- scalebox/test/test_basic.py +78 -78
- scalebox/test/test_code_interpreter_async_comprehensive.py +2653 -2653
- scalebox/test/{test_code_interpreter_e2bsync_comprehensive.py → test_code_interpreter_execcode.py} +328 -392
- scalebox/test/test_code_interpreter_sync_comprehensive.py +3416 -3412
- scalebox/test/test_csx_desktop_examples.py +130 -0
- scalebox/test/test_sandbox_async_comprehensive.py +736 -738
- scalebox/test/test_sandbox_stress_and_edge_cases.py +778 -778
- scalebox/test/test_sandbox_sync_comprehensive.py +779 -770
- scalebox/test/test_sandbox_usage_examples.py +987 -987
- scalebox/test/testacreate.py +24 -24
- scalebox/test/testagetinfo.py +18 -18
- scalebox/test/testcodeinterpreter_async.py +508 -508
- scalebox/test/testcodeinterpreter_sync.py +239 -239
- scalebox/test/testcomputeuse.py +2 -2
- scalebox/test/testnovnc.py +12 -12
- scalebox/test/testsandbox_api.py +15 -0
- scalebox/test/testsandbox_async.py +202 -118
- scalebox/test/testsandbox_sync.py +71 -38
- scalebox/version.py +2 -2
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/METADATA +104 -103
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/RECORD +66 -66
- scalebox/test/test_code_interpreter_e2basync_comprehensive.py +0 -2655
- scalebox/test/test_e2b_first.py +0 -11
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/WHEEL +0 -0
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/entry_points.txt +0 -0
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/licenses/LICENSE +0 -0
- {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/top_level.txt +0 -0
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import inspect
|
|
3
|
-
from typing import Any, AsyncIterator, Callable, Coroutine, Optional, Tuple, Union
|
|
4
|
-
|
|
5
|
-
from ...generated import api_pb2
|
|
6
|
-
from ...generated.rpc import handle_rpc_exception
|
|
7
|
-
from ...sandbox.commands.command_handle import (
|
|
8
|
-
CommandExitException,
|
|
9
|
-
CommandResult,
|
|
10
|
-
PtyOutput,
|
|
11
|
-
Stderr,
|
|
12
|
-
Stdout,
|
|
13
|
-
)
|
|
14
|
-
from ...sandbox_async.utils import OutputHandler
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class AsyncCommandHandle:
|
|
18
|
-
"""
|
|
19
|
-
Command execution handle.
|
|
20
|
-
|
|
21
|
-
It provides methods for waiting for the command to finish, retrieving stdout/stderr, and killing the command.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def pid(self):
|
|
26
|
-
"""
|
|
27
|
-
Command process ID.
|
|
28
|
-
"""
|
|
29
|
-
return self._pid
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def stdout(self):
|
|
33
|
-
"""
|
|
34
|
-
Command stdout output.
|
|
35
|
-
"""
|
|
36
|
-
return self._stdout
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def stderr(self):
|
|
40
|
-
"""
|
|
41
|
-
Command stderr output.
|
|
42
|
-
"""
|
|
43
|
-
return self._stderr
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def error(self):
|
|
47
|
-
"""
|
|
48
|
-
Command execution error message.
|
|
49
|
-
"""
|
|
50
|
-
if self._result is None:
|
|
51
|
-
return None
|
|
52
|
-
return self._result.error
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def exit_code(self):
|
|
56
|
-
"""
|
|
57
|
-
Command execution exit code.
|
|
58
|
-
|
|
59
|
-
`0` if the command finished successfully.
|
|
60
|
-
|
|
61
|
-
It is `None` if the command is still running.
|
|
62
|
-
"""
|
|
63
|
-
if self._result is None:
|
|
64
|
-
return None
|
|
65
|
-
return self._result.exit_code
|
|
66
|
-
|
|
67
|
-
def __init__(
|
|
68
|
-
self,
|
|
69
|
-
pid: int,
|
|
70
|
-
handle_kill: Callable[[], Coroutine[Any, Any, bool]],
|
|
71
|
-
events: AsyncIterator[
|
|
72
|
-
Union[api_pb2.StartResponse, api_pb2.ConnectResponse]
|
|
73
|
-
],
|
|
74
|
-
on_stdout: Optional[OutputHandler[Stdout]] = None,
|
|
75
|
-
on_stderr: Optional[OutputHandler[Stderr]] = None,
|
|
76
|
-
on_pty: Optional[OutputHandler[PtyOutput]] = None,
|
|
77
|
-
):
|
|
78
|
-
self._pid = pid
|
|
79
|
-
self._handle_kill = handle_kill
|
|
80
|
-
self._events = events
|
|
81
|
-
|
|
82
|
-
self._stdout: str = ""
|
|
83
|
-
self._stderr: str = ""
|
|
84
|
-
|
|
85
|
-
self._on_stdout = on_stdout
|
|
86
|
-
self._on_stderr = on_stderr
|
|
87
|
-
self._on_pty = on_pty
|
|
88
|
-
|
|
89
|
-
self._result: Optional[CommandResult] = None
|
|
90
|
-
self._iteration_exception: Optional[Exception] = None
|
|
91
|
-
|
|
92
|
-
self._wait = asyncio.create_task(self._handle_events())
|
|
93
|
-
|
|
94
|
-
async def _iterate_events(
|
|
95
|
-
self,
|
|
96
|
-
) -> AsyncIterator[
|
|
97
|
-
Union[
|
|
98
|
-
Tuple[Stdout, None, None],
|
|
99
|
-
Tuple[None, Stderr, None],
|
|
100
|
-
Tuple[None, None, PtyOutput],
|
|
101
|
-
],
|
|
102
|
-
]:
|
|
103
|
-
async for event in self._events:
|
|
104
|
-
if event.event.HasField("data"):
|
|
105
|
-
if event.event.data.stdout:
|
|
106
|
-
out = event.event.data.stdout.decode("utf-8", "replace")
|
|
107
|
-
self._stdout += out
|
|
108
|
-
yield out, None, None
|
|
109
|
-
if event.event.data.stderr:
|
|
110
|
-
out = event.event.data.stderr.decode("utf-8", "replace")
|
|
111
|
-
self._stderr += out
|
|
112
|
-
yield None, out, None
|
|
113
|
-
if event.event.data.pty:
|
|
114
|
-
yield None, None, event.event.data.pty
|
|
115
|
-
if event.event.HasField("end"):
|
|
116
|
-
self._result = CommandResult(
|
|
117
|
-
stdout=self._stdout,
|
|
118
|
-
stderr=self._stderr,
|
|
119
|
-
exit_code=event.event.end.exit_code,
|
|
120
|
-
error=event.event.end.error,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
async def disconnect(self) -> None:
|
|
124
|
-
"""
|
|
125
|
-
Disconnects from the command.
|
|
126
|
-
|
|
127
|
-
The command is not killed, but SDK stops receiving events from the command.
|
|
128
|
-
You can reconnect to the command using `sandbox.commands.connect` method.
|
|
129
|
-
"""
|
|
130
|
-
self._wait.cancel()
|
|
131
|
-
# BUG: In Python 3.8 closing async generator can throw RuntimeError.
|
|
132
|
-
# await self._events.aclose()
|
|
133
|
-
|
|
134
|
-
async def _handle_events(self):
|
|
135
|
-
try:
|
|
136
|
-
async for stdout, stderr, pty in self._iterate_events():
|
|
137
|
-
if stdout is not None and self._on_stdout:
|
|
138
|
-
cb = self._on_stdout(stdout)
|
|
139
|
-
if inspect.isawaitable(cb):
|
|
140
|
-
await cb
|
|
141
|
-
elif stderr is not None and self._on_stderr:
|
|
142
|
-
cb = self._on_stderr(stderr)
|
|
143
|
-
if inspect.isawaitable(cb):
|
|
144
|
-
await cb
|
|
145
|
-
elif pty is not None and self._on_pty:
|
|
146
|
-
cb = self._on_pty(pty)
|
|
147
|
-
if inspect.isawaitable(cb):
|
|
148
|
-
await cb
|
|
149
|
-
except StopAsyncIteration:
|
|
150
|
-
pass
|
|
151
|
-
except Exception as e:
|
|
152
|
-
self._iteration_exception = handle_rpc_exception(e)
|
|
153
|
-
|
|
154
|
-
async def wait(self) -> CommandResult:
|
|
155
|
-
"""
|
|
156
|
-
Wait for the command to finish and return the result.
|
|
157
|
-
If the command exits with a non-zero exit code, it throws a `CommandExitException`.
|
|
158
|
-
|
|
159
|
-
:return: `CommandResult` result of command execution
|
|
160
|
-
"""
|
|
161
|
-
await self._wait
|
|
162
|
-
if self._iteration_exception:
|
|
163
|
-
raise self._iteration_exception
|
|
164
|
-
|
|
165
|
-
if self._result is None:
|
|
166
|
-
raise Exception("Command ended without an end event")
|
|
167
|
-
|
|
168
|
-
if self._result.exit_code != 0:
|
|
169
|
-
raise CommandExitException(
|
|
170
|
-
stdout=self._stdout,
|
|
171
|
-
stderr=self._stderr,
|
|
172
|
-
exit_code=self._result.exit_code,
|
|
173
|
-
error=self._result.error,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
return self._result
|
|
177
|
-
|
|
178
|
-
async def kill(self) -> bool:
|
|
179
|
-
"""
|
|
180
|
-
Kills the command.
|
|
181
|
-
|
|
182
|
-
It uses `SIGKILL` signal to kill the command
|
|
183
|
-
|
|
184
|
-
:return: `True` if the command was killed successfully, `False` if the command was not found
|
|
185
|
-
"""
|
|
186
|
-
result = await self._handle_kill()
|
|
187
|
-
return result
|
|
1
|
+
import asyncio
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import Any, AsyncIterator, Callable, Coroutine, Optional, Tuple, Union
|
|
4
|
+
|
|
5
|
+
from ...generated import api_pb2
|
|
6
|
+
from ...generated.rpc import handle_rpc_exception
|
|
7
|
+
from ...sandbox.commands.command_handle import (
|
|
8
|
+
CommandExitException,
|
|
9
|
+
CommandResult,
|
|
10
|
+
PtyOutput,
|
|
11
|
+
Stderr,
|
|
12
|
+
Stdout,
|
|
13
|
+
)
|
|
14
|
+
from ...sandbox_async.utils import OutputHandler
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AsyncCommandHandle:
|
|
18
|
+
"""
|
|
19
|
+
Command execution handle.
|
|
20
|
+
|
|
21
|
+
It provides methods for waiting for the command to finish, retrieving stdout/stderr, and killing the command.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def pid(self):
|
|
26
|
+
"""
|
|
27
|
+
Command process ID.
|
|
28
|
+
"""
|
|
29
|
+
return self._pid
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def stdout(self):
|
|
33
|
+
"""
|
|
34
|
+
Command stdout output.
|
|
35
|
+
"""
|
|
36
|
+
return self._stdout
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def stderr(self):
|
|
40
|
+
"""
|
|
41
|
+
Command stderr output.
|
|
42
|
+
"""
|
|
43
|
+
return self._stderr
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def error(self):
|
|
47
|
+
"""
|
|
48
|
+
Command execution error message.
|
|
49
|
+
"""
|
|
50
|
+
if self._result is None:
|
|
51
|
+
return None
|
|
52
|
+
return self._result.error
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def exit_code(self):
|
|
56
|
+
"""
|
|
57
|
+
Command execution exit code.
|
|
58
|
+
|
|
59
|
+
`0` if the command finished successfully.
|
|
60
|
+
|
|
61
|
+
It is `None` if the command is still running.
|
|
62
|
+
"""
|
|
63
|
+
if self._result is None:
|
|
64
|
+
return None
|
|
65
|
+
return self._result.exit_code
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
pid: int,
|
|
70
|
+
handle_kill: Callable[[], Coroutine[Any, Any, bool]],
|
|
71
|
+
events: AsyncIterator[
|
|
72
|
+
Union[api_pb2.StartResponse, api_pb2.ConnectResponse]
|
|
73
|
+
],
|
|
74
|
+
on_stdout: Optional[OutputHandler[Stdout]] = None,
|
|
75
|
+
on_stderr: Optional[OutputHandler[Stderr]] = None,
|
|
76
|
+
on_pty: Optional[OutputHandler[PtyOutput]] = None,
|
|
77
|
+
):
|
|
78
|
+
self._pid = pid
|
|
79
|
+
self._handle_kill = handle_kill
|
|
80
|
+
self._events = events
|
|
81
|
+
|
|
82
|
+
self._stdout: str = ""
|
|
83
|
+
self._stderr: str = ""
|
|
84
|
+
|
|
85
|
+
self._on_stdout = on_stdout
|
|
86
|
+
self._on_stderr = on_stderr
|
|
87
|
+
self._on_pty = on_pty
|
|
88
|
+
|
|
89
|
+
self._result: Optional[CommandResult] = None
|
|
90
|
+
self._iteration_exception: Optional[Exception] = None
|
|
91
|
+
|
|
92
|
+
self._wait = asyncio.create_task(self._handle_events())
|
|
93
|
+
|
|
94
|
+
async def _iterate_events(
|
|
95
|
+
self,
|
|
96
|
+
) -> AsyncIterator[
|
|
97
|
+
Union[
|
|
98
|
+
Tuple[Stdout, None, None],
|
|
99
|
+
Tuple[None, Stderr, None],
|
|
100
|
+
Tuple[None, None, PtyOutput],
|
|
101
|
+
],
|
|
102
|
+
]:
|
|
103
|
+
async for event in self._events:
|
|
104
|
+
if event.event.HasField("data"):
|
|
105
|
+
if event.event.data.stdout:
|
|
106
|
+
out = event.event.data.stdout.decode("utf-8", "replace")
|
|
107
|
+
self._stdout += out
|
|
108
|
+
yield out, None, None
|
|
109
|
+
if event.event.data.stderr:
|
|
110
|
+
out = event.event.data.stderr.decode("utf-8", "replace")
|
|
111
|
+
self._stderr += out
|
|
112
|
+
yield None, out, None
|
|
113
|
+
if event.event.data.pty:
|
|
114
|
+
yield None, None, event.event.data.pty
|
|
115
|
+
if event.event.HasField("end"):
|
|
116
|
+
self._result = CommandResult(
|
|
117
|
+
stdout=self._stdout,
|
|
118
|
+
stderr=self._stderr,
|
|
119
|
+
exit_code=event.event.end.exit_code,
|
|
120
|
+
error=event.event.end.error,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def disconnect(self) -> None:
|
|
124
|
+
"""
|
|
125
|
+
Disconnects from the command.
|
|
126
|
+
|
|
127
|
+
The command is not killed, but SDK stops receiving events from the command.
|
|
128
|
+
You can reconnect to the command using `sandbox.commands.connect` method.
|
|
129
|
+
"""
|
|
130
|
+
self._wait.cancel()
|
|
131
|
+
# BUG: In Python 3.8 closing async generator can throw RuntimeError.
|
|
132
|
+
# await self._events.aclose()
|
|
133
|
+
|
|
134
|
+
async def _handle_events(self):
|
|
135
|
+
try:
|
|
136
|
+
async for stdout, stderr, pty in self._iterate_events():
|
|
137
|
+
if stdout is not None and self._on_stdout:
|
|
138
|
+
cb = self._on_stdout(stdout)
|
|
139
|
+
if inspect.isawaitable(cb):
|
|
140
|
+
await cb
|
|
141
|
+
elif stderr is not None and self._on_stderr:
|
|
142
|
+
cb = self._on_stderr(stderr)
|
|
143
|
+
if inspect.isawaitable(cb):
|
|
144
|
+
await cb
|
|
145
|
+
elif pty is not None and self._on_pty:
|
|
146
|
+
cb = self._on_pty(pty)
|
|
147
|
+
if inspect.isawaitable(cb):
|
|
148
|
+
await cb
|
|
149
|
+
except StopAsyncIteration:
|
|
150
|
+
pass
|
|
151
|
+
except Exception as e:
|
|
152
|
+
self._iteration_exception = handle_rpc_exception(e)
|
|
153
|
+
|
|
154
|
+
async def wait(self) -> CommandResult:
|
|
155
|
+
"""
|
|
156
|
+
Wait for the command to finish and return the result.
|
|
157
|
+
If the command exits with a non-zero exit code, it throws a `CommandExitException`.
|
|
158
|
+
|
|
159
|
+
:return: `CommandResult` result of command execution
|
|
160
|
+
"""
|
|
161
|
+
await self._wait
|
|
162
|
+
if self._iteration_exception:
|
|
163
|
+
raise self._iteration_exception
|
|
164
|
+
|
|
165
|
+
if self._result is None:
|
|
166
|
+
raise Exception("Command ended without an end event")
|
|
167
|
+
|
|
168
|
+
if self._result.exit_code != 0:
|
|
169
|
+
raise CommandExitException(
|
|
170
|
+
stdout=self._stdout,
|
|
171
|
+
stderr=self._stderr,
|
|
172
|
+
exit_code=self._result.exit_code,
|
|
173
|
+
error=self._result.error,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return self._result
|
|
177
|
+
|
|
178
|
+
async def kill(self) -> bool:
|
|
179
|
+
"""
|
|
180
|
+
Kills the command.
|
|
181
|
+
|
|
182
|
+
It uses `SIGKILL` signal to kill the command
|
|
183
|
+
|
|
184
|
+
:return: `True` if the command was killed successfully, `False` if the command was not found
|
|
185
|
+
"""
|
|
186
|
+
result = await self._handle_kill()
|
|
187
|
+
return result
|