Mindcraft's picture
Publish frozen reproduction executor image source
17d5066 verified
Raw
History Blame Contribute Delete
3.76 kB
#!/usr/bin/env python3
"""Prepare, submit, reconcile, poll, or cancel one durable direct HF Job attempt."""
from __future__ import annotations
import argparse
import json
import sys
from datetime import UTC, datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from repro_control.hf_provider import HFJobsProvider
from repro_control.launcher import (
AmbiguousSubmission,
BudgetSnapshot,
DirectLauncher,
LaunchError,
LauncherState,
)
def _launcher(args) -> DirectLauncher:
provider = HFJobsProvider(namespace=args.namespace) if args.action != "prepare" else None
return DirectLauncher(
args.state,
provider=provider,
enable_submit=args.enable_submit,
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--state", type=Path, required=True)
parser.add_argument("--namespace", default="Mindcraft")
parser.add_argument(
"--enable-submit",
action="store_true",
help="Required for submit/cancel; omitted by default to keep the CLI non-mutating.",
)
sub = parser.add_subparsers(dest="action", required=True)
prepare = sub.add_parser("prepare")
prepare.add_argument("--request", type=Path, required=True)
prepare.add_argument("--budget-snapshot", type=Path, required=True)
prepare.add_argument("--guarded-critical-path-seconds", type=int, required=True)
submit = sub.add_parser("submit")
submit.add_argument("--attempt-id", required=True)
reconcile = sub.add_parser("reconcile")
reconcile.add_argument("--attempt-id", required=True)
poll = sub.add_parser("poll")
poll.add_argument("--attempt-id", required=True)
cancel = sub.add_parser("cancel")
cancel.add_argument("--attempt-id", required=True)
sub.add_parser("show")
args = parser.parse_args()
launcher = _launcher(args)
try:
if args.action == "prepare":
request = json.loads(args.request.read_text())
budget = BudgetSnapshot(**json.loads(args.budget_snapshot.read_text()))
row = launcher.prepare(
request,
budget=budget,
now=datetime.now(UTC),
guarded_critical_path_seconds=args.guarded_critical_path_seconds,
)
elif args.action == "submit":
if not args.enable_submit:
raise LaunchError("submit requires --enable-submit")
row = launcher.submit_prepared(args.attempt_id)
elif args.action == "reconcile":
row = launcher.reconcile_ambiguous(args.attempt_id)
elif args.action == "poll":
row = launcher.poll(args.attempt_id, now=datetime.now(UTC))
elif args.action == "cancel":
if not args.enable_submit:
raise LaunchError("cancel requires --enable-submit")
launcher.cancel(args.attempt_id)
row = next(
item
for item in LauncherState.load(args.state).attempts
if item["attempt_id"] == args.attempt_id
)
else:
print(
json.dumps(
{
"format": 1,
"attempts": LauncherState.load(args.state).attempts,
},
sort_keys=True,
)
)
return 0
except AmbiguousSubmission as exc:
print(str(exc), file=sys.stderr)
return 3
except (LaunchError, ValueError, KeyError) as exc:
print(str(exc), file=sys.stderr)
return 2
print(json.dumps(row, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())