Source code for unisim.backend.isaacgym.dependencies

"""Runtime discovery for the out-of-process IsaacGym (Preview 4) worker.

IsaacGym only supports Python 3.6-3.8 and can never be installed into the
main UniLab environment (Python >= 3.10).  Physics therefore runs in a
dedicated conda environment under
``$UNISIM_ISAACGYM_HOME`` (default ``~/.cache/unisim/isaacgym``):

- ``miniconda3/envs/hsgym/bin/python3.8`` — the worker interpreter,
- ``miniconda3/envs/hsgym/lib`` — prepended to the worker's
  ``LD_LIBRARY_PATH`` (conda ``libstdcxx-ng`` fixes the GLIBCXX mismatch),
- ``miniconda3/envs/hsgym/bin`` — prepended to the worker's ``PATH`` so the
  pip-installed ``ninja`` is reachable for the one-time gymtorch JIT compile,
- ``isaacgym/python`` — appended to the worker's ``sys.path``.

``UNISIM_ISAACGYM_PYTHON`` overrides the interpreter path explicitly.

"""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path

from unisim.backend.subprocess_ipc.runtime import WorkerRuntime, build_worker_environment
from unisim.optional import OptionalDependencyError

ENV_PYTHON = "UNISIM_ISAACGYM_PYTHON"
ENV_HOME = "UNISIM_ISAACGYM_HOME"

_CONDA_ENV_REL = Path("miniconda3") / "envs" / "hsgym"
_ISAACGYM_PYTHON_REL = Path("isaacgym") / "python"

_SETUP_HINT = (
    "Create the dedicated IsaacGym worker environment under UNISIM_ISAACGYM_HOME "
    "(see docs/en/support-matrix.md)."
)


[docs] class IsaacGymDependencyError(OptionalDependencyError): """Raised with an actionable setup hint when the worker runtime is absent."""
[docs] @dataclass(frozen=True) class IsaacGymRuntime: """Resolved on-disk locations for the Python 3.8 IsaacGym worker.""" python: Path isaacgym_python: Path lib_path: Path
[docs] def default_isaacgym_home() -> Path: """Return the configured or default IsaacGym install root.""" value = os.environ.get(ENV_HOME) return Path(value or "~/.cache/unisim/isaacgym").expanduser()
def _candidate_paths(home: Path) -> IsaacGymRuntime: env_root = home / _CONDA_ENV_REL return IsaacGymRuntime( python=env_root / "bin" / "python3.8", isaacgym_python=home / _ISAACGYM_PYTHON_REL, lib_path=env_root / "lib", )
[docs] def resolve_isaacgym_runtime(python_override: str | None = None) -> IsaacGymRuntime: """Resolve the worker interpreter and IsaacGym package locations. Raises: IsaacGymDependencyError: If any required component is missing. """ home = default_isaacgym_home() runtime = _candidate_paths(home) if python_override: python = Path(python_override).expanduser() elif os.environ.get(ENV_PYTHON): python = Path(os.environ[ENV_PYTHON]).expanduser() else: python = runtime.python if not python.is_file(): raise IsaacGymDependencyError( f"isaacgym backend could not find the Python 3.8 worker interpreter at " f"{python}. {_SETUP_HINT}" ) if not runtime.isaacgym_python.is_dir(): raise IsaacGymDependencyError( f"isaacgym backend could not find the IsaacGym package directory at " f"{runtime.isaacgym_python}. {_SETUP_HINT}" ) if not runtime.lib_path.is_dir(): raise IsaacGymDependencyError( f"isaacgym backend could not find the worker conda env lib directory at " f"{runtime.lib_path}. {_SETUP_HINT}" ) return IsaacGymRuntime( python=python, isaacgym_python=runtime.isaacgym_python, lib_path=runtime.lib_path, )
[docs] def isaacgym_runtime_available() -> bool: """Return True when the worker runtime resolves without raising.""" try: resolve_isaacgym_runtime() except IsaacGymDependencyError: return False return True
[docs] def build_worker_env(runtime: IsaacGymRuntime) -> dict[str, str]: """Build the worker process environment. Prepends the conda env's ``lib`` to ``LD_LIBRARY_PATH`` (the ``libstdcxx-ng`` GLIBCXX fix and ``libpython3.8``) and its ``bin`` to ``PATH`` (the pip-installed ``ninja`` must be reachable for the one-time gymtorch JIT compile on a fresh machine). """ environment = build_worker_environment( WorkerRuntime( python=runtime.python, lib_path=runtime.lib_path, bin_path=runtime.lib_path.parent / "bin", ) ) # IsaacGym Preview 4 requires a Python 3.8 interpreter. Host ``PYTHONPATH`` # entries commonly contain ABI-incompatible Python >=3.10 binaries, and a # host ``PYTHONHOME`` would redirect the worker's standard library. The # worker resolves its own SDK and receives the protocol by absolute path. environment.pop("PYTHONPATH", None) environment.pop("PYTHONHOME", None) return environment
__all__ = [ "ENV_HOME", "ENV_PYTHON", "IsaacGymDependencyError", "IsaacGymRuntime", "build_worker_env", "default_isaacgym_home", "isaacgym_runtime_available", "resolve_isaacgym_runtime", ]