opensandbox 0.1.2.dev0__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 (151) hide show
  1. opensandbox/__init__.py +117 -0
  2. opensandbox/adapters/__init__.py +36 -0
  3. opensandbox/adapters/command_adapter.py +224 -0
  4. opensandbox/adapters/converter/__init__.py +52 -0
  5. opensandbox/adapters/converter/event_node.py +55 -0
  6. opensandbox/adapters/converter/exception_converter.py +232 -0
  7. opensandbox/adapters/converter/execution_converter.py +74 -0
  8. opensandbox/adapters/converter/execution_event_dispatcher.py +129 -0
  9. opensandbox/adapters/converter/filesystem_model_converter.py +135 -0
  10. opensandbox/adapters/converter/metrics_model_converter.py +43 -0
  11. opensandbox/adapters/converter/response_handler.py +112 -0
  12. opensandbox/adapters/converter/sandbox_model_converter.py +325 -0
  13. opensandbox/adapters/factory.py +113 -0
  14. opensandbox/adapters/filesystem_adapter.py +489 -0
  15. opensandbox/adapters/health_adapter.py +110 -0
  16. opensandbox/adapters/metrics_adapter.py +123 -0
  17. opensandbox/adapters/sandboxes_adapter.py +362 -0
  18. opensandbox/api/__init__.py +17 -0
  19. opensandbox/api/execd/__init__.py +24 -0
  20. opensandbox/api/execd/api/__init__.py +17 -0
  21. opensandbox/api/execd/api/code_interpreting/__init__.py +17 -0
  22. opensandbox/api/execd/api/code_interpreting/create_code_context.py +203 -0
  23. opensandbox/api/execd/api/code_interpreting/delete_context.py +192 -0
  24. opensandbox/api/execd/api/code_interpreting/delete_contexts_by_language.py +192 -0
  25. opensandbox/api/execd/api/code_interpreting/get_context.py +193 -0
  26. opensandbox/api/execd/api/code_interpreting/interrupt_code.py +192 -0
  27. opensandbox/api/execd/api/code_interpreting/list_contexts.py +203 -0
  28. opensandbox/api/execd/api/code_interpreting/run_code.py +207 -0
  29. opensandbox/api/execd/api/command/__init__.py +17 -0
  30. opensandbox/api/execd/api/command/get_background_command_logs.py +233 -0
  31. opensandbox/api/execd/api/command/get_command_status.py +198 -0
  32. opensandbox/api/execd/api/command/interrupt_command.py +192 -0
  33. opensandbox/api/execd/api/command/run_command.py +203 -0
  34. opensandbox/api/execd/api/filesystem/__init__.py +17 -0
  35. opensandbox/api/execd/api/filesystem/chmod_files.py +197 -0
  36. opensandbox/api/execd/api/filesystem/download_file.py +233 -0
  37. opensandbox/api/execd/api/filesystem/get_files_info.py +204 -0
  38. opensandbox/api/execd/api/filesystem/make_dirs.py +197 -0
  39. opensandbox/api/execd/api/filesystem/remove_dirs.py +189 -0
  40. opensandbox/api/execd/api/filesystem/remove_files.py +189 -0
  41. opensandbox/api/execd/api/filesystem/rename_files.py +205 -0
  42. opensandbox/api/execd/api/filesystem/replace_content.py +197 -0
  43. opensandbox/api/execd/api/filesystem/search_files.py +227 -0
  44. opensandbox/api/execd/api/filesystem/upload_file.py +199 -0
  45. opensandbox/api/execd/api/health/__init__.py +17 -0
  46. opensandbox/api/execd/api/health/ping.py +106 -0
  47. opensandbox/api/execd/api/metric/__init__.py +17 -0
  48. opensandbox/api/execd/api/metric/get_metrics.py +165 -0
  49. opensandbox/api/execd/api/metric/watch_metrics.py +169 -0
  50. opensandbox/api/execd/client.py +284 -0
  51. opensandbox/api/execd/errors.py +32 -0
  52. opensandbox/api/execd/models/__init__.py +63 -0
  53. opensandbox/api/execd/models/chmod_files_body.py +75 -0
  54. opensandbox/api/execd/models/code_context.py +89 -0
  55. opensandbox/api/execd/models/code_context_request.py +78 -0
  56. opensandbox/api/execd/models/command_status_response.py +174 -0
  57. opensandbox/api/execd/models/error_response.py +86 -0
  58. opensandbox/api/execd/models/file_info.py +128 -0
  59. opensandbox/api/execd/models/file_metadata.py +105 -0
  60. opensandbox/api/execd/models/get_files_info_response_200.py +75 -0
  61. opensandbox/api/execd/models/make_dirs_body.py +75 -0
  62. opensandbox/api/execd/models/metrics.py +110 -0
  63. opensandbox/api/execd/models/permission.py +98 -0
  64. opensandbox/api/execd/models/rename_file_item.py +86 -0
  65. opensandbox/api/execd/models/replace_content_body.py +75 -0
  66. opensandbox/api/execd/models/replace_file_content_item.py +86 -0
  67. opensandbox/api/execd/models/run_code_request.py +105 -0
  68. opensandbox/api/execd/models/run_command_request.py +98 -0
  69. opensandbox/api/execd/models/server_stream_event.py +164 -0
  70. opensandbox/api/execd/models/server_stream_event_error.py +99 -0
  71. opensandbox/api/execd/models/server_stream_event_results.py +67 -0
  72. opensandbox/api/execd/models/server_stream_event_type.py +32 -0
  73. opensandbox/api/execd/models/upload_file_body.py +110 -0
  74. opensandbox/api/execd/py.typed +1 -0
  75. opensandbox/api/execd/types.py +70 -0
  76. opensandbox/api/lifecycle/__init__.py +24 -0
  77. opensandbox/api/lifecycle/api/__init__.py +17 -0
  78. opensandbox/api/lifecycle/api/sandboxes/__init__.py +17 -0
  79. opensandbox/api/lifecycle/api/sandboxes/delete_sandboxes_sandbox_id.py +202 -0
  80. opensandbox/api/lifecycle/api/sandboxes/get_sandboxes.py +252 -0
  81. opensandbox/api/lifecycle/api/sandboxes/get_sandboxes_sandbox_id.py +219 -0
  82. opensandbox/api/lifecycle/api/sandboxes/get_sandboxes_sandbox_id_endpoints_port.py +221 -0
  83. opensandbox/api/lifecycle/api/sandboxes/post_sandboxes.py +241 -0
  84. opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_pause.py +202 -0
  85. opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_renew_expiration.py +231 -0
  86. opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_resume.py +202 -0
  87. opensandbox/api/lifecycle/client.py +284 -0
  88. opensandbox/api/lifecycle/errors.py +32 -0
  89. opensandbox/api/lifecycle/models/__init__.py +57 -0
  90. opensandbox/api/lifecycle/models/create_sandbox_request.py +204 -0
  91. opensandbox/api/lifecycle/models/create_sandbox_request_env.py +67 -0
  92. opensandbox/api/lifecycle/models/create_sandbox_request_extensions.py +71 -0
  93. opensandbox/api/lifecycle/models/create_sandbox_request_metadata.py +68 -0
  94. opensandbox/api/lifecycle/models/create_sandbox_response.py +138 -0
  95. opensandbox/api/lifecycle/models/create_sandbox_response_metadata.py +62 -0
  96. opensandbox/api/lifecycle/models/endpoint.py +62 -0
  97. opensandbox/api/lifecycle/models/error_response.py +69 -0
  98. opensandbox/api/lifecycle/models/image_spec.py +91 -0
  99. opensandbox/api/lifecycle/models/image_spec_auth.py +68 -0
  100. opensandbox/api/lifecycle/models/list_sandboxes_response.py +101 -0
  101. opensandbox/api/lifecycle/models/pagination_info.py +110 -0
  102. opensandbox/api/lifecycle/models/renew_sandbox_expiration_request.py +63 -0
  103. opensandbox/api/lifecycle/models/renew_sandbox_expiration_response.py +62 -0
  104. opensandbox/api/lifecycle/models/resource_limits.py +73 -0
  105. opensandbox/api/lifecycle/models/sandbox.py +151 -0
  106. opensandbox/api/lifecycle/models/sandbox_metadata.py +62 -0
  107. opensandbox/api/lifecycle/models/sandbox_status.py +138 -0
  108. opensandbox/api/lifecycle/py.typed +1 -0
  109. opensandbox/api/lifecycle/types.py +70 -0
  110. opensandbox/config/__init__.py +23 -0
  111. opensandbox/config/connection.py +159 -0
  112. opensandbox/config/connection_sync.py +131 -0
  113. opensandbox/constants.py +20 -0
  114. opensandbox/exceptions/__init__.py +38 -0
  115. opensandbox/exceptions/sandbox.py +134 -0
  116. opensandbox/manager.py +240 -0
  117. opensandbox/models/__init__.py +81 -0
  118. opensandbox/models/execd.py +233 -0
  119. opensandbox/models/execd_sync.py +43 -0
  120. opensandbox/models/filesystem.py +205 -0
  121. opensandbox/models/sandboxes.py +301 -0
  122. opensandbox/py.typed +0 -0
  123. opensandbox/sandbox.py +616 -0
  124. opensandbox/services/__init__.py +34 -0
  125. opensandbox/services/command.py +75 -0
  126. opensandbox/services/filesystem.py +238 -0
  127. opensandbox/services/health.py +46 -0
  128. opensandbox/services/metrics.py +48 -0
  129. opensandbox/services/sandbox.py +175 -0
  130. opensandbox/sync/__init__.py +23 -0
  131. opensandbox/sync/adapters/__init__.py +34 -0
  132. opensandbox/sync/adapters/command_adapter.py +165 -0
  133. opensandbox/sync/adapters/converter/__init__.py +20 -0
  134. opensandbox/sync/adapters/converter/execution_event_dispatcher.py +107 -0
  135. opensandbox/sync/adapters/factory.py +53 -0
  136. opensandbox/sync/adapters/filesystem_adapter.py +311 -0
  137. opensandbox/sync/adapters/health_adapter.py +60 -0
  138. opensandbox/sync/adapters/metrics_adapter.py +71 -0
  139. opensandbox/sync/adapters/sandboxes_adapter.py +260 -0
  140. opensandbox/sync/manager.py +206 -0
  141. opensandbox/sync/sandbox.py +581 -0
  142. opensandbox/sync/services/__init__.py +32 -0
  143. opensandbox/sync/services/command.py +80 -0
  144. opensandbox/sync/services/filesystem.py +245 -0
  145. opensandbox/sync/services/health.py +47 -0
  146. opensandbox/sync/services/metrics.py +48 -0
  147. opensandbox/sync/services/sandbox.py +174 -0
  148. opensandbox-0.1.2.dev0.dist-info/METADATA +537 -0
  149. opensandbox-0.1.2.dev0.dist-info/RECORD +151 -0
  150. opensandbox-0.1.2.dev0.dist-info/WHEEL +4 -0
  151. opensandbox-0.1.2.dev0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,117 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """
17
+ OpenSandbox Python SDK
18
+
19
+ Secure, isolated execution environments for code and applications.
20
+
21
+ ## Basic Usage
22
+
23
+ ```python
24
+ import asyncio
25
+ from opensandbox import Sandbox
26
+ from opensandbox.models.execd import RunCommandOpts
27
+ from opensandbox.models.sandboxes import SandboxImageSpec
28
+
29
+ async def main():
30
+ # Create a sandbox instance.
31
+ #
32
+ # Note on lifecycle:
33
+ # - Exiting the context manager will call `sandbox.close()` (local HTTP resources only).
34
+ # - You must still call `sandbox.kill()` to terminate the remote sandbox instance.
35
+ async with await Sandbox.create("python:3.11") as sandbox:
36
+ # Write a file
37
+ await sandbox.files.write_file("hello.py", "print('Hello World')")
38
+
39
+ # Execute a command
40
+ result = await sandbox.commands.run("python hello.py")
41
+ print(result.logs.stdout[0].text) # Hello World
42
+
43
+ if __name__ == "__main__":
44
+ asyncio.run(main())
45
+ ```
46
+
47
+ ## Advanced Usage
48
+
49
+ ```python
50
+ from datetime import timedelta
51
+ from opensandbox import Sandbox
52
+ from opensandbox.config import ConnectionConfig
53
+ from opensandbox.models.execd import RunCommandOpts
54
+ from opensandbox.models.sandboxes import SandboxImageSpec, SandboxImageAuth
55
+
56
+ async def main():
57
+ config = ConnectionConfig(
58
+ api_key="your-api-key",
59
+ domain="api.opensandbox.io"
60
+ )
61
+
62
+ # With private registry auth
63
+ image_spec = SandboxImageSpec(
64
+ "my-registry.com/python:3.11",
65
+ auth=SandboxImageAuth(username="user", password="secret")
66
+ )
67
+
68
+ sandbox = await Sandbox.create(
69
+ image_spec,
70
+ timeout=timedelta(minutes=30),
71
+ env={"PYTHONPATH": "/workspace"},
72
+ connection_config=config,
73
+ )
74
+
75
+ try:
76
+ # File operations
77
+ await sandbox.files.write_file("script.py", "print('Hello OpenSandbox!')")
78
+
79
+ # Command execution
80
+ result = await sandbox.commands.run("python script.py")
81
+ print(result.logs.stdout[0].text)
82
+
83
+ # Get metrics
84
+ metrics = await sandbox.get_metrics()
85
+ print(f"Memory usage: {metrics.memory_used_in_mib}MB")
86
+
87
+ finally:
88
+ await sandbox.kill()
89
+ await sandbox.close()
90
+
91
+ if __name__ == "__main__":
92
+ asyncio.run(main())
93
+ ```
94
+
95
+ For advanced code execution with persistent contexts, see the separate
96
+ `opensandbox-code-interpreter` package.
97
+ """
98
+
99
+ from importlib.metadata import PackageNotFoundError
100
+ from importlib.metadata import version as _pkg_version
101
+
102
+ from opensandbox.manager import SandboxManager
103
+ from opensandbox.sandbox import Sandbox
104
+ from opensandbox.sync import SandboxManagerSync, SandboxSync
105
+
106
+ try:
107
+ __version__ = _pkg_version("opensandbox")
108
+ except PackageNotFoundError: # pragma: no cover
109
+ # Fallback for editable/uninstalled source checkouts.
110
+ __version__ = "0.0.0"
111
+
112
+ __all__ = [
113
+ "Sandbox",
114
+ "SandboxManager",
115
+ "SandboxSync",
116
+ "SandboxManagerSync",
117
+ ]
@@ -0,0 +1,36 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """
17
+ Adapter layer for OpenSandbox SDK.
18
+
19
+ Implements the service protocols using HTTP API calls.
20
+ """
21
+
22
+ from opensandbox.adapters.command_adapter import CommandsAdapter
23
+ from opensandbox.adapters.factory import AdapterFactory
24
+ from opensandbox.adapters.filesystem_adapter import FilesystemAdapter
25
+ from opensandbox.adapters.health_adapter import HealthAdapter
26
+ from opensandbox.adapters.metrics_adapter import MetricsAdapter
27
+ from opensandbox.adapters.sandboxes_adapter import SandboxesAdapter
28
+
29
+ __all__ = [
30
+ "AdapterFactory",
31
+ "SandboxesAdapter",
32
+ "FilesystemAdapter",
33
+ "CommandsAdapter",
34
+ "HealthAdapter",
35
+ "MetricsAdapter",
36
+ ]
@@ -0,0 +1,224 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """
17
+ Command service adapter implementation.
18
+
19
+ Implementation of Commands that adapts openapi-python-client generated CommandApi.
20
+ This adapter handles command execution within sandboxes, providing both
21
+ synchronous and streaming execution modes with proper session management.
22
+ """
23
+
24
+ import json
25
+ import logging
26
+
27
+ import httpx
28
+
29
+ from opensandbox.adapters.converter.event_node import EventNode
30
+ from opensandbox.adapters.converter.exception_converter import (
31
+ ExceptionConverter,
32
+ )
33
+ from opensandbox.adapters.converter.execution_converter import (
34
+ ExecutionConverter,
35
+ )
36
+ from opensandbox.adapters.converter.execution_event_dispatcher import (
37
+ ExecutionEventDispatcher,
38
+ )
39
+ from opensandbox.adapters.converter.response_handler import handle_api_error
40
+ from opensandbox.config import ConnectionConfig
41
+ from opensandbox.exceptions import InvalidArgumentException, SandboxApiException
42
+ from opensandbox.models.execd import Execution, ExecutionHandlers, RunCommandOpts
43
+ from opensandbox.models.sandboxes import SandboxEndpoint
44
+ from opensandbox.services.command import Commands
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+
49
+ class CommandsAdapter(Commands):
50
+ """
51
+ Implementation of Commands that adapts openapi-python-client generated CommandApi.
52
+
53
+ This adapter handles command execution within sandboxes, providing both
54
+ synchronous and streaming execution modes with proper session management.
55
+
56
+ The adapter uses direct httpx streaming for command execution to handle
57
+ Server-Sent Events (SSE) properly, while using the generated API client
58
+ for simpler operations like interrupt.
59
+ """
60
+
61
+ RUN_COMMAND_PATH = "/command"
62
+ INTERRUPT_COMMAND_PATH = "/command/{execution_id}/interrupt"
63
+
64
+ def __init__(
65
+ self,
66
+ connection_config: ConnectionConfig,
67
+ execd_endpoint: SandboxEndpoint,
68
+ ) -> None:
69
+ """
70
+ Initialize the command service adapter.
71
+
72
+ Args:
73
+ connection_config: Connection configuration (shared transport, headers, timeouts)
74
+ execd_endpoint: Endpoint for execd service
75
+ """
76
+ self.connection_config = connection_config
77
+ self.execd_endpoint = execd_endpoint
78
+ from opensandbox.api.execd import Client
79
+
80
+ protocol = self.connection_config.protocol
81
+ base_url = f"{protocol}://{self.execd_endpoint.endpoint}"
82
+ timeout_seconds = self.connection_config.request_timeout.total_seconds()
83
+ timeout = httpx.Timeout(timeout_seconds)
84
+
85
+ headers = {
86
+ "User-Agent": self.connection_config.user_agent,
87
+ **self.connection_config.headers,
88
+ }
89
+
90
+ # Execd API does not require authentication
91
+ self._client = Client(
92
+ base_url=base_url,
93
+ timeout=timeout,
94
+ )
95
+
96
+ # Inject httpx client (adapter-owned)
97
+ self._httpx_client = httpx.AsyncClient(
98
+ base_url=base_url,
99
+ headers=headers,
100
+ timeout=timeout,
101
+ transport=self.connection_config.transport,
102
+ )
103
+ self._client.set_async_httpx_client(self._httpx_client)
104
+
105
+ # SSE client (read timeout disabled)
106
+ sse_headers = {
107
+ **headers,
108
+ "Accept": "text/event-stream",
109
+ "Cache-Control": "no-cache",
110
+ }
111
+ self._sse_client = httpx.AsyncClient(
112
+ headers=sse_headers,
113
+ timeout=httpx.Timeout(
114
+ connect=timeout_seconds,
115
+ read=None,
116
+ write=timeout_seconds,
117
+ pool=None,
118
+ ),
119
+ transport=self.connection_config.transport,
120
+ )
121
+
122
+ async def _get_client(self):
123
+ """Return the client for execd API (no auth required)."""
124
+ return self._client
125
+
126
+ def _get_execd_url(self, path: str) -> str:
127
+ """Build URL for execd endpoint."""
128
+ protocol = self.connection_config.protocol
129
+ return f"{protocol}://{self.execd_endpoint.endpoint}{path}"
130
+
131
+ async def _get_sse_client(self) -> httpx.AsyncClient:
132
+ """Return SSE client (read timeout disabled) for execd streaming."""
133
+ return self._sse_client
134
+
135
+ async def run(
136
+ self,
137
+ command: str,
138
+ *,
139
+ opts: RunCommandOpts | None = None,
140
+ handlers: ExecutionHandlers | None = None,
141
+ ) -> Execution:
142
+ """Execute a shell command within the sandbox.
143
+
144
+ This method uses direct httpx streaming to handle SSE responses
145
+ from the execd service.
146
+ """
147
+ if not command.strip():
148
+ raise InvalidArgumentException("Command cannot be empty")
149
+
150
+ try:
151
+ # Convert domain model to API model
152
+ opts = opts or RunCommandOpts()
153
+ json_body = ExecutionConverter.to_api_run_command_json(command, opts)
154
+
155
+ # Prepare URL
156
+ url = self._get_execd_url(self.RUN_COMMAND_PATH)
157
+
158
+ execution = Execution(
159
+ id=None,
160
+ execution_count=None,
161
+ result=[],
162
+ error=None,
163
+ )
164
+
165
+ # Use SSE client for streaming responses (read timeout disabled)
166
+ client = await self._get_sse_client()
167
+
168
+ # Use streaming request for SSE
169
+ async with client.stream("POST", url, json=json_body) as response:
170
+ if response.status_code != 200:
171
+ await response.aread()
172
+ error_body = response.text
173
+ logger.error(
174
+ f"Failed to run command. Status: {response.status_code}, Body: {error_body}"
175
+ )
176
+ raise SandboxApiException(
177
+ message=f"Failed to run command. Status code: {response.status_code}",
178
+ status_code=response.status_code,
179
+ )
180
+
181
+ dispatcher = ExecutionEventDispatcher(execution, handlers)
182
+
183
+ async for line in response.aiter_lines():
184
+ if not line.strip():
185
+ continue
186
+
187
+ # Handle potential SSE format "data: ..."
188
+ data = line
189
+ if data.startswith("data:"):
190
+ data = data[5:].strip()
191
+
192
+ try:
193
+ event_dict = json.loads(data)
194
+ event_node = EventNode(**event_dict)
195
+ await dispatcher.dispatch(event_node)
196
+ except Exception as e:
197
+ logger.error(f"Failed to parse SSE line: {line}", exc_info=e)
198
+
199
+ return execution
200
+
201
+ except Exception as e:
202
+ logger.error(
203
+ "Failed to run command (length: %s)",
204
+ len(command),
205
+ exc_info=e,
206
+ )
207
+ raise ExceptionConverter.to_sandbox_exception(e) from e
208
+
209
+ async def interrupt(self, execution_id: str) -> None:
210
+ """Interrupt a running command execution."""
211
+ try:
212
+ from opensandbox.api.execd.api.command import interrupt_command
213
+
214
+ client = await self._get_client()
215
+ response_obj = await interrupt_command.asyncio_detailed(
216
+ client=client,
217
+ id=execution_id,
218
+ )
219
+
220
+ handle_api_error(response_obj, "Interrupt command")
221
+
222
+ except Exception as e:
223
+ logger.error("Failed to interrupt command", exc_info=e)
224
+ raise ExceptionConverter.to_sandbox_exception(e) from e
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """
17
+ Model converter utilities for API/domain model mapping.
18
+
19
+ This package provides:
20
+ - ExceptionConverter: Convert various exceptions to SandboxException
21
+ - ResponseHandler: Unified API response handling
22
+ - SandboxModelConverter: Convert between API and domain models
23
+ - FilesystemModelConverter: Convert filesystem-related models
24
+ - MetricsModelConverter: Convert metrics-related models
25
+ - ExecutionConverter: Convert execution-related models
26
+ """
27
+
28
+ from opensandbox.adapters.converter.exception_converter import (
29
+ ExceptionConverter,
30
+ parse_sandbox_error,
31
+ )
32
+ from opensandbox.adapters.converter.filesystem_model_converter import (
33
+ FilesystemModelConverter,
34
+ )
35
+ from opensandbox.adapters.converter.metrics_model_converter import (
36
+ MetricsModelConverter,
37
+ )
38
+ from opensandbox.adapters.converter.response_handler import (
39
+ handle_api_error,
40
+ )
41
+ from opensandbox.adapters.converter.sandbox_model_converter import (
42
+ SandboxModelConverter,
43
+ )
44
+
45
+ __all__ = [
46
+ "ExceptionConverter",
47
+ "parse_sandbox_error",
48
+ "FilesystemModelConverter",
49
+ "MetricsModelConverter",
50
+ "SandboxModelConverter",
51
+ "handle_api_error",
52
+ ]
@@ -0,0 +1,55 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """
17
+ EventNode model for parsing Server-Sent Events from execd.
18
+ """
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field
21
+
22
+
23
+ class EventNodeError(BaseModel):
24
+ """Error details in an event node."""
25
+
26
+ name: str | None = Field(default=None, alias="ename")
27
+ value: str | None = Field(default=None, alias="evalue")
28
+ traceback: list[str] = Field(default_factory=list)
29
+
30
+
31
+ class EventNodeResults(BaseModel):
32
+ """Results container in an event node."""
33
+
34
+ text: str | None = Field(default=None, alias="text")
35
+
36
+ def get_text(self) -> str:
37
+ """Get the text representation of the result."""
38
+ return self.text or ""
39
+
40
+ model_config = ConfigDict(extra="allow") # Allow other mime types
41
+
42
+
43
+ class EventNode(BaseModel):
44
+ """
45
+ Represents a single event from the server stream.
46
+ Corresponds to ServerStreamEvent in OpenAPI spec.
47
+ """
48
+
49
+ type: str
50
+ text: str | None = None
51
+ execution_count: int | None = Field(default=None, alias="execution_count")
52
+ execution_time_in_millis: int | None = Field(default=None, alias="execution_time")
53
+ timestamp: int
54
+ results: EventNodeResults | None = None
55
+ error: EventNodeError | None = None