Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clients/python/src/ate_env/backend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@
from .substrate import SubstrateBackendDriver

__all__ = ["BackendDriver", "MockBackendDriver", "SubstrateBackendDriver"]

1 change: 1 addition & 0 deletions clients/python/src/ate_env/fleet.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ def _acquire_one(
backend=self.backend,
runtime=runtime,
data_plane=data_plane,
default_timeout_s=self.config.step_timeout_s,
)

def _build_runtime(
Expand Down
15 changes: 9 additions & 6 deletions clients/python/src/ate_env/handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ class SandboxHandle:
backend: BackendDriver = field(repr=False)
runtime: RuntimeGuestHook = field(repr=False)
data_plane: DataPlaneEndpoint | None = None
default_timeout_s: float = 120.0

def exec(
self,
command: str | list[str],
cwd: str = "",
env: dict[str, str] | None = None,
timeout_s: float = 120.0,
timeout_s: float | None = None,
check: bool = True,
) -> ExecResult:
"""Execute command inside the sandbox.
Expand All @@ -57,10 +58,11 @@ def exec(
of ``check``.
Returns ExecResult.
"""
res = self.runtime.exec(command, cwd=cwd, env=env, timeout_s=timeout_s)
to_s = self.default_timeout_s if timeout_s is None else timeout_s
res = self.runtime.exec(command, cwd=cwd, env=env, timeout_s=to_s)
if check and res.timed_out:
raise CommandTimeoutError(
f"Command '{command}' timed out after {timeout_s:g}s:\n"
f"Command '{command}' timed out after {to_s:g}s:\n"
f"STDOUT: {res.stdout}\nSTDERR: {res.stderr}",
result=res,
)
Expand All @@ -77,14 +79,15 @@ async def exec_async(
command: str | list[str],
cwd: str = "",
env: dict[str, str] | None = None,
timeout_s: float = 120.0,
timeout_s: float | None = None,
check: bool = True,
) -> ExecResult:
"""Asynchronously execute command inside the sandbox."""
res = await self.runtime.exec_async(command, cwd=cwd, env=env, timeout_s=timeout_s)
to_s = self.default_timeout_s if timeout_s is None else timeout_s
res = await self.runtime.exec_async(command, cwd=cwd, env=env, timeout_s=to_s)
if check and res.timed_out:
raise CommandTimeoutError(
f"Command '{command}' timed out after {timeout_s:g}s:\n"
f"Command '{command}' timed out after {to_s:g}s:\n"
f"STDOUT: {res.stdout}\nSTDERR: {res.stderr}",
result=res,
)
Expand Down
2 changes: 1 addition & 1 deletion clients/python/src/ate_env/loop_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async def _shutdown() -> None:
pass
self._loop.call_soon_threadsafe(self._loop.stop)
self._thread.join(timeout=2.0)
if not self._loop.is_closed():
if not self._thread.is_alive() and not self._loop.is_closed():
self._loop.close()

def __del__(self) -> None:
Expand Down
7 changes: 7 additions & 0 deletions clients/python/tests/test_exec_semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,10 @@ def test_mock_runtime_file_round_trip(name):
def test_read_missing_file_raises_file_not_found():
with pytest.raises(FileNotFoundError):
MockRuntimeHook().read_file_bytes("/missing.txt")


def test_fleet_step_timeout_applies_to_handle():
fleet = SandboxFleet(FleetConfig(backend="mock", step_timeout_s=42.0))
h = fleet.acquire("t1")
assert h.default_timeout_s == 42.0
h.release()
Loading