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.
Files changed (68) hide show
  1. scalebox/__init__.py +1 -1
  2. scalebox/api/__init__.py +128 -128
  3. scalebox/api/client/__init__.py +8 -8
  4. scalebox/api/client/api/sandboxes/get_sandboxes.py +5 -3
  5. scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +2 -2
  6. scalebox/api/client/api/sandboxes/post_sandboxes.py +2 -2
  7. scalebox/api/client/client.py +288 -288
  8. scalebox/api/client/models/listed_sandbox.py +11 -9
  9. scalebox/api/client/models/new_sandbox.py +1 -1
  10. scalebox/api/client/models/sandbox.py +125 -125
  11. scalebox/api/client/models/sandbox_state.py +1 -0
  12. scalebox/api/client/types.py +46 -46
  13. scalebox/code_interpreter/code_interpreter_async.py +370 -369
  14. scalebox/code_interpreter/code_interpreter_sync.py +318 -317
  15. scalebox/connection_config.py +92 -92
  16. scalebox/csx_desktop/main.py +12 -12
  17. scalebox/generated/api_pb2_connect.py +17 -66
  18. scalebox/sandbox_async/commands/command.py +307 -307
  19. scalebox/sandbox_async/commands/command_handle.py +187 -187
  20. scalebox/sandbox_async/commands/pty.py +187 -187
  21. scalebox/sandbox_async/filesystem/filesystem.py +557 -557
  22. scalebox/sandbox_async/filesystem/watch_handle.py +61 -61
  23. scalebox/sandbox_async/main.py +647 -646
  24. scalebox/sandbox_async/sandbox_api.py +365 -365
  25. scalebox/sandbox_async/utils.py +7 -7
  26. scalebox/sandbox_sync/__init__.py +2 -2
  27. scalebox/sandbox_sync/commands/command.py +300 -300
  28. scalebox/sandbox_sync/commands/command_handle.py +150 -150
  29. scalebox/sandbox_sync/commands/pty.py +181 -181
  30. scalebox/sandbox_sync/filesystem/filesystem.py +543 -543
  31. scalebox/sandbox_sync/filesystem/watch_handle.py +66 -66
  32. scalebox/sandbox_sync/main.py +789 -790
  33. scalebox/sandbox_sync/sandbox_api.py +356 -356
  34. scalebox/test/CODE_INTERPRETER_TESTS_READY.md +256 -256
  35. scalebox/test/README.md +164 -164
  36. scalebox/test/aclient.py +72 -72
  37. scalebox/test/code_interpreter_centext.py +21 -21
  38. scalebox/test/code_interpreter_centext_sync.py +21 -21
  39. scalebox/test/code_interpreter_test.py +1 -1
  40. scalebox/test/code_interpreter_test_sync.py +1 -1
  41. scalebox/test/run_all_validation_tests.py +334 -334
  42. scalebox/test/test_basic.py +78 -78
  43. scalebox/test/test_code_interpreter_async_comprehensive.py +2653 -2653
  44. scalebox/test/{test_code_interpreter_e2bsync_comprehensive.py → test_code_interpreter_execcode.py} +328 -392
  45. scalebox/test/test_code_interpreter_sync_comprehensive.py +3416 -3412
  46. scalebox/test/test_csx_desktop_examples.py +130 -0
  47. scalebox/test/test_sandbox_async_comprehensive.py +736 -738
  48. scalebox/test/test_sandbox_stress_and_edge_cases.py +778 -778
  49. scalebox/test/test_sandbox_sync_comprehensive.py +779 -770
  50. scalebox/test/test_sandbox_usage_examples.py +987 -987
  51. scalebox/test/testacreate.py +24 -24
  52. scalebox/test/testagetinfo.py +18 -18
  53. scalebox/test/testcodeinterpreter_async.py +508 -508
  54. scalebox/test/testcodeinterpreter_sync.py +239 -239
  55. scalebox/test/testcomputeuse.py +2 -2
  56. scalebox/test/testnovnc.py +12 -12
  57. scalebox/test/testsandbox_api.py +15 -0
  58. scalebox/test/testsandbox_async.py +202 -118
  59. scalebox/test/testsandbox_sync.py +71 -38
  60. scalebox/version.py +2 -2
  61. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/METADATA +104 -103
  62. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/RECORD +66 -66
  63. scalebox/test/test_code_interpreter_e2basync_comprehensive.py +0 -2655
  64. scalebox/test/test_e2b_first.py +0 -11
  65. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/WHEEL +0 -0
  66. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/entry_points.txt +0 -0
  67. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/licenses/LICENSE +0 -0
  68. {scalebox_sdk-0.1.4.dist-info → scalebox_sdk-0.1.25.dist-info}/top_level.txt +0 -0
@@ -1,317 +1,318 @@
1
- import logging
2
- import socket
3
- import time
4
- from typing import Dict, Iterator, Literal, Optional, Union, overload
5
-
6
- import urllib3
7
- from httpx import Timeout
8
- from urllib3 import Retry
9
-
10
- from ..exceptions import InvalidArgumentException
11
- from ..generated import api_pb2, api_pb2_connect
12
- from ..sandbox_sync.main import Sandbox as BaseSandbox
13
- from .constants import DEFAULT_TEMPLATE, DEFAULT_TIMEOUT, JUPYTER_PORT
14
- from .exceptions import format_execution_timeout_error, format_request_timeout_error
15
- from .models import (
16
- Context,
17
- Execution,
18
- ExecutionError,
19
- OutputHandler,
20
- OutputMessage,
21
- Result,
22
- parse_output,
23
- )
24
-
25
- logger = logging.getLogger(__name__)
26
-
27
-
28
- class Sandbox(BaseSandbox):
29
- """
30
- E2B cloud sandbox is a secure and isolated cloud environment.
31
-
32
- The sandbox allows you to:
33
- - Access Linux OS
34
- - Create, list, and delete files and directories
35
- - Run commands
36
- - Run isolated code
37
- - Access the internet
38
-
39
- Check docs [here](https://.dev/docs).
40
-
41
- Use the `Sandbox()` to create a new sandbox.
42
-
43
- Example:
44
- ```python
45
- from scalebox.code_interpreter import Sandbox
46
-
47
- sandbox = Sandbox()
48
- ```
49
- """
50
-
51
- default_template = DEFAULT_TEMPLATE
52
-
53
- @property
54
- def _jupyter_url(self) -> str:
55
- return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}"
56
-
57
- @overload
58
- def run_code(
59
- self,
60
- code: str,
61
- language: Union[Literal["python"], None] = "python",
62
- on_stdout: Optional[OutputHandler[OutputMessage]] = None,
63
- on_stderr: Optional[OutputHandler[OutputMessage]] = None,
64
- on_result: Optional[OutputHandler[Result]] = None,
65
- on_error: Optional[OutputHandler[ExecutionError]] = None,
66
- envs: Optional[Dict[str, str]] = None,
67
- timeout: Optional[float] = None,
68
- request_timeout: Optional[float] = None,
69
- ) -> Execution:
70
- """
71
- Runs the code as Python.
72
-
73
- Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
74
-
75
- You can reference previously defined variables, imports, and functions in the code.
76
-
77
- :param code: Code to execute
78
- :param language: Language to use for code execution. If not defined, the default Python context is used.
79
- :param on_stdout: Callback for stdout messages
80
- :param on_stderr: Callback for stderr messages
81
- :param on_result: Callback for the `Result` object
82
- :param on_error: Callback for the `ExecutionError` object
83
- :param envs: Custom environment variables
84
- :param timeout: Timeout for the code execution in **seconds**
85
- :param request_timeout: Timeout for the request in **seconds**
86
-
87
- :return: `Execution` result object
88
- """
89
- ...
90
-
91
- @overload
92
- def run_code(
93
- self,
94
- code: str,
95
- language: Optional[str] = "python",
96
- on_stdout: Optional[OutputHandler[OutputMessage]] = None,
97
- on_stderr: Optional[OutputHandler[OutputMessage]] = None,
98
- on_result: Optional[OutputHandler[Result]] = None,
99
- on_error: Optional[OutputHandler[ExecutionError]] = None,
100
- envs: Optional[Dict[str, str]] = None,
101
- timeout: Optional[float] = None,
102
- request_timeout: Optional[float] = None,
103
- ) -> Execution:
104
- """
105
- Runs the code for the specified language.
106
-
107
- Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
108
- If no language is specified, Python is used.
109
-
110
- You can reference previously defined variables, imports, and functions in the code.
111
-
112
- :param code: Code to execute
113
- :param language: Language to use for code execution. If not defined, the default Python context is used.
114
- :param on_stdout: Callback for stdout messages
115
- :param on_stderr: Callback for stderr messages
116
- :param on_result: Callback for the `Result` object
117
- :param on_error: Callback for the `ExecutionError` object
118
- :param envs: Custom environment variables
119
- :param timeout: Timeout for the code execution in **seconds**
120
- :param request_timeout: Timeout for the request in **seconds**
121
-
122
- :return: `Execution` result object
123
- """
124
- ...
125
-
126
- @overload
127
- def run_code(
128
- self,
129
- code: str,
130
- context: Optional[Context] = None,
131
- on_stdout: Optional[OutputHandler[OutputMessage]] = None,
132
- on_stderr: Optional[OutputHandler[OutputMessage]] = None,
133
- on_result: Optional[OutputHandler[Result]] = None,
134
- on_error: Optional[OutputHandler[ExecutionError]] = None,
135
- envs: Optional[Dict[str, str]] = None,
136
- timeout: Optional[float] = None,
137
- request_timeout: Optional[float] = None,
138
- ) -> Execution:
139
- """
140
- Runs the code in the specified context, if not specified, the default context is used.
141
-
142
- Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
143
-
144
- You can reference previously defined variables, imports, and functions in the code.
145
-
146
- :param code: Code to execute
147
- :param context: Concrete context to run the code in. If not specified, the default context for the language is used. It's mutually exclusive with the language.
148
- :param on_stdout: Callback for stdout messages
149
- :param on_stderr: Callback for stderr messages
150
- :param on_result: Callback for the `Result` object
151
- :param on_error: Callback for the `ExecutionError` object
152
- :param envs: Custom environment variables
153
- :param timeout: Timeout for the code execution in **seconds**
154
- :param request_timeout: Timeout for the request in **seconds**
155
-
156
- :return: `Execution` result object
157
- """
158
- ...
159
-
160
- def run_code(
161
- self,
162
- code: str,
163
- language: Optional[str] = None,
164
- context: Optional[Context] = None,
165
- on_stdout: Optional[OutputHandler[OutputMessage]] = None,
166
- on_stderr: Optional[OutputHandler[OutputMessage]] = None,
167
- on_result: Optional[OutputHandler[Result]] = None,
168
- on_error: Optional[OutputHandler[ExecutionError]] = None,
169
- envs: Optional[Dict[str, str]] = None,
170
- timeout: Optional[float] = None,
171
- request_timeout: Optional[float] = None,
172
- ) -> Execution:
173
- logger.debug(f"Executing code {code}")
174
-
175
- if language and context:
176
- raise InvalidArgumentException(
177
- "You can provide context or language, but not both at the same time."
178
- )
179
-
180
- timeout = None if timeout == 0 else (timeout or DEFAULT_TIMEOUT)
181
- request_timeout = request_timeout or self._connection_config.request_timeout
182
- context_id = context.id if context else None
183
- client = api_pb2_connect.ExecutionServiceClient(
184
- base_url=self.envd_api_url,
185
- http_client=self._urllib3_pool,
186
- )
187
-
188
- # Create execution request
189
- execute_request = api_pb2.ExecuteRequest(
190
- code=code,
191
- context_id=context_id or "",
192
- language=language or "",
193
- env_vars=envs,
194
- )
195
-
196
- try:
197
- # Calculate deadline for gRPC call
198
- deadline = time.time() + request_timeout + (timeout or 0)
199
-
200
- # Execute code via gRPC
201
- execution = Execution()
202
- headers = {
203
- "Authorization": "Bearer root",
204
- }
205
- response_stream = client.execute(
206
- execute_request,
207
- timeout_seconds=deadline - time.time(),
208
- extra_headers=headers,
209
- )
210
-
211
- # Process stream responses
212
- for response in response_stream:
213
- # Convert gRPC response to the format expected by parse_output
214
- # This assumes parse_output can handle gRPC response format
215
- # You might need to adjust parse_output or convert the response here
216
- parse_output(
217
- execution,
218
- response,
219
- on_stdout=on_stdout,
220
- on_stderr=on_stderr,
221
- on_result=on_result,
222
- on_error=on_error,
223
- )
224
-
225
- return execution
226
-
227
- except Exception as e:
228
- # Handle different types of timeout exceptions
229
- if "timeout" in str(e).lower() or "deadline" in str(e).lower():
230
- if "execution" in str(e).lower():
231
- raise format_execution_timeout_error()
232
- else:
233
- raise format_request_timeout_error()
234
- else:
235
- # Re-raise other exceptions
236
- raise e
237
-
238
- def create_code_context(
239
- self,
240
- cwd: Optional[str] = None,
241
- language: Optional[str] = None,
242
- request_timeout: Optional[float] = None,
243
- ) -> Context:
244
- """
245
- Creates a new context to run code in.
246
-
247
- :param cwd: Set the current working directory for the context, defaults to `/home/user`
248
- :param language: Language of the context. If not specified, defaults to Python
249
- :param request_timeout: Timeout for the request in **milliseconds**
250
-
251
- :return: Context object
252
- """
253
- logger.debug(f"Creating new {language} context")
254
-
255
- # Create context request
256
- create_context_request = api_pb2.CreateContextRequest(
257
- language=language or "python3",
258
- cwd=cwd or "",
259
- )
260
-
261
- try:
262
- client = api_pb2_connect.ContextServiceClient(
263
- base_url=self.envd_api_url,
264
- http_client=self._urllib3_pool,
265
- )
266
- headers = {
267
- "Authorization": "Bearer root",
268
- }
269
- # Create context via gRPC
270
- response = client.create_context(
271
- create_context_request,
272
- timeout_seconds=request_timeout
273
- or self._connection_config.request_timeout,
274
- extra_headers=headers,
275
- )
276
-
277
- return Context.from_json(
278
- {
279
- "id": response.id,
280
- "language": response.language,
281
- "cwd": response.cwd,
282
- }
283
- )
284
-
285
- except Exception as e:
286
- # Handle timeout exceptions
287
- if "timeout" in str(e).lower() or "deadline" in str(e).lower():
288
- raise format_request_timeout_error()
289
- else:
290
- # Re-raise other exceptions
291
- raise e
292
-
293
- def destroy_context(self, context: Context) -> None:
294
- """
295
- Destroys a context.
296
-
297
- :param context: Context to destroy
298
- """
299
- logger.debug(f"Destroying context {context.id}")
300
-
301
- # Create destroy context request
302
- destroy_context_request = api_pb2.DestroyContextRequest(
303
- context_id=context.id,
304
- )
305
-
306
- try:
307
- client = api_pb2_connect.ContextServiceClient(
308
- base_url=self.envd_api_url,
309
- http_client=self._urllib3_pool,
310
- )
311
- headers = {
312
- "Authorization": "Bearer root",
313
- }
314
- client.destroy_context(destroy_context_request, extra_headers=headers)
315
-
316
- except Exception as e:
317
- logger.warning(f"Failed to destroy context {context.id}: {e}")
1
+ import logging
2
+ import socket
3
+ import time
4
+ from typing import Dict, Iterator, Literal, Optional, Union, overload
5
+
6
+ import urllib3
7
+ from httpx import Timeout
8
+ from urllib3 import Retry
9
+
10
+ from ..exceptions import InvalidArgumentException
11
+ from ..generated import api_pb2, api_pb2_connect
12
+ from ..sandbox_sync.main import Sandbox as BaseSandbox
13
+ from .constants import DEFAULT_TEMPLATE, DEFAULT_TIMEOUT, JUPYTER_PORT
14
+ from .exceptions import format_execution_timeout_error, format_request_timeout_error
15
+ from .models import (
16
+ Context,
17
+ Execution,
18
+ ExecutionError,
19
+ OutputHandler,
20
+ OutputMessage,
21
+ Result,
22
+ parse_output,
23
+ )
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class Sandbox(BaseSandbox):
29
+ """
30
+ Scalebox cloud sandbox is a secure and isolated cloud environment.
31
+
32
+ The sandbox allows you to:
33
+ - Access Linux OS
34
+ - Create, list, and delete files and directories
35
+ - Run commands
36
+ - Run isolated code
37
+ - Access the internet
38
+
39
+ Check docs for more information.
40
+
41
+ Use the `Sandbox()` to create a new sandbox.
42
+
43
+ Example:
44
+ ```python
45
+ from scalebox.code_interpreter import Sandbox
46
+
47
+ sandbox = Sandbox()
48
+ ```
49
+ """
50
+
51
+ default_template = DEFAULT_TEMPLATE
52
+
53
+ @property
54
+ def _jupyter_url(self) -> str:
55
+ return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}"
56
+
57
+ @overload
58
+ def run_code(
59
+ self,
60
+ code: str,
61
+ language: Union[Literal["python"], None] = "python",
62
+ on_stdout: Optional[OutputHandler[OutputMessage]] = None,
63
+ on_stderr: Optional[OutputHandler[OutputMessage]] = None,
64
+ on_result: Optional[OutputHandler[Result]] = None,
65
+ on_error: Optional[OutputHandler[ExecutionError]] = None,
66
+ envs: Optional[Dict[str, str]] = None,
67
+ timeout: Optional[float] = None,
68
+ request_timeout: Optional[float] = None,
69
+ ) -> Execution:
70
+ """
71
+ Runs the code as Python.
72
+
73
+ Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
74
+
75
+ You can reference previously defined variables, imports, and functions in the code.
76
+
77
+ :param code: Code to execute
78
+ :param language: Language to use for code execution. If not defined, the default Python context is used.
79
+ :param on_stdout: Callback for stdout messages
80
+ :param on_stderr: Callback for stderr messages
81
+ :param on_result: Callback for the `Result` object
82
+ :param on_error: Callback for the `ExecutionError` object
83
+ :param envs: Custom environment variables
84
+ :param timeout: Timeout for the code execution in **seconds**
85
+ :param request_timeout: Timeout for the request in **seconds**
86
+
87
+ :return: `Execution` result object
88
+ """
89
+ ...
90
+
91
+ @overload
92
+ def run_code(
93
+ self,
94
+ code: str,
95
+ language: Optional[str] = "python",
96
+ on_stdout: Optional[OutputHandler[OutputMessage]] = None,
97
+ on_stderr: Optional[OutputHandler[OutputMessage]] = None,
98
+ on_result: Optional[OutputHandler[Result]] = None,
99
+ on_error: Optional[OutputHandler[ExecutionError]] = None,
100
+ envs: Optional[Dict[str, str]] = None,
101
+ timeout: Optional[float] = None,
102
+ request_timeout: Optional[float] = None,
103
+ ) -> Execution:
104
+ """
105
+ Runs the code for the specified language.
106
+
107
+ Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
108
+ If no language is specified, Python is used.
109
+
110
+ You can reference previously defined variables, imports, and functions in the code.
111
+
112
+ :param code: Code to execute
113
+ :param language: Language to use for code execution. If not defined, the default Python context is used.
114
+ :param on_stdout: Callback for stdout messages
115
+ :param on_stderr: Callback for stderr messages
116
+ :param on_result: Callback for the `Result` object
117
+ :param on_error: Callback for the `ExecutionError` object
118
+ :param envs: Custom environment variables
119
+ :param timeout: Timeout for the code execution in **seconds**
120
+ :param request_timeout: Timeout for the request in **seconds**
121
+
122
+ :return: `Execution` result object
123
+ """
124
+ ...
125
+
126
+ @overload
127
+ def run_code(
128
+ self,
129
+ code: str,
130
+ context: Optional[Context] = None,
131
+ on_stdout: Optional[OutputHandler[OutputMessage]] = None,
132
+ on_stderr: Optional[OutputHandler[OutputMessage]] = None,
133
+ on_result: Optional[OutputHandler[Result]] = None,
134
+ on_error: Optional[OutputHandler[ExecutionError]] = None,
135
+ envs: Optional[Dict[str, str]] = None,
136
+ timeout: Optional[float] = None,
137
+ request_timeout: Optional[float] = None,
138
+ ) -> Execution:
139
+ """
140
+ Runs the code in the specified context, if not specified, the default context is used.
141
+
142
+ Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
143
+
144
+ You can reference previously defined variables, imports, and functions in the code.
145
+
146
+ :param code: Code to execute
147
+ :param context: Concrete context to run the code in. If not specified, the default context for the language is used. It's mutually exclusive with the language.
148
+ :param on_stdout: Callback for stdout messages
149
+ :param on_stderr: Callback for stderr messages
150
+ :param on_result: Callback for the `Result` object
151
+ :param on_error: Callback for the `ExecutionError` object
152
+ :param envs: Custom environment variables
153
+ :param timeout: Timeout for the code execution in **seconds**
154
+ :param request_timeout: Timeout for the request in **seconds**
155
+
156
+ :return: `Execution` result object
157
+ """
158
+ ...
159
+
160
+ def run_code(
161
+ self,
162
+ code: str,
163
+ language: Optional[str] = None,
164
+ context: Optional[Context] = None,
165
+ on_stdout: Optional[OutputHandler[OutputMessage]] = None,
166
+ on_stderr: Optional[OutputHandler[OutputMessage]] = None,
167
+ on_result: Optional[OutputHandler[Result]] = None,
168
+ on_error: Optional[OutputHandler[ExecutionError]] = None,
169
+ envs: Optional[Dict[str, str]] = None,
170
+ timeout: Optional[float] = None,
171
+ request_timeout: Optional[float] = None,
172
+ ) -> Execution:
173
+ logger.debug(f"Executing code {code}")
174
+
175
+ if language and context:
176
+ raise InvalidArgumentException(
177
+ "You can provide context or language, but not both at the same time."
178
+ )
179
+ if language is None and context is None:
180
+ language = "python"
181
+ timeout = None if timeout == 0 else (timeout or DEFAULT_TIMEOUT)
182
+ request_timeout = request_timeout or self._connection_config.request_timeout
183
+ context_id = context.id if context else None
184
+ client = api_pb2_connect.ExecutionServiceClient(
185
+ base_url=self.envd_api_url,
186
+ http_client=self._urllib3_pool,
187
+ )
188
+
189
+ # Create execution request
190
+ execute_request = api_pb2.ExecuteRequest(
191
+ code=code,
192
+ context_id=context_id or "",
193
+ language=language or "",
194
+ env_vars=envs,
195
+ )
196
+
197
+ try:
198
+ # Calculate deadline for gRPC call
199
+ deadline = time.time() + request_timeout + (timeout or 0)
200
+
201
+ # Execute code via gRPC
202
+ execution = Execution()
203
+ # headers = {
204
+ # "Authorization": "Bearer root",
205
+ # }
206
+ response_stream = client.execute(
207
+ execute_request,
208
+ timeout_seconds=deadline - time.time(),
209
+ extra_headers=self.connection_config.headers,
210
+ )
211
+
212
+ # Process stream responses
213
+ for response in response_stream:
214
+ # Convert gRPC response to the format expected by parse_output
215
+ # This assumes parse_output can handle gRPC response format
216
+ # You might need to adjust parse_output or convert the response here
217
+ parse_output(
218
+ execution,
219
+ response,
220
+ on_stdout=on_stdout,
221
+ on_stderr=on_stderr,
222
+ on_result=on_result,
223
+ on_error=on_error,
224
+ )
225
+
226
+ return execution
227
+
228
+ except Exception as e:
229
+ # Handle different types of timeout exceptions
230
+ if "timeout" in str(e).lower() or "deadline" in str(e).lower():
231
+ if "execution" in str(e).lower():
232
+ raise format_execution_timeout_error()
233
+ else:
234
+ raise format_request_timeout_error()
235
+ else:
236
+ # Re-raise other exceptions
237
+ raise e
238
+
239
+ def create_code_context(
240
+ self,
241
+ cwd: Optional[str] = None,
242
+ language: Optional[str] = None,
243
+ request_timeout: Optional[float] = None,
244
+ ) -> Context:
245
+ """
246
+ Creates a new context to run code in.
247
+
248
+ :param cwd: Set the current working directory for the context, defaults to `/home/user`
249
+ :param language: Language of the context. If not specified, defaults to Python
250
+ :param request_timeout: Timeout for the request in **milliseconds**
251
+
252
+ :return: Context object
253
+ """
254
+ logger.debug(f"Creating new {language} context")
255
+
256
+ # Create context request
257
+ create_context_request = api_pb2.CreateContextRequest(
258
+ language=language or "python3",
259
+ cwd=cwd or "",
260
+ )
261
+
262
+ try:
263
+ client = api_pb2_connect.ContextServiceClient(
264
+ base_url=self.envd_api_url,
265
+ http_client=self._urllib3_pool,
266
+ )
267
+ # headers = {
268
+ # "Authorization": "Bearer root",
269
+ # }
270
+ # Create context via gRPC
271
+ response = client.create_context(
272
+ create_context_request,
273
+ timeout_seconds=request_timeout
274
+ or self._connection_config.request_timeout,
275
+ extra_headers=self.connection_config.headers,
276
+ )
277
+
278
+ return Context.from_json(
279
+ {
280
+ "id": response.id,
281
+ "language": response.language,
282
+ "cwd": response.cwd,
283
+ }
284
+ )
285
+
286
+ except Exception as e:
287
+ # Handle timeout exceptions
288
+ if "timeout" in str(e).lower() or "deadline" in str(e).lower():
289
+ raise format_request_timeout_error()
290
+ else:
291
+ # Re-raise other exceptions
292
+ raise e
293
+
294
+ def destroy_context(self, context: Context) -> None:
295
+ """
296
+ Destroys a context.
297
+
298
+ :param context: Context to destroy
299
+ """
300
+ logger.debug(f"Destroying context {context.id}")
301
+
302
+ # Create destroy context request
303
+ destroy_context_request = api_pb2.DestroyContextRequest(
304
+ context_id=context.id,
305
+ )
306
+
307
+ try:
308
+ client = api_pb2_connect.ContextServiceClient(
309
+ base_url=self.envd_api_url,
310
+ http_client=self._urllib3_pool,
311
+ )
312
+ # headers = {
313
+ # "Authorization": "Bearer root",
314
+ # }
315
+ client.destroy_context(destroy_context_request,extra_headers=self.connection_config.headers)
316
+
317
+ except Exception as e:
318
+ logger.warning(f"Failed to destroy context {context.id}: {e}")