hud-python 0.3.5__py3-none-any.whl → 0.4.0__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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__init__.py +22 -89
- hud/agents/__init__.py +17 -0
- hud/agents/art.py +101 -0
- hud/agents/base.py +599 -0
- hud/{mcp → agents}/claude.py +373 -321
- hud/{mcp → agents}/langchain.py +250 -250
- hud/agents/misc/__init__.py +7 -0
- hud/{agent → agents}/misc/response_agent.py +80 -80
- hud/{mcp → agents}/openai.py +352 -334
- hud/agents/openai_chat_generic.py +154 -0
- hud/{mcp → agents}/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -0
- hud/agents/tests/test_claude.py +324 -0
- hud/{mcp → agents}/tests/test_client.py +363 -324
- hud/{mcp → agents}/tests/test_openai.py +237 -238
- hud/cli/__init__.py +617 -0
- hud/cli/__main__.py +8 -0
- hud/cli/analyze.py +371 -0
- hud/cli/analyze_metadata.py +230 -0
- hud/cli/build.py +427 -0
- hud/cli/clone.py +185 -0
- hud/cli/cursor.py +92 -0
- hud/cli/debug.py +392 -0
- hud/cli/docker_utils.py +83 -0
- hud/cli/init.py +281 -0
- hud/cli/interactive.py +353 -0
- hud/cli/mcp_server.py +756 -0
- hud/cli/pull.py +336 -0
- hud/cli/push.py +379 -0
- hud/cli/remote_runner.py +311 -0
- hud/cli/runner.py +160 -0
- hud/cli/tests/__init__.py +3 -0
- hud/cli/tests/test_analyze.py +284 -0
- hud/cli/tests/test_cli_init.py +265 -0
- hud/cli/tests/test_cli_main.py +27 -0
- hud/cli/tests/test_clone.py +142 -0
- hud/cli/tests/test_cursor.py +253 -0
- hud/cli/tests/test_debug.py +453 -0
- hud/cli/tests/test_mcp_server.py +139 -0
- hud/cli/tests/test_utils.py +388 -0
- hud/cli/utils.py +263 -0
- hud/clients/README.md +143 -0
- hud/clients/__init__.py +16 -0
- hud/clients/base.py +354 -0
- hud/clients/fastmcp.py +202 -0
- hud/clients/mcp_use.py +278 -0
- hud/clients/tests/__init__.py +1 -0
- hud/clients/tests/test_client_integration.py +111 -0
- hud/clients/tests/test_fastmcp.py +342 -0
- hud/clients/tests/test_protocol.py +188 -0
- hud/clients/utils/__init__.py +1 -0
- hud/clients/utils/retry_transport.py +160 -0
- hud/datasets.py +322 -192
- hud/misc/__init__.py +1 -0
- hud/{agent → misc}/claude_plays_pokemon.py +292 -283
- hud/otel/__init__.py +35 -0
- hud/otel/collector.py +142 -0
- hud/otel/config.py +164 -0
- hud/otel/context.py +536 -0
- hud/otel/exporters.py +366 -0
- hud/otel/instrumentation.py +97 -0
- hud/otel/processors.py +118 -0
- hud/otel/tests/__init__.py +1 -0
- hud/otel/tests/test_processors.py +197 -0
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -0
- hud/server/helper/__init__.py +5 -0
- hud/server/low_level.py +132 -0
- hud/server/server.py +166 -0
- hud/server/tests/__init__.py +3 -0
- hud/settings.py +73 -79
- hud/shared/__init__.py +5 -0
- hud/{exceptions.py → shared/exceptions.py} +180 -180
- hud/{server → shared}/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -0
- hud/{server → shared}/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -30
- hud/telemetry/instrument.py +379 -0
- hud/telemetry/job.py +309 -141
- hud/telemetry/replay.py +74 -0
- hud/telemetry/trace.py +83 -0
- hud/tools/__init__.py +33 -34
- hud/tools/base.py +365 -65
- hud/tools/bash.py +161 -137
- hud/tools/computer/__init__.py +15 -13
- hud/tools/computer/anthropic.py +437 -420
- hud/tools/computer/hud.py +376 -334
- hud/tools/computer/openai.py +295 -292
- hud/tools/computer/settings.py +82 -0
- hud/tools/edit.py +314 -290
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -532
- hud/tools/executors/pyautogui.py +621 -619
- hud/tools/executors/tests/__init__.py +1 -1
- hud/tools/executors/tests/test_base_executor.py +338 -338
- hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
- hud/tools/executors/xdo.py +511 -503
- hud/tools/{playwright_tool.py → playwright.py} +412 -379
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -0
- hud/tools/tests/test_bash.py +158 -152
- hud/tools/tests/test_bash_extended.py +197 -0
- hud/tools/tests/test_computer.py +425 -52
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -240
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -157
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -0
- hud/tools/utils.py +50 -50
- hud/types.py +136 -89
- hud/utils/__init__.py +10 -16
- hud/utils/async_utils.py +65 -0
- hud/utils/design.py +168 -0
- hud/utils/mcp.py +55 -0
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -0
- hud/utils/tests/test_init.py +17 -21
- hud/utils/tests/test_progress.py +261 -225
- hud/utils/tests/test_telemetry.py +82 -37
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- hud_python-0.4.0.dist-info/METADATA +474 -0
- hud_python-0.4.0.dist-info/RECORD +132 -0
- hud_python-0.4.0.dist-info/entry_points.txt +3 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.0.dist-info}/licenses/LICENSE +21 -21
- hud/adapters/__init__.py +0 -8
- hud/adapters/claude/__init__.py +0 -5
- hud/adapters/claude/adapter.py +0 -180
- hud/adapters/claude/tests/__init__.py +0 -1
- hud/adapters/claude/tests/test_adapter.py +0 -519
- hud/adapters/common/__init__.py +0 -6
- hud/adapters/common/adapter.py +0 -178
- hud/adapters/common/tests/test_adapter.py +0 -289
- hud/adapters/common/types.py +0 -446
- hud/adapters/operator/__init__.py +0 -5
- hud/adapters/operator/adapter.py +0 -108
- hud/adapters/operator/tests/__init__.py +0 -1
- hud/adapters/operator/tests/test_adapter.py +0 -370
- hud/agent/__init__.py +0 -19
- hud/agent/base.py +0 -126
- hud/agent/claude.py +0 -271
- hud/agent/langchain.py +0 -215
- hud/agent/misc/__init__.py +0 -3
- hud/agent/operator.py +0 -268
- hud/agent/tests/__init__.py +0 -1
- hud/agent/tests/test_base.py +0 -202
- hud/env/__init__.py +0 -11
- hud/env/client.py +0 -35
- hud/env/docker_client.py +0 -349
- hud/env/environment.py +0 -446
- hud/env/local_docker_client.py +0 -358
- hud/env/remote_client.py +0 -212
- hud/env/remote_docker_client.py +0 -292
- hud/gym.py +0 -130
- hud/job.py +0 -773
- hud/mcp/__init__.py +0 -17
- hud/mcp/base.py +0 -631
- hud/mcp/client.py +0 -312
- hud/mcp/tests/test_base.py +0 -512
- hud/mcp/tests/test_claude.py +0 -294
- hud/task.py +0 -149
- hud/taskset.py +0 -237
- hud/telemetry/_trace.py +0 -347
- hud/telemetry/context.py +0 -230
- hud/telemetry/exporter.py +0 -575
- hud/telemetry/instrumentation/__init__.py +0 -3
- hud/telemetry/instrumentation/mcp.py +0 -259
- hud/telemetry/instrumentation/registry.py +0 -59
- hud/telemetry/mcp_models.py +0 -270
- hud/telemetry/tests/__init__.py +0 -1
- hud/telemetry/tests/test_context.py +0 -210
- hud/telemetry/tests/test_trace.py +0 -312
- hud/tools/helper/README.md +0 -56
- hud/tools/helper/__init__.py +0 -9
- hud/tools/helper/mcp_server.py +0 -78
- hud/tools/helper/server_initialization.py +0 -115
- hud/tools/helper/utils.py +0 -58
- hud/trajectory.py +0 -94
- hud/utils/agent.py +0 -37
- hud/utils/common.py +0 -256
- hud/utils/config.py +0 -120
- hud/utils/deprecation.py +0 -115
- hud/utils/misc.py +0 -53
- hud/utils/tests/test_common.py +0 -277
- hud/utils/tests/test_config.py +0 -129
- hud_python-0.3.5.dist-info/METADATA +0 -284
- hud_python-0.3.5.dist-info/RECORD +0 -120
- /hud/{adapters/common → shared}/tests/__init__.py +0 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.0.dist-info}/WHEEL +0 -0
hud/env/docker_client.py
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import abc
|
|
4
|
-
import json
|
|
5
|
-
import logging
|
|
6
|
-
import os
|
|
7
|
-
import uuid
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import TYPE_CHECKING, Any
|
|
10
|
-
|
|
11
|
-
from hud.env.client import Client
|
|
12
|
-
from hud.types import EnvironmentStatus
|
|
13
|
-
from hud.utils.common import _compile_pathspec, directory_to_tar_bytes
|
|
14
|
-
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from hud.utils import ExecuteResult
|
|
17
|
-
from hud.utils.config import FunctionConfig
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger("hud.env.docker_client")
|
|
20
|
-
|
|
21
|
-
STATUS_MESSAGES = {
|
|
22
|
-
EnvironmentStatus.RUNNING.value: "is running",
|
|
23
|
-
EnvironmentStatus.ERROR.value: "had an error initializing",
|
|
24
|
-
EnvironmentStatus.COMPLETED.value: "completed",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
PACKAGE_NAME = "hud_controller"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class InvokeError(Exception):
|
|
31
|
-
"""
|
|
32
|
-
Error raised when an invoke fails.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def invoke_template(config: FunctionConfig, package_name: str, divider: str) -> str:
|
|
37
|
-
"""
|
|
38
|
-
Return a python script to run the given config.
|
|
39
|
-
"""
|
|
40
|
-
func_parts = config.function.split(".")
|
|
41
|
-
module_str = ".".join([package_name] + func_parts[:-1])
|
|
42
|
-
func_str = func_parts[-1]
|
|
43
|
-
|
|
44
|
-
# the reason we call `json.dumps` twice is to escape the json string
|
|
45
|
-
return f"""import json
|
|
46
|
-
from {module_str} import {func_str}
|
|
47
|
-
args = json.loads({json.dumps(json.dumps(config.args))})
|
|
48
|
-
result = {func_str}(*args)
|
|
49
|
-
result_str = json.dumps(result)
|
|
50
|
-
print("{divider}")
|
|
51
|
-
print(result_str)
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class DockerClient(Client):
|
|
56
|
-
"""
|
|
57
|
-
Base class for environment clients.
|
|
58
|
-
|
|
59
|
-
Handles updating the environment when local files change.
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
_last_pyproject_toml_str: str | None = None
|
|
63
|
-
_last_update_time: int = 0
|
|
64
|
-
_last_file_mtimes: dict[str, float] = {} # noqa: RUF012 - Not recognized as Pydantic model
|
|
65
|
-
_source_path: Path | None = None
|
|
66
|
-
|
|
67
|
-
@property
|
|
68
|
-
def source_path(self) -> Path | None:
|
|
69
|
-
"""Get the source path."""
|
|
70
|
-
return self._source_path
|
|
71
|
-
|
|
72
|
-
def set_source_path(self, source_path: Path) -> None:
|
|
73
|
-
"""
|
|
74
|
-
Set the source path for this environment controller.
|
|
75
|
-
Can only be set once, and cannot be set if source_path is already set.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
source_path: Path to the source code to use in the environment
|
|
79
|
-
|
|
80
|
-
Raises:
|
|
81
|
-
ValueError: If source_path has already been set
|
|
82
|
-
"""
|
|
83
|
-
if self._source_path:
|
|
84
|
-
raise ValueError("Source path has already been set")
|
|
85
|
-
|
|
86
|
-
# Validate source path
|
|
87
|
-
if not source_path.exists():
|
|
88
|
-
raise FileNotFoundError(f"Source path {source_path} does not exist")
|
|
89
|
-
if not source_path.is_dir():
|
|
90
|
-
raise NotADirectoryError(f"Source path {source_path} is not a directory")
|
|
91
|
-
|
|
92
|
-
# Parse pyproject.toml to get package name
|
|
93
|
-
pyproject_path = source_path / "pyproject.toml"
|
|
94
|
-
if not pyproject_path.exists():
|
|
95
|
-
raise FileNotFoundError(f"pyproject.toml not found in {source_path}")
|
|
96
|
-
|
|
97
|
-
# validate package name
|
|
98
|
-
try:
|
|
99
|
-
import toml
|
|
100
|
-
except ImportError:
|
|
101
|
-
raise ImportError(
|
|
102
|
-
"toml is required for parsing pyproject.toml files. "
|
|
103
|
-
"Please install it with 'pip install toml'"
|
|
104
|
-
) from None
|
|
105
|
-
pyproject_data = toml.load(pyproject_path)
|
|
106
|
-
package_name = pyproject_data.get("project", {}).get("name")
|
|
107
|
-
if not package_name:
|
|
108
|
-
raise ValueError("Could not find package name in pyproject.toml")
|
|
109
|
-
if package_name != PACKAGE_NAME:
|
|
110
|
-
raise ValueError(f"Package name in pyproject.toml must be {PACKAGE_NAME}")
|
|
111
|
-
|
|
112
|
-
self._source_path = source_path
|
|
113
|
-
|
|
114
|
-
# set current mtimes
|
|
115
|
-
self._last_file_mtimes = self._get_all_file_mtimes()
|
|
116
|
-
|
|
117
|
-
@classmethod
|
|
118
|
-
@abc.abstractmethod
|
|
119
|
-
async def build_image(cls, build_context: Path) -> tuple[str, dict[str, Any]]:
|
|
120
|
-
"""
|
|
121
|
-
Build an image from a build context.
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
tuple[str, dict[str, Any]]: The image tag and build output
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
@classmethod
|
|
128
|
-
@abc.abstractmethod
|
|
129
|
-
async def create(cls, image: str) -> DockerClient:
|
|
130
|
-
"""
|
|
131
|
-
Creates an environment client from an image.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
image: The image to build the environment from
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
EnvClient: An instance of the environment client
|
|
138
|
-
"""
|
|
139
|
-
|
|
140
|
-
@abc.abstractmethod
|
|
141
|
-
async def get_status(self) -> EnvironmentStatus:
|
|
142
|
-
"""
|
|
143
|
-
Get the current status of the environment.
|
|
144
|
-
|
|
145
|
-
Returns:
|
|
146
|
-
EnvironmentStatus: A status enum indicating the current state of the environment
|
|
147
|
-
"""
|
|
148
|
-
|
|
149
|
-
def _get_all_file_mtimes(self) -> dict[str, float]:
|
|
150
|
-
"""
|
|
151
|
-
Get modification times for all files in the source path.
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
Dict[str, float]: Dictionary mapping file paths to modification times
|
|
155
|
-
"""
|
|
156
|
-
if not self._source_path:
|
|
157
|
-
return {}
|
|
158
|
-
|
|
159
|
-
# Build ignore spec (currently we only care about .hudignore but reuse
|
|
160
|
-
# the common helper for consistency).
|
|
161
|
-
spec = _compile_pathspec(
|
|
162
|
-
self._source_path,
|
|
163
|
-
respect_gitignore=False,
|
|
164
|
-
respect_dockerignore=False,
|
|
165
|
-
respect_hudignore=True,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
file_mtimes: dict[str, float] = {}
|
|
169
|
-
|
|
170
|
-
for root, _, files in os.walk(self._source_path):
|
|
171
|
-
for file in files:
|
|
172
|
-
file_path = Path(root) / file
|
|
173
|
-
rel_path = file_path.relative_to(self._source_path).as_posix()
|
|
174
|
-
|
|
175
|
-
# Skip ignored files
|
|
176
|
-
if spec and spec.match_file(rel_path):
|
|
177
|
-
continue
|
|
178
|
-
|
|
179
|
-
try:
|
|
180
|
-
file_mtimes[str(file_path)] = file_path.stat().st_mtime
|
|
181
|
-
except (FileNotFoundError, PermissionError):
|
|
182
|
-
# Skip files that can't be accessed
|
|
183
|
-
continue
|
|
184
|
-
|
|
185
|
-
return file_mtimes
|
|
186
|
-
|
|
187
|
-
async def needs_update(self) -> bool:
|
|
188
|
-
"""
|
|
189
|
-
Check if the environment needs an update by:
|
|
190
|
-
1. Checking if any file has been modified since the last update
|
|
191
|
-
|
|
192
|
-
Returns:
|
|
193
|
-
bool: True if the environment needs an update, False otherwise.
|
|
194
|
-
"""
|
|
195
|
-
# If no source path, no update needed
|
|
196
|
-
if not self.source_path:
|
|
197
|
-
return False
|
|
198
|
-
|
|
199
|
-
# Check if any file has been modified since the last update
|
|
200
|
-
current_mtimes = self._get_all_file_mtimes()
|
|
201
|
-
|
|
202
|
-
# If we don't have previous modification times, we need an update
|
|
203
|
-
if not self._last_file_mtimes:
|
|
204
|
-
return True
|
|
205
|
-
|
|
206
|
-
# Check for removed files
|
|
207
|
-
for file_path in self._last_file_mtimes:
|
|
208
|
-
if file_path not in current_mtimes:
|
|
209
|
-
return True
|
|
210
|
-
|
|
211
|
-
# Check for new or modified files
|
|
212
|
-
for file_path, mtime in current_mtimes.items():
|
|
213
|
-
if file_path not in self._last_file_mtimes or mtime > self._last_file_mtimes[file_path]:
|
|
214
|
-
return True
|
|
215
|
-
|
|
216
|
-
return False
|
|
217
|
-
|
|
218
|
-
async def update(self) -> None:
|
|
219
|
-
"""
|
|
220
|
-
Base update method for environment controllers.
|
|
221
|
-
For controllers with no source path, this is a no-op.
|
|
222
|
-
"""
|
|
223
|
-
# If no source path, nothing to update
|
|
224
|
-
if not self._source_path:
|
|
225
|
-
return
|
|
226
|
-
|
|
227
|
-
logger.info("Updating environment")
|
|
228
|
-
|
|
229
|
-
# Save current file modification times
|
|
230
|
-
self._last_file_mtimes = self._get_all_file_mtimes()
|
|
231
|
-
|
|
232
|
-
# Create tar archive of the source code and send it to the container
|
|
233
|
-
tar_bytes = directory_to_tar_bytes(self._source_path)
|
|
234
|
-
await self.execute(["mkdir", "-p", "/controller"], timeout=5)
|
|
235
|
-
await self.put_archive("/controller", tar_bytes)
|
|
236
|
-
|
|
237
|
-
# Check if pyproject.toml exists and parse it
|
|
238
|
-
pyproject_path = self._source_path / "pyproject.toml"
|
|
239
|
-
if not pyproject_path.exists():
|
|
240
|
-
raise FileNotFoundError(f"pyproject.toml not found in {self._source_path}")
|
|
241
|
-
|
|
242
|
-
# Read and parse the current content of pyproject.toml
|
|
243
|
-
current_pyproject_content = pyproject_path.read_text()
|
|
244
|
-
if (
|
|
245
|
-
self._last_pyproject_toml_str is None
|
|
246
|
-
or self._last_pyproject_toml_str != current_pyproject_content
|
|
247
|
-
):
|
|
248
|
-
# Update package name if pyproject.toml changed
|
|
249
|
-
try:
|
|
250
|
-
import toml
|
|
251
|
-
except ImportError:
|
|
252
|
-
raise ImportError(
|
|
253
|
-
"toml is required for parsing pyproject.toml files. "
|
|
254
|
-
"Please install it with 'pip install toml'"
|
|
255
|
-
) from None
|
|
256
|
-
pyproject_data = toml.loads(current_pyproject_content)
|
|
257
|
-
self._package_name = pyproject_data.get("project", {}).get("name")
|
|
258
|
-
if not self._package_name:
|
|
259
|
-
raise ValueError("Could not find package name in pyproject.toml")
|
|
260
|
-
logger.info("Installing %s in /controller", self._package_name)
|
|
261
|
-
result = await self.execute(
|
|
262
|
-
["bash", "-c", "cd /controller && pip install -e . --break-system-packages"],
|
|
263
|
-
timeout=60,
|
|
264
|
-
)
|
|
265
|
-
if result["stdout"]:
|
|
266
|
-
logger.info("STDOUT:\n%s", result["stdout"])
|
|
267
|
-
if result["stderr"]:
|
|
268
|
-
logger.warning("STDERR:\n%s", result["stderr"])
|
|
269
|
-
# Save current pyproject.toml content
|
|
270
|
-
self._last_pyproject_toml_str = current_pyproject_content
|
|
271
|
-
|
|
272
|
-
@abc.abstractmethod
|
|
273
|
-
async def execute(
|
|
274
|
-
self,
|
|
275
|
-
command: list[str],
|
|
276
|
-
*,
|
|
277
|
-
timeout: int | None = None, # noqa: ASYNC109
|
|
278
|
-
) -> ExecuteResult:
|
|
279
|
-
"""
|
|
280
|
-
Execute a command in the environment. May not be supported by all environments.
|
|
281
|
-
|
|
282
|
-
Args:
|
|
283
|
-
command: The command to execute
|
|
284
|
-
workdir: The working directory to execute the command in
|
|
285
|
-
timeout: The timeout for the command
|
|
286
|
-
|
|
287
|
-
Returns:
|
|
288
|
-
ExecuteResult: The result of the command
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
async def invoke(self, config: FunctionConfig) -> tuple[Any, bytes, bytes]:
|
|
292
|
-
"""
|
|
293
|
-
Invoke a function in the environment. Supported by all environments.
|
|
294
|
-
|
|
295
|
-
Args:
|
|
296
|
-
config: The configuration to invoke
|
|
297
|
-
|
|
298
|
-
Returns:
|
|
299
|
-
tuple[Any, bytes, bytes]: The result of the invocation, stdout, and stderr
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
if await self.needs_update():
|
|
303
|
-
logger.info("Environment needs update, updating")
|
|
304
|
-
await self.update()
|
|
305
|
-
|
|
306
|
-
# generate a random uuid as a divider
|
|
307
|
-
divider = str(uuid.uuid4())
|
|
308
|
-
|
|
309
|
-
template = invoke_template(config, PACKAGE_NAME, divider)
|
|
310
|
-
logger.debug("Invoking template: %s", template)
|
|
311
|
-
|
|
312
|
-
result = await self.execute(["python3", "-c", template])
|
|
313
|
-
|
|
314
|
-
# parse the result
|
|
315
|
-
# we take the whole stderr as the stderr, and the stdout is the result pre-divider
|
|
316
|
-
stderr = result["stderr"]
|
|
317
|
-
stdout_parts = result["stdout"].split(divider.encode())
|
|
318
|
-
stdout = stdout_parts[0]
|
|
319
|
-
|
|
320
|
-
# parse the json part of the stdout (if it exists)
|
|
321
|
-
if len(stdout_parts) > 1:
|
|
322
|
-
result = json.loads(stdout_parts[1])
|
|
323
|
-
else:
|
|
324
|
-
logger.warning("Potential error: %s", stderr)
|
|
325
|
-
result = None
|
|
326
|
-
|
|
327
|
-
return result, stdout, stderr
|
|
328
|
-
|
|
329
|
-
@abc.abstractmethod
|
|
330
|
-
async def get_archive(self, path: str) -> bytes:
|
|
331
|
-
"""
|
|
332
|
-
Get an archive of a path from the environment.
|
|
333
|
-
May not be supported by all environments. (notably browser environments)
|
|
334
|
-
Args:
|
|
335
|
-
path: The path to get the archive of
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
bytes: The archive of the path
|
|
339
|
-
"""
|
|
340
|
-
|
|
341
|
-
@abc.abstractmethod
|
|
342
|
-
async def put_archive(self, path: str, data: bytes) -> bool:
|
|
343
|
-
"""
|
|
344
|
-
Put an archive of data at a path in the environment.
|
|
345
|
-
May not be supported by all environments. (notably browser environments)
|
|
346
|
-
Args:
|
|
347
|
-
path: The path to put the archive at
|
|
348
|
-
data: The data to put in the archive
|
|
349
|
-
"""
|