scalebox-sdk 0.1.25__py3-none-any.whl → 1.0.1__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 +2 -2
- scalebox/api/__init__.py +3 -1
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- scalebox/api/client/models/connect_sandbox.py +59 -0
- scalebox/api/client/models/error.py +2 -2
- scalebox/api/client/models/listed_sandbox.py +19 -1
- scalebox/api/client/models/new_sandbox.py +10 -0
- scalebox/api/client/models/sandbox.py +13 -0
- scalebox/api/client/models/sandbox_detail.py +24 -0
- scalebox/cli.py +125 -125
- scalebox/client/aclient.py +57 -57
- scalebox/client/client.py +102 -102
- scalebox/code_interpreter/__init__.py +12 -12
- scalebox/code_interpreter/charts.py +230 -230
- scalebox/code_interpreter/constants.py +3 -3
- scalebox/code_interpreter/exceptions.py +13 -13
- scalebox/code_interpreter/models.py +485 -485
- scalebox/connection_config.py +34 -1
- scalebox/csx_connect/__init__.py +1 -1
- scalebox/csx_connect/client.py +485 -485
- scalebox/csx_desktop/main.py +651 -651
- scalebox/exceptions.py +83 -83
- scalebox/generated/api.py +61 -61
- scalebox/generated/api_pb2.py +203 -203
- scalebox/generated/api_pb2.pyi +956 -956
- scalebox/generated/api_pb2_connect.py +1407 -1407
- scalebox/generated/rpc.py +50 -50
- scalebox/sandbox/main.py +146 -139
- scalebox/sandbox/sandbox_api.py +105 -91
- scalebox/sandbox/signature.py +40 -40
- scalebox/sandbox/utils.py +34 -34
- scalebox/sandbox_async/main.py +226 -44
- scalebox/sandbox_async/sandbox_api.py +124 -3
- scalebox/sandbox_sync/main.py +205 -130
- scalebox/sandbox_sync/sandbox_api.py +119 -3
- scalebox/test/CODE_INTERPRETER_TESTS_READY.md +323 -323
- scalebox/test/README.md +329 -329
- scalebox/test/bedrock_openai_adapter.py +67 -0
- scalebox/test/code_interpreter_test.py +34 -34
- scalebox/test/code_interpreter_test_sync.py +34 -34
- scalebox/test/run_stress_code_interpreter_sync.py +166 -0
- scalebox/test/simple_upload_example.py +123 -0
- scalebox/test/stabitiy_test.py +310 -0
- scalebox/test/test_browser_use.py +25 -0
- scalebox/test/test_browser_use_scalebox.py +61 -0
- scalebox/test/test_code_interpreter_sync_comprehensive.py +115 -65
- scalebox/test/test_connect_pause_async.py +277 -0
- scalebox/test/test_connect_pause_sync.py +267 -0
- scalebox/test/test_desktop_sandbox_sf.py +117 -0
- scalebox/test/test_download_url.py +49 -0
- scalebox/test/test_sandbox_async_comprehensive.py +1 -1
- scalebox/test/test_sandbox_object_storage_example.py +146 -0
- scalebox/test/test_sandbox_object_storage_example_async.py +156 -0
- scalebox/test/test_sf.py +137 -0
- scalebox/test/test_watch_dir_async.py +56 -0
- scalebox/test/testacreate.py +1 -1
- scalebox/test/testagetinfo.py +1 -1
- scalebox/test/testcomputeuse.py +243 -243
- scalebox/test/testsandbox_api.py +1 -3
- scalebox/test/testsandbox_sync.py +1 -1
- scalebox/test/upload_100mb_example.py +355 -0
- scalebox/utils/httpcoreclient.py +297 -297
- scalebox/utils/httpxclient.py +403 -403
- scalebox/version.py +2 -2
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/METADATA +1 -1
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/RECORD +70 -53
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/WHEEL +1 -1
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/entry_points.txt +0 -0
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.1.dist-info}/top_level.txt +0 -0
scalebox/sandbox/signature.py
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import hashlib
|
|
3
|
-
import time
|
|
4
|
-
from typing import Literal, Optional, TypedDict
|
|
5
|
-
|
|
6
|
-
Operation = Literal["read", "write"]
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Signature(TypedDict):
|
|
10
|
-
signature: str
|
|
11
|
-
expiration: Optional[int] # Unix timestamp or None
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def get_signature(
|
|
15
|
-
path: str,
|
|
16
|
-
operation: Operation,
|
|
17
|
-
user: str,
|
|
18
|
-
envd_access_token: Optional[str],
|
|
19
|
-
expiration_in_seconds: Optional[int] = None,
|
|
20
|
-
) -> Signature:
|
|
21
|
-
"""
|
|
22
|
-
Generate a v1 signature for sandbox file URLs.
|
|
23
|
-
"""
|
|
24
|
-
if not envd_access_token:
|
|
25
|
-
raise ValueError("Access token is not set and signature cannot be generated!")
|
|
26
|
-
|
|
27
|
-
expiration = (
|
|
28
|
-
int(time.time()) + expiration_in_seconds if expiration_in_seconds else None
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
raw = (
|
|
32
|
-
f"{path}:{operation}:{user}:{envd_access_token}"
|
|
33
|
-
if expiration is None
|
|
34
|
-
else f"{path}:{operation}:{user}:{envd_access_token}:{expiration}"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
digest = hashlib.sha256(raw.encode("utf-8")).digest()
|
|
38
|
-
encoded = base64.b64encode(digest).rstrip(b"=").decode("ascii")
|
|
39
|
-
|
|
40
|
-
return {"signature": f"v1_{encoded}", "expiration": expiration}
|
|
1
|
+
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import time
|
|
4
|
+
from typing import Literal, Optional, TypedDict
|
|
5
|
+
|
|
6
|
+
Operation = Literal["read", "write"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Signature(TypedDict):
|
|
10
|
+
signature: str
|
|
11
|
+
expiration: Optional[int] # Unix timestamp or None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_signature(
|
|
15
|
+
path: str,
|
|
16
|
+
operation: Operation,
|
|
17
|
+
user: str,
|
|
18
|
+
envd_access_token: Optional[str],
|
|
19
|
+
expiration_in_seconds: Optional[int] = None,
|
|
20
|
+
) -> Signature:
|
|
21
|
+
"""
|
|
22
|
+
Generate a v1 signature for sandbox file URLs.
|
|
23
|
+
"""
|
|
24
|
+
if not envd_access_token:
|
|
25
|
+
raise ValueError("Access token is not set and signature cannot be generated!")
|
|
26
|
+
|
|
27
|
+
expiration = (
|
|
28
|
+
int(time.time()) + expiration_in_seconds if expiration_in_seconds else None
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
raw = (
|
|
32
|
+
f"{path}:{operation}:{user}:{envd_access_token}"
|
|
33
|
+
if expiration is None
|
|
34
|
+
else f"{path}:{operation}:{user}:{envd_access_token}:{expiration}"
|
|
35
|
+
)
|
|
36
|
+
print(raw)
|
|
37
|
+
digest = hashlib.sha256(raw.encode("utf-8")).digest()
|
|
38
|
+
encoded = base64.b64encode(digest).rstrip(b"=").decode("ascii")
|
|
39
|
+
|
|
40
|
+
return {"signature": f"v1_{encoded}", "expiration": expiration}
|
scalebox/sandbox/utils.py
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import functools
|
|
2
|
-
from typing import Any, Optional, Type, TypeVar, cast
|
|
3
|
-
|
|
4
|
-
T = TypeVar("T")
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class class_method_variant(object):
|
|
8
|
-
def __init__(self, class_method_name):
|
|
9
|
-
self.class_method_name = class_method_name
|
|
10
|
-
|
|
11
|
-
method: Any
|
|
12
|
-
|
|
13
|
-
def __call__(self, method: T) -> T:
|
|
14
|
-
self.method = method
|
|
15
|
-
return cast(T, self)
|
|
16
|
-
|
|
17
|
-
def __get__(self, obj, objtype: Optional[Type[Any]] = None):
|
|
18
|
-
@functools.wraps(self.method)
|
|
19
|
-
def _wrapper(*args, **kwargs):
|
|
20
|
-
if obj is not None:
|
|
21
|
-
# Method was called as an instance method, e.g.
|
|
22
|
-
# instance.method(...)
|
|
23
|
-
return self.method(obj, *args, **kwargs)
|
|
24
|
-
elif len(args) > 0 and objtype is not None and isinstance(args[0], objtype):
|
|
25
|
-
# Method was called as a class method with the instance as the
|
|
26
|
-
# first argument, e.g. Class.method(instance, ...) which in
|
|
27
|
-
# Python is the same thing as calling an instance method
|
|
28
|
-
return self.method(args[0], *args[1:], **kwargs)
|
|
29
|
-
else:
|
|
30
|
-
# Method was called as a class method, e.g. Class.method(...)
|
|
31
|
-
class_method = getattr(objtype, self.class_method_name)
|
|
32
|
-
return class_method(*args, **kwargs)
|
|
33
|
-
|
|
34
|
-
return _wrapper
|
|
1
|
+
import functools
|
|
2
|
+
from typing import Any, Optional, Type, TypeVar, cast
|
|
3
|
+
|
|
4
|
+
T = TypeVar("T")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class class_method_variant(object):
|
|
8
|
+
def __init__(self, class_method_name):
|
|
9
|
+
self.class_method_name = class_method_name
|
|
10
|
+
|
|
11
|
+
method: Any
|
|
12
|
+
|
|
13
|
+
def __call__(self, method: T) -> T:
|
|
14
|
+
self.method = method
|
|
15
|
+
return cast(T, self)
|
|
16
|
+
|
|
17
|
+
def __get__(self, obj, objtype: Optional[Type[Any]] = None):
|
|
18
|
+
@functools.wraps(self.method)
|
|
19
|
+
def _wrapper(*args, **kwargs):
|
|
20
|
+
if obj is not None:
|
|
21
|
+
# Method was called as an instance method, e.g.
|
|
22
|
+
# instance.method(...)
|
|
23
|
+
return self.method(obj, *args, **kwargs)
|
|
24
|
+
elif len(args) > 0 and objtype is not None and isinstance(args[0], objtype):
|
|
25
|
+
# Method was called as a class method with the instance as the
|
|
26
|
+
# first argument, e.g. Class.method(instance, ...) which in
|
|
27
|
+
# Python is the same thing as calling an instance method
|
|
28
|
+
return self.method(args[0], *args[1:], **kwargs)
|
|
29
|
+
else:
|
|
30
|
+
# Method was called as a class method, e.g. Class.method(...)
|
|
31
|
+
class_method = getattr(objtype, self.class_method_name)
|
|
32
|
+
return class_method(*args, **kwargs)
|
|
33
|
+
|
|
34
|
+
return _wrapper
|
scalebox/sandbox_async/main.py
CHANGED
|
@@ -6,10 +6,10 @@ from typing import Dict, List, Optional, TypedDict, overload
|
|
|
6
6
|
import aiohttp
|
|
7
7
|
import httpx
|
|
8
8
|
from aiohttp import TCPConnector
|
|
9
|
-
from typing_extensions import Unpack
|
|
9
|
+
from typing_extensions import Unpack, Self
|
|
10
10
|
|
|
11
11
|
from ..api.client.types import Unset
|
|
12
|
-
from ..connection_config import ConnectionConfig, ProxyTypes
|
|
12
|
+
from ..connection_config import ConnectionConfig, ProxyTypes, ApiParams
|
|
13
13
|
from ..exceptions import SandboxException, request_timeout_error
|
|
14
14
|
from ..generated.api import ENVD_API_HEALTH_ROUTE, ahandle_envd_api_exception
|
|
15
15
|
from ..sandbox.main import SandboxSetup
|
|
@@ -41,6 +41,8 @@ class AsyncSandboxOpts(TypedDict):
|
|
|
41
41
|
envd_version: Optional[str]
|
|
42
42
|
envd_access_token: Optional[str]
|
|
43
43
|
connection_config: ConnectionConfig
|
|
44
|
+
object_storage: Optional[Dict[str, str]]
|
|
45
|
+
network_proxy: Optional[Dict[str, any]]
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
@@ -101,6 +103,17 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
101
103
|
"""
|
|
102
104
|
return self._sandbox_domain
|
|
103
105
|
|
|
106
|
+
@property
|
|
107
|
+
def object_storage(self) -> Optional[Dict[str, str]]:
|
|
108
|
+
"""
|
|
109
|
+
Object storage configuration returned during sandbox creation (if any).
|
|
110
|
+
Only synchronous sandboxes currently expose this field.
|
|
111
|
+
"""
|
|
112
|
+
return self._object_storage
|
|
113
|
+
|
|
114
|
+
def network_proxy(self) -> Optional[Dict[str, object]]:
|
|
115
|
+
return self._network_proxy
|
|
116
|
+
|
|
104
117
|
@property
|
|
105
118
|
def envd_api_url(self) -> str:
|
|
106
119
|
return self._envd_api_url
|
|
@@ -126,7 +139,8 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
126
139
|
super().__init__()
|
|
127
140
|
|
|
128
141
|
self._connection_config = opts["connection_config"]
|
|
129
|
-
|
|
142
|
+
self._object_storage = opts["object_storage"]
|
|
143
|
+
self._network_proxy = opts["network_proxy"]
|
|
130
144
|
self._sandbox_id = opts["sandbox_id"]
|
|
131
145
|
self._sandbox_domain = opts["sandbox_domain"] or self.connection_config.domain
|
|
132
146
|
debug=self._connection_config.debug
|
|
@@ -222,13 +236,16 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
222
236
|
timeout: Optional[int] = None,
|
|
223
237
|
metadata: Optional[Dict[str, str]] = None,
|
|
224
238
|
envs: Optional[Dict[str, str]] = None,
|
|
239
|
+
object_storage: Optional[Dict[str, str]] = None,
|
|
225
240
|
api_key: Optional[str] = None,
|
|
226
241
|
domain: Optional[str] = None,
|
|
227
242
|
debug: Optional[bool] = None,
|
|
243
|
+
sandbox_id: Optional[str] = None,
|
|
228
244
|
request_timeout: Optional[float] = None,
|
|
229
245
|
proxy: Optional[ProxyTypes] = None,
|
|
230
246
|
secure: Optional[bool] = None,
|
|
231
247
|
allow_internet_access: Optional[bool] = True,
|
|
248
|
+
net_proxy_country: Optional[str] = None,
|
|
232
249
|
):
|
|
233
250
|
"""
|
|
234
251
|
Create a new sandbox.
|
|
@@ -251,11 +268,32 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
251
268
|
"""
|
|
252
269
|
|
|
253
270
|
connection_headers = {"Authorization": "Bearer root", }
|
|
271
|
+
network_proxy = {}
|
|
254
272
|
if debug:
|
|
255
273
|
sandbox_id = "debug_sandbox_id"
|
|
256
274
|
sandbox_domain = None
|
|
257
275
|
envd_version = None
|
|
258
276
|
envd_access_token = None
|
|
277
|
+
elif sandbox_id is not None:
|
|
278
|
+
response = await SandboxApi._cls_get_info(
|
|
279
|
+
sandbox_id,
|
|
280
|
+
api_key=api_key,
|
|
281
|
+
domain=domain,
|
|
282
|
+
debug=debug,
|
|
283
|
+
request_timeout=request_timeout,
|
|
284
|
+
proxy=proxy,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
sandbox_domain = response.sandbox_domain
|
|
288
|
+
envd_version = response.envd_version
|
|
289
|
+
envd_access_token = response._envd_access_token
|
|
290
|
+
object_storage = response.object_storage
|
|
291
|
+
network_proxy = response.network_proxy
|
|
292
|
+
|
|
293
|
+
if response._envd_access_token is not None and not isinstance(
|
|
294
|
+
response._envd_access_token, Unset
|
|
295
|
+
):
|
|
296
|
+
connection_headers["X-Access-Token"] = response._envd_access_token
|
|
259
297
|
else:
|
|
260
298
|
response = await SandboxApi._create_sandbox(
|
|
261
299
|
template=template or cls.default_template,
|
|
@@ -269,12 +307,16 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
269
307
|
secure=secure,
|
|
270
308
|
proxy=proxy,
|
|
271
309
|
allow_internet_access=allow_internet_access,
|
|
310
|
+
object_storage=object_storage,
|
|
311
|
+
net_proxy_country=net_proxy_country,
|
|
272
312
|
)
|
|
273
313
|
|
|
274
314
|
sandbox_id = response.sandbox_id
|
|
275
315
|
sandbox_domain = response.sandbox_domain
|
|
276
316
|
envd_version = response.envd_version
|
|
277
317
|
envd_access_token = response.envd_access_token
|
|
318
|
+
object_storage = response.object_storage
|
|
319
|
+
network_proxy = response.network_proxy
|
|
278
320
|
|
|
279
321
|
if envd_access_token is not None and not isinstance(
|
|
280
322
|
envd_access_token, Unset
|
|
@@ -297,6 +339,8 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
297
339
|
envd_version=envd_version,
|
|
298
340
|
envd_access_token=envd_access_token,
|
|
299
341
|
connection_config=connection_config,
|
|
342
|
+
object_storage=object_storage,
|
|
343
|
+
network_proxy=network_proxy
|
|
300
344
|
)
|
|
301
345
|
timeout = 10.0
|
|
302
346
|
interval = 0.3
|
|
@@ -312,69 +356,158 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
312
356
|
time.sleep(interval)
|
|
313
357
|
elapsed += interval
|
|
314
358
|
else:
|
|
315
|
-
print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout
|
|
359
|
+
print("connect "+sandbox_domain+ENVD_API_HEALTH_ROUTE +" timeout 10s")
|
|
316
360
|
return sanbox
|
|
317
361
|
|
|
362
|
+
@overload
|
|
363
|
+
async def connect(
|
|
364
|
+
self,
|
|
365
|
+
timeout: Optional[int] = None,
|
|
366
|
+
**opts: Unpack[ApiParams],
|
|
367
|
+
) -> Self:
|
|
368
|
+
"""
|
|
369
|
+
Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
|
|
370
|
+
Sandbox must be either running or be paused.
|
|
371
|
+
|
|
372
|
+
With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
|
|
373
|
+
|
|
374
|
+
:param timeout: Timeout for the sandbox in **seconds**
|
|
375
|
+
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
|
|
376
|
+
:return: A running sandbox instance
|
|
377
|
+
|
|
378
|
+
@example
|
|
379
|
+
```python
|
|
380
|
+
sandbox = await AsyncSandbox.create()
|
|
381
|
+
await sandbox.beta_pause()
|
|
382
|
+
|
|
383
|
+
# Another code block
|
|
384
|
+
same_sandbox = await sandbox.connect()
|
|
385
|
+
```
|
|
386
|
+
"""
|
|
387
|
+
...
|
|
388
|
+
|
|
389
|
+
@overload
|
|
318
390
|
@classmethod
|
|
319
391
|
async def connect(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
proxy: Optional[ProxyTypes] = None,
|
|
326
|
-
):
|
|
392
|
+
cls,
|
|
393
|
+
sandbox_id: str,
|
|
394
|
+
timeout: Optional[int] = None,
|
|
395
|
+
**opts: Unpack[ApiParams],
|
|
396
|
+
) -> Self:
|
|
327
397
|
"""
|
|
328
|
-
Connect to
|
|
398
|
+
Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
|
|
399
|
+
Sandbox must be either running or be paused.
|
|
400
|
+
|
|
329
401
|
With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
|
|
330
402
|
|
|
331
403
|
:param sandbox_id: Sandbox ID
|
|
332
|
-
:param
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
:return: sandbox instance for the existing sandbox
|
|
404
|
+
:param timeout: Timeout for the sandbox in **seconds**
|
|
405
|
+
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
|
|
406
|
+
:return: A running sandbox instance
|
|
336
407
|
|
|
337
408
|
@example
|
|
338
409
|
```python
|
|
339
410
|
sandbox = await AsyncSandbox.create()
|
|
340
|
-
|
|
411
|
+
await AsyncSandbox.beta_pause(sandbox.sandbox_id)
|
|
341
412
|
|
|
342
413
|
# Another code block
|
|
343
|
-
same_sandbox = await AsyncSandbox.connect(sandbox_id)
|
|
414
|
+
same_sandbox = await AsyncSandbox.connect(sandbox.sandbox_id))
|
|
415
|
+
```
|
|
344
416
|
"""
|
|
417
|
+
...
|
|
345
418
|
|
|
346
|
-
|
|
419
|
+
@class_method_variant("_cls_connect")
|
|
420
|
+
async def connect(
|
|
421
|
+
self,
|
|
422
|
+
timeout: Optional[int] = None,
|
|
423
|
+
**opts: Unpack[ApiParams],
|
|
424
|
+
) -> Self:
|
|
425
|
+
"""
|
|
426
|
+
Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
|
|
427
|
+
Sandbox must be either running or be paused.
|
|
347
428
|
|
|
348
|
-
|
|
349
|
-
sandbox_id,
|
|
350
|
-
api_key=api_key,
|
|
351
|
-
domain=domain,
|
|
352
|
-
debug=debug,
|
|
353
|
-
proxy=proxy,
|
|
354
|
-
)
|
|
429
|
+
With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
|
|
355
430
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
connection_headers["X-Access-Token"] = response._envd_access_token
|
|
360
|
-
print("connection_headers:"+str(connection_headers))
|
|
431
|
+
:param timeout: Timeout for the sandbox in **seconds**
|
|
432
|
+
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
|
|
433
|
+
:return: A running sandbox instance
|
|
361
434
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
headers=connection_headers,
|
|
367
|
-
proxy=proxy,
|
|
368
|
-
)
|
|
435
|
+
@example
|
|
436
|
+
```python
|
|
437
|
+
sandbox = await AsyncSandbox.create()
|
|
438
|
+
await sandbox.beta_pause()
|
|
369
439
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
440
|
+
# Another code block
|
|
441
|
+
same_sandbox = await sandbox.connect()
|
|
442
|
+
```
|
|
443
|
+
"""
|
|
444
|
+
await SandboxApi._cls_connect(
|
|
445
|
+
sandbox_id=self.sandbox_id,
|
|
446
|
+
timeout=timeout,
|
|
447
|
+
**opts,
|
|
376
448
|
)
|
|
377
449
|
|
|
450
|
+
return self
|
|
451
|
+
# @classmethod
|
|
452
|
+
# async def connect(
|
|
453
|
+
# cls,
|
|
454
|
+
# sandbox_id: str,
|
|
455
|
+
# api_key: Optional[str] = None,
|
|
456
|
+
# domain: Optional[str] = None,
|
|
457
|
+
# debug: Optional[bool] = None,
|
|
458
|
+
# proxy: Optional[ProxyTypes] = None,
|
|
459
|
+
# ):
|
|
460
|
+
# """
|
|
461
|
+
# Connect to an existing sandbox.
|
|
462
|
+
# With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
|
|
463
|
+
#
|
|
464
|
+
# :param sandbox_id: Sandbox ID
|
|
465
|
+
# :param api_key: SBX API Key to use for authentication, defaults to `SBX_API_KEY` environment variable
|
|
466
|
+
# :param proxy: Proxy to use for the request and for the **requests made to the returned sandbox**
|
|
467
|
+
#
|
|
468
|
+
# :return: sandbox instance for the existing sandbox
|
|
469
|
+
#
|
|
470
|
+
# @example
|
|
471
|
+
# ```python
|
|
472
|
+
# sandbox = await AsyncSandbox.create()
|
|
473
|
+
# sandbox_id = sandbox.sandbox_id
|
|
474
|
+
#
|
|
475
|
+
# # Another code block
|
|
476
|
+
# same_sandbox = await AsyncSandbox.connect(sandbox_id)
|
|
477
|
+
# """
|
|
478
|
+
#
|
|
479
|
+
# connection_headers = {"Authorization": "Bearer root"}
|
|
480
|
+
#
|
|
481
|
+
# response = await SandboxApi._cls_get_info(
|
|
482
|
+
# sandbox_id,
|
|
483
|
+
# api_key=api_key,
|
|
484
|
+
# domain=domain,
|
|
485
|
+
# debug=debug,
|
|
486
|
+
# proxy=proxy,
|
|
487
|
+
# )
|
|
488
|
+
#
|
|
489
|
+
# if response._envd_access_token is not None and not isinstance(
|
|
490
|
+
# response._envd_access_token, Unset
|
|
491
|
+
# ):
|
|
492
|
+
# connection_headers["X-Access-Token"] = response._envd_access_token
|
|
493
|
+
# print("connection_headers:"+str(connection_headers))
|
|
494
|
+
#
|
|
495
|
+
# connection_config = ConnectionConfig(
|
|
496
|
+
# api_key=api_key,
|
|
497
|
+
# domain=domain,
|
|
498
|
+
# debug=debug,
|
|
499
|
+
# headers=connection_headers,
|
|
500
|
+
# proxy=proxy,
|
|
501
|
+
# )
|
|
502
|
+
#
|
|
503
|
+
# return cls(
|
|
504
|
+
# sandbox_id=sandbox_id,
|
|
505
|
+
# sandbox_domain=response.sandbox_domain,
|
|
506
|
+
# connection_config=connection_config,
|
|
507
|
+
# envd_version=response.envd_version,
|
|
508
|
+
# envd_access_token=response._envd_access_token,
|
|
509
|
+
# )
|
|
510
|
+
|
|
378
511
|
async def __aenter__(self):
|
|
379
512
|
if self._session.closed:
|
|
380
513
|
connector = TCPConnector(
|
|
@@ -645,3 +778,52 @@ class AsyncSandbox(SandboxSetup, SandboxApi):
|
|
|
645
778
|
end=end,
|
|
646
779
|
**config_dict,
|
|
647
780
|
)
|
|
781
|
+
|
|
782
|
+
@overload
|
|
783
|
+
async def beta_pause(
|
|
784
|
+
self,
|
|
785
|
+
**opts: Unpack[ApiParams],
|
|
786
|
+
) -> None:
|
|
787
|
+
"""
|
|
788
|
+
[BETA] This feature is in beta and may change in the future.
|
|
789
|
+
|
|
790
|
+
Pause the sandbox.
|
|
791
|
+
|
|
792
|
+
:return: Sandbox ID that can be used to resume the sandbox
|
|
793
|
+
"""
|
|
794
|
+
...
|
|
795
|
+
|
|
796
|
+
@overload
|
|
797
|
+
@staticmethod
|
|
798
|
+
async def beta_pause(
|
|
799
|
+
sandbox_id: str,
|
|
800
|
+
**opts: Unpack[ApiParams],
|
|
801
|
+
) -> None:
|
|
802
|
+
"""
|
|
803
|
+
[BETA] This feature is in beta and may change in the future.
|
|
804
|
+
|
|
805
|
+
Pause the sandbox specified by sandbox ID.
|
|
806
|
+
|
|
807
|
+
:param sandbox_id: Sandbox ID
|
|
808
|
+
|
|
809
|
+
:return: Sandbox ID that can be used to resume the sandbox
|
|
810
|
+
"""
|
|
811
|
+
...
|
|
812
|
+
|
|
813
|
+
@class_method_variant("_cls_pause")
|
|
814
|
+
async def beta_pause(
|
|
815
|
+
self,
|
|
816
|
+
**opts: Unpack[ApiParams],
|
|
817
|
+
) -> None:
|
|
818
|
+
"""
|
|
819
|
+
[BETA] This feature is in beta and may change in the future.
|
|
820
|
+
|
|
821
|
+
Pause the sandbox.
|
|
822
|
+
|
|
823
|
+
:return: Sandbox ID that can be used to resume the sandbox
|
|
824
|
+
"""
|
|
825
|
+
|
|
826
|
+
await SandboxApi._cls_pause(
|
|
827
|
+
sandbox_id=self.sandbox_id,
|
|
828
|
+
**opts,
|
|
829
|
+
)
|