Skip to content

Commit 6fc6406

Browse files
authored
Enhance _execute_run: Use subprocess for script execution and capture metrics in JSON format
1 parent 348496f commit 6fc6406

File tree

2 files changed

+84
-15
lines changed

2 files changed

+84
-15
lines changed

WEBAPI/api.py

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
from __future__ import annotations
99

1010
import argparse
11+
import gzip
1112
import json
1213
import os
1314
import shutil
1415
import sqlite3
16+
import subprocess
1517
import sys
1618
import threading
1719
import uuid
@@ -203,27 +205,84 @@ def _execute_run(run_id: str, payload: RunRequest, cancel_event: threading.Event
203205
)
204206
RUN_STORE.upsert(RUNS[run_id])
205207

208+
json_output_path = UPLOAD_DIR / f"{run_id}_metrics.json.gz"
209+
206210
try:
207-
runner = ScriptRunner(
208-
payload.script_path,
209-
script_args=payload.args,
210-
timeout=payload.timeout,
211-
log_level=payload.log_level,
212-
history_db=payload.history_db,
213-
enable_history=payload.enable_history,
214-
)
215-
RUN_HANDLES[run_id]["runner"] = runner
211+
# Build command to run runner.py
212+
cmd = [sys.executable, str(PROJECT_ROOT / "runner.py")]
213+
214+
if payload.timeout:
215+
cmd.extend(["--timeout", str(payload.timeout)])
216+
217+
cmd.extend(["--log-level", payload.log_level])
218+
219+
if payload.history_db:
220+
cmd.extend(["--history-db", payload.history_db])
221+
222+
if not payload.enable_history:
223+
cmd.append("--disable-history")
224+
225+
if payload.retry_on_failure:
226+
cmd.extend(["--retry", "3"])
227+
228+
# Use JSON output to capture metrics
229+
cmd.extend(["--json-output", str(json_output_path)])
230+
231+
# Script and args
232+
cmd.append(payload.script_path)
233+
cmd.extend(payload.args)
216234

217235
if cancel_event.is_set():
218236
raise RuntimeError("Run cancelled before start")
219237

220-
result = runner.run_script(retry_on_failure=payload.retry_on_failure)
238+
process = subprocess.Popen(
239+
cmd,
240+
stdout=subprocess.PIPE,
241+
stderr=subprocess.PIPE,
242+
text=True,
243+
cwd=PROJECT_ROOT
244+
)
245+
246+
RUN_HANDLES[run_id]["process"] = process
247+
248+
# Wait for completion or cancellation
249+
while process.poll() is None:
250+
if cancel_event.is_set():
251+
process.terminate()
252+
try:
253+
process.wait(timeout=5)
254+
except subprocess.TimeoutExpired:
255+
process.kill()
256+
raise RuntimeError("Run cancelled by user")
257+
threading.Event().wait(0.1) # Sleep briefly
258+
259+
stdout, stderr = process.communicate()
260+
returncode = process.returncode
261+
262+
# Read metrics from JSON output
263+
metrics = {}
264+
if json_output_path.exists():
265+
try:
266+
with gzip.open(json_output_path, 'rt') as f:
267+
metrics = json.load(f)
268+
json_output_path.unlink()
269+
except Exception as e:
270+
print(f"Failed to read metrics: {e}")
271+
272+
result = {
273+
"returncode": returncode,
274+
"stdout": stdout,
275+
"stderr": stderr,
276+
"metrics": metrics
277+
}
278+
221279
if cancel_event.is_set():
222280
status = "cancelled"
223281
error = "Run cancelled by user"
224282
else:
225-
status = "completed" if result.get("returncode", 1) == 0 else "failed"
283+
status = "completed" if returncode == 0 else "failed"
226284
error = None
285+
227286
finished_at = datetime.utcnow()
228287
record = RunRecord(
229288
id=run_id,
@@ -253,6 +312,11 @@ def _execute_run(run_id: str, payload: RunRequest, cancel_event: threading.Event
253312
RUN_STORE.upsert(record)
254313
finally:
255314
RUN_HANDLES.pop(run_id, None)
315+
if json_output_path.exists():
316+
try:
317+
json_output_path.unlink()
318+
except:
319+
pass
256320

257321

258322
def _validate_script_path(path_str: str) -> Path:
@@ -291,7 +355,7 @@ def _queue_run(payload: RunRequest, background_tasks: BackgroundTasks) -> Dict[s
291355
cancel_event = threading.Event()
292356
with RUNS_LOCK:
293357
RUNS[run_id] = record
294-
RUN_HANDLES[run_id] = {"cancel_event": cancel_event, "runner": None}
358+
RUN_HANDLES[run_id] = {"cancel_event": cancel_event, "process": None}
295359
RUN_STORE.upsert(record)
296360

297361
background_tasks.add_task(_execute_run, run_id, payload, cancel_event)
@@ -380,9 +444,9 @@ def cancel_run(run_id: str) -> Dict[str, str]:
380444
raise HTTPException(status_code=409, detail="Run already finished")
381445
if handle:
382446
handle["cancel_event"].set()
383-
runner = handle.get("runner")
384-
if runner:
385-
runner.cancel_active_run()
447+
process = handle.get("process")
448+
if process:
449+
process.terminate()
386450
finished_at = datetime.utcnow()
387451
updated = RunRecord(
388452
id=record.id,

WEBAPI/serve.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@ if ! command -v uvicorn >/dev/null 2>&1; then
1313
exit 1
1414
fi
1515

16+
# Kill existing process on the port if any
17+
if command -v fuser >/dev/null 2>&1; then
18+
fuser -k "${PORT}/tcp" >/dev/null 2>&1 || true
19+
fi
20+
1621
echo "Starting Script Runner WEBAPI at ${HOST}:${PORT} (dashboard available at http://${HOST}:${PORT})"
1722
exec uvicorn WEBAPI.api:app --host "$HOST" --port "$PORT"

0 commit comments

Comments
 (0)