From ed21eadc793fa799c762aef6b049e06f6f5e1da5 Mon Sep 17 00:00:00 2001 From: Aditya Arakeri Date: Thu, 15 Oct 2026 23:00:30 -0700 Subject: [PATCH] fix(examples): exit cleanly when Docker is unavailable The experiment workflow example went straight to docker build, so a stopped Docker daemon surfaced as a SandboxError traceback. main() now checks for a running Linux-container daemon first and reports sandbox and docker CLI failures as a one-line error with exit code 1. Co-Authored-By: Claude Opus 5.5 --- examples/experiment_workflow.py | 16 +++++++++++++++- tests/test_experiment_example.py | 25 ++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/examples/experiment_workflow.py b/examples/experiment_workflow.py index b116a3b..2cca905 100644 --- a/examples/experiment_workflow.py +++ b/examples/experiment_workflow.py @@ -15,12 +15,14 @@ EvaluationSpec, Evaluator, ExperimentStore, + SandboxError, SandboxTrialExecutor, TrialCase, TrialRunner, build_default_images, compare_experiments, ) +from tbcore.testing import docker_available AGENT = """from pathlib import Path import json @@ -140,7 +142,19 @@ def main() -> None: "--image", help="already built sandbox image; default builds bundled images" ) args = parser.parse_args() - print(json.dumps(run(args.store, args.image), indent=2)) + if not docker_available(): + parser.exit( + 1, + "error: Docker is not available. Install Docker, start the daemon (Docker Desktop " + "on Windows/macOS) in Linux-container mode, and retry.\n", + ) + try: + result = run(args.store, args.image) + except SandboxError as exc: + parser.exit(1, f"error: {exc}\n") + except subprocess.CalledProcessError as exc: + parser.exit(1, f"error: {' '.join(exc.cmd)} failed:\n{exc.stderr}") + print(json.dumps(result, indent=2)) if __name__ == "__main__": diff --git a/tests/test_experiment_example.py b/tests/test_experiment_example.py index 9c95b93..72ea37a 100644 --- a/tests/test_experiment_example.py +++ b/tests/test_experiment_example.py @@ -1,9 +1,10 @@ import shutil from pathlib import Path +import pytest from examples import experiment_workflow -from tbcore import CapturePolicy, ExperimentStore +from tbcore import CapturePolicy, ExperimentStore, SandboxError def test_documented_example_and_disposable_backup_recovery(tmp_path: Path, monkeypatch): @@ -35,3 +36,25 @@ def __call__(self, case, workspace, attempt_id, context): encoding="utf-8" ) == "Write the target answer." recovered.close() + + +def test_main_exits_cleanly_when_docker_is_unavailable(tmp_path: Path, monkeypatch, capsys): + monkeypatch.setattr(experiment_workflow, "docker_available", lambda: False) + monkeypatch.setattr("sys.argv", ["experiment_workflow.py", "--store", str(tmp_path)]) + with pytest.raises(SystemExit) as exited: + experiment_workflow.main() + assert exited.value.code == 1 + assert "Docker is not available" in capsys.readouterr().err + + +def test_main_reports_sandbox_errors_without_traceback(tmp_path: Path, monkeypatch, capsys): + def fail(store_root, image): + raise SandboxError("docker build failed for tbcore/sandbox:latest") + + monkeypatch.setattr(experiment_workflow, "docker_available", lambda: True) + monkeypatch.setattr(experiment_workflow, "run", fail) + monkeypatch.setattr("sys.argv", ["experiment_workflow.py", "--store", str(tmp_path)]) + with pytest.raises(SystemExit) as exited: + experiment_workflow.main() + assert exited.value.code == 1 + assert "error: docker build failed" in capsys.readouterr().err