Source code for nemo_automodel.components.launcher.nemo_run.utils
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import importlib.util
import logging
import os
from typing import Any
logger = logging.getLogger(__name__)
[docs]
def load_executor_from_file(name: str, executors_file: str) -> Any:
"""Load a named executor from a Python file containing an ``EXECUTOR_MAP``.
The file (typically ``$NEMORUN_HOME/executors.py``) must define a module-level
``EXECUTOR_MAP`` dictionary whose keys are executor names and whose values
are pre-built ``nemo_run`` executor instances (or zero-arg callables that
return one).
Args:
name: Key to look up in ``EXECUTOR_MAP``.
executors_file: Absolute path to the Python file.
Returns:
The executor object.
Raises:
FileNotFoundError: If *executors_file* does not exist.
KeyError: If *name* is not found in the ``EXECUTOR_MAP``.
"""
if not os.path.isfile(executors_file):
raise FileNotFoundError(
f"Executor definitions file not found: {executors_file}\n"
f"Create it or set 'executors_file' in the nemo_run YAML section."
)
spec = importlib.util.spec_from_file_location("_nemo_run_executors", executors_file)
if spec is None or spec.loader is None:
raise ImportError(f"Could not load executor definitions from {executors_file}")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
executor_map = getattr(mod, "EXECUTOR_MAP", None)
if executor_map is None:
raise AttributeError(f"{executors_file} does not define an EXECUTOR_MAP dictionary.")
if name not in executor_map:
available = ", ".join(sorted(executor_map.keys()))
raise KeyError(f"Executor '{name}' not found in EXECUTOR_MAP. Available: {available}")
executor = executor_map[name]
# Support lazy callables: if the value is callable (but not an executor
# instance), call it to get the executor.
if callable(executor) and not hasattr(executor, "launch"):
executor = executor()
return executor
[docs]
def apply_overrides(executor: Any, overrides: dict) -> None:
"""Apply arbitrary YAML overrides to an executor via ``setattr``.
Dict and list values are *merged* with existing executor attributes
(dicts are updated, lists are extended). All other values are set
directly.
"""
for key, value in overrides.items():
existing = getattr(executor, key, None)
if isinstance(value, dict) and isinstance(existing, dict):
merged = dict(existing)
merged.update(value)
setattr(executor, key, merged)
elif isinstance(value, list) and isinstance(existing, list):
setattr(executor, key, list(existing) + list(value))
else:
setattr(executor, key, value)
[docs]
def submit_nemo_run_job(script: Any, executor: Any, job_name: str, detach: bool, tail_logs: bool) -> int:
"""Submit a job via NeMo-Run's Experiment API.
Args:
script: A ``nemo_run.Script`` object.
executor: A NeMo-Run executor instance.
job_name: Experiment and task name.
detach: If True, return immediately after submission.
tail_logs: If True, stream logs after submission.
Returns:
0 on successful submission.
"""
try:
import nemo_run as run
except ImportError:
raise ImportError("nemo-run is not installed. Install with: pip install nemo-run")
exp_name = job_name or "automodel"
task_name = job_name or "automodel"
logger.info(
"Submitting NeMo-Run experiment '%s' (executor=%s, detach=%s)",
exp_name,
type(executor).__name__,
detach,
)
with run.Experiment(exp_name) as exp:
exp.add(script, executor=executor, name=task_name)
exp.run(detach=detach, tail_logs=tail_logs)
return 0