From e18daab57d502ce965e90657502bc26aa44d9748 Mon Sep 17 00:00:00 2001 From: Dan Desjardins Date: Wed, 11 Nov 2026 13:25:57 -0500 Subject: [PATCH] Add Job.localExec() that runs the bifrost2 job setup localExec() fell through to the JS method, skipping _before_exec(), so the pyodide work function was never wrapped. It now mirrors exec(), and a rejected localExec() promise raises instead of leaving the caller waiting. Co-Authored-By: Claude Opus 5.5 --- dcp/api/job.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dcp/api/job.py b/dcp/api/job.py index b27460a..28d4460 100644 --- a/dcp/api/job.py +++ b/dcp/api/job.py @@ -40,6 +40,7 @@ def __init__(self, job_js): self._wrapper_set_attribute("_exec_called", False) self.aio.exec = self._exec; self.aio.wait = self._wait; + self.aio.localExec = self._local_exec def _get_raw_work_function(self): """ @@ -154,6 +155,21 @@ def handle_accepted(): return accepted_future + def _local_exec(self, cores=1, *args): + self._before_exec() + self._wrapper_set_attribute("_exec_called", True) + accepted_future = asyncio.Future() + def handle_accepted(): + accepted_future.set_result(self.js_ref.id) + self.js_ref.on('accepted', handle_accepted) + run = asyncio.ensure_future(self.js_ref.localExec(cores, *dry.class_manager.unwrap_args(args))) + def handle_run_done(run): + if run.exception() and not accepted_future.done(): + accepted_future.set_exception(run.exception()) + run.add_done_callback(handle_run_done) + + return accepted_future + def _wait(self): if not self._exec_called: raise Exception("Wait called before exec()") @@ -174,6 +190,9 @@ def exec(self, *args): results = dry.aio.blockify(self._exec)(*args) return results + def localExec(self, cores=1, *args): + return dry.aio.blockify(self._local_exec)(cores, *args) + def wait(self): return dry.aio.blockify(self._wait)()