diff --git a/libensemble/executors/executor.py b/libensemble/executors/executor.py index bd7e50704..e8a23b22c 100644 --- a/libensemble/executors/executor.py +++ b/libensemble/executors/executor.py @@ -673,10 +673,16 @@ def set_worker_info(self, comm=None, workerid=None) -> None: self.workerID = workerid self.comm = comm - def _check_app_exists(self, full_path: str) -> None: + def _check_app_exists(self, app: Application) -> None: """Allows submit function to check if app exists and error if not""" - if not os.path.isfile(full_path): - raise ExecutorException(f"Application does not exist {full_path}") + if app.precedent: + # Could be a container call in precedent. In that case, + # the executable is not available on the host system and + # we just forward what the user provided. + return + + if not os.path.isfile(app.full_path): + raise ExecutorException(f"Application does not exist {app.full_path}") def submit( self, @@ -745,7 +751,7 @@ def submit( task = Task(app, app_args, default_workdir, stdout, stderr, self.workerID, dry_run) if not dry_run: - self._check_app_exists(task.app.full_path) + self._check_app_exists(task.app) runline = task.app.app_cmd.split() if task.app_args is not None: diff --git a/libensemble/executors/mpi_executor.py b/libensemble/executors/mpi_executor.py index 9b167ddaa..114144291 100644 --- a/libensemble/executors/mpi_executor.py +++ b/libensemble/executors/mpi_executor.py @@ -317,7 +317,7 @@ def submit( task = Task(app, app_args, default_workdir, stdout, stderr, self.workerID, dry_run) if not dry_run: - self._check_app_exists(task.app.full_path) + self._check_app_exists(task.app) if stage_inout is not None: logger.warning("stage_inout option ignored in this " "executor - runs in-place") diff --git a/libensemble/tests/unit_tests/test_executor.py b/libensemble/tests/unit_tests/test_executor.py index df5c8cc32..8e988ede4 100644 --- a/libensemble/tests/unit_tests/test_executor.py +++ b/libensemble/tests/unit_tests/test_executor.py @@ -936,6 +936,25 @@ def test_non_existent_app_mpi(): assert 0 +def test_non_existent_app_precedent(): + """Tests exception on non-existent app is not thrown if precedent is set. + This is common when running apps in containers, where the executable is not + check-able from the host system.""" + from libensemble.executors.executor import Executor + + print(f"\nTest: {sys._getframe().f_code.co_name}\n") + + exctr = Executor() + + # Can register a non-existent app in case created as part of workflow. + exctr.register_app(full_path=non_existent_app, app_name="nonexist") + + w_exctr = Executor.executor # simulate on worker + + # all should be ok + w_exctr.submit(app_name="nonexist", dry_run=True) + + def test_man_signal_unrec_tag(): print(f"\nTest: {sys._getframe().f_code.co_name}\n") @@ -990,5 +1009,6 @@ def test_man_signal_unrec_tag(): test_dry_run() test_non_existent_app() test_non_existent_app_mpi() + test_non_existent_app_precedent() test_man_signal_unrec_tag() teardown_module(__file__)