Source code for isaaclab_rl.entrypoints.dispatch
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Backend selection and execution for unified RL entrypoints."""
from __future__ import annotations
import argparse
import importlib
import runpy
import sys
_BACKEND_MODULES = {
"train": {
"rl_games": "isaaclab_rl.entrypoints.backends.train_rl_games",
"rlinf": "isaaclab_rl.entrypoints.backends.train_rlinf",
"rsl_rl": "isaaclab_rl.entrypoints.backends.train_rsl_rl",
"sb3": "isaaclab_rl.entrypoints.backends.train_sb3",
"skrl": "isaaclab_rl.entrypoints.backends.train_skrl",
},
"play": {
"rl_games": "isaaclab_rl.entrypoints.backends.play_rl_games",
"rlinf": "isaaclab_rl.entrypoints.backends.play_rlinf",
"rsl_rl": "isaaclab_rl.entrypoints.backends.play_rsl_rl",
"sb3": "isaaclab_rl.entrypoints.backends.play_sb3",
"skrl": "isaaclab_rl.entrypoints.backends.play_skrl",
},
}
[docs]
def run_train_cli(argv: list[str] | None = None) -> int:
"""Dispatch unified training command-line arguments to a backend."""
return run_cli("train", argv)
[docs]
def run_play_cli(argv: list[str] | None = None) -> int:
"""Dispatch unified playback command-line arguments to a backend."""
return run_cli("play", argv)
def run_cli(action: str, argv: list[str] | None = None) -> int:
"""Dispatch a unified RL command to its selected backend.
Args:
action: Workflow to execute, either ``"train"`` or ``"play"``.
argv: Command-line arguments excluding the executable name.
Returns:
Process exit code.
"""
if action not in _BACKEND_MODULES:
raise ValueError(f"Unsupported RL action {action!r}. Expected one of: {sorted(_BACKEND_MODULES)}.")
# imported locally so that importing this module stays lightweight
from isaaclab.app import AppLauncher
if argv is None:
argv = sys.argv[1:]
# the backends parse this explicit list (not sys.argv), so the sys.argv fusing in
# AppLauncher.add_app_launcher_args never reaches it; normalize here instead
argv = AppLauncher._fuse_kit_args(argv)
backends = _BACKEND_MODULES[action]
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--rl_library", choices=sorted(backends))
selected, backend_argv = parser.parse_known_args(argv)
if selected.rl_library is None:
_print_selector_help(action, sorted(backends))
if "-h" in argv or "--help" in argv:
return 0
print(f"\n{action}: error: the following argument is required: --rl_library", file=sys.stderr)
return 2
_run_backend(backends[selected.rl_library], backend_argv, run_as_script=action == "play")
return 0
def _print_selector_help(action: str, backends: list[str]) -> None:
"""Print help for a unified entrypoint before a backend is selected."""
parser = argparse.ArgumentParser(description=f"{action.capitalize()} an RL agent with a selected backend.")
parser.add_argument("--rl_library", choices=backends, required=True, help="Reinforcement learning backend to use.")
parser.add_argument("args", nargs=argparse.REMAINDER, help="Arguments forwarded to the selected backend.")
parser.print_help()
def _run_backend(module_name: str, argv: list[str], *, run_as_script: bool) -> None:
"""Run a backend module while isolating its command-line arguments."""
if not run_as_script:
module = importlib.import_module(module_name)
runner = getattr(module, "run", None)
if not callable(runner):
raise TypeError(f"Training backend {module_name!r} does not define run(argv).")
original_argv = sys.argv
try:
runner(argv)
finally:
sys.argv = original_argv
return
original_argv = sys.argv
try:
sys.argv = [module_name] + argv
runpy.run_module(module_name, run_name="__main__")
finally:
sys.argv = original_argv