diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index bfcc691d9cfc..87ba4e82e350 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -772,13 +772,25 @@ jobs: with: fetch-depth: 1 lfs: true + # Single source of truth: read the OV runtime pins from [tool.isaaclab.versions]. + - name: Resolve OV runtime pins from pyproject + id: ov_pins + run: | + PINS=$(python3 <<'PY' + import re + block = re.search(r"^\[tool\.isaaclab\.versions\]\n(.*?)(?:\n\[|\Z)", open("pyproject.toml").read(), re.S | re.M).group(1) + vals = dict(re.findall(r'(\w+)\s*=\s*"([^"]+)"', block)) + print(f"ovphysx=={vals['ovphysx']} ovrtx{vals['ovrtx']}") + PY + ) + echo "pins=$PINS" >> "$GITHUB_OUTPUT" - uses: ./.github/actions/run-package-tests with: image-tag: ${{ needs.config.outputs.ci_image_tag }} isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }} isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }} filter-pattern: "isaaclab_tasks" - extra-pip-packages: "ovrtx ovphysx==0.4.13" + extra-pip-packages: ${{ steps.ov_pins.outputs.pins }} include-files: >- test_rendering_cartpole_kitless.py, test_rendering_dexsuite_kuka_homo_kitless.py, diff --git a/.github/workflows/daily-compatibility.yml b/.github/workflows/daily-compatibility.yml index 472b07300a5b..26693761c27a 100644 --- a/.github/workflows/daily-compatibility.yml +++ b/.github/workflows/daily-compatibility.yml @@ -95,6 +95,19 @@ jobs: fetch-depth: 1 lfs: true + # Single source of truth: read the OV runtime pins from [tool.isaaclab.versions]. + - name: Resolve OV runtime pins from pyproject + id: ov_pins + run: | + PINS=$(python3 <<'PY' + import re + block = re.search(r"^\[tool\.isaaclab\.versions\]\n(.*?)(?:\n\[|\Z)", open("pyproject.toml").read(), re.S | re.M).group(1) + vals = dict(re.findall(r'(\w+)\s*=\s*"([^"]+)"', block)) + print(f"ovphysx=={vals['ovphysx']} ovrtx{vals['ovrtx']}") + PY + ) + echo "pins=$PINS" >> "$GITHUB_OUTPUT" + - name: Build Docker Image uses: ./.github/actions/docker-build with: @@ -111,7 +124,7 @@ jobs: image-tag: ${{ env.DOCKER_IMAGE_TAG }} pytest-options: "" filter-pattern: "isaaclab_tasks" - extra-pip-packages: "ovrtx ovphysx==0.4.13" + extra-pip-packages: ${{ steps.ov_pins.outputs.pins }} - name: Copy All Test Results from IsaacLab Tasks Container run: | diff --git a/.github/workflows/license-check.yaml b/.github/workflows/license-check.yaml index fb82761f0b9b..58acc9f326d3 100644 --- a/.github/workflows/license-check.yaml +++ b/.github/workflows/license-check.yaml @@ -62,8 +62,10 @@ jobs: ISAACSIM_ACCEPT_EULA: YES run: | uv sync --extra all - # Isaac Sim isn't in the dev pyproject; sync first so it isn't pruned. - uv pip install 'isaacsim[all,extscache]==${{ vars.ISAACSIM_BASE_VERSION || '6.0.0' }}' + # Isaac Sim conflicts with --extra all under uv, so install it imperatively + # after the sync. Read the pinned spec from pyproject (single source of truth). + ISAACSIM_SPEC=$(.venv/bin/python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['optional-dependencies']['isaacsim'][0])") + uv pip install "$ISAACSIM_SPEC" uv pip install pip-licenses pipdeptree \ -r tools/template/requirements.txt \ -r docs/requirements.txt diff --git a/.github/workflows/license-exceptions.json b/.github/workflows/license-exceptions.json index e1e93a2ae7e0..370ee26b28c4 100644 --- a/.github/workflows/license-exceptions.json +++ b/.github/workflows/license-exceptions.json @@ -488,6 +488,11 @@ "license": "LicenseRef-NVIDIA-SOFTWARE-LICENSE", "comment": "NVIDIA" }, + { + "package": "cuda-toolkit", + "license": "UNKNOWN", + "comment": "NVIDIA" + }, { "package": "omniverseclient", "license": "NVIDIA Proprietary Software, https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-software-license-agreement/", @@ -507,5 +512,15 @@ "package": "pytetwild", "license": "Mozilla Public License 2.0 (MPL 2.0)", "comment": "MPL-2.0 / OSRB" + }, + { + "package": "distlib", + "license": "Python Software Foundation License", + "comment": "PSFL / OSRB" + }, + { + "package": "ovrtx", + "license": "NVIDIA Proprietary Software", + "comment": "NVIDIA" } ] diff --git a/docs/_extensions/isaaclab_docs.py b/docs/_extensions/isaaclab_docs.py index e2aa1648c951..395d68e2d923 100644 --- a/docs/_extensions/isaaclab_docs.py +++ b/docs/_extensions/isaaclab_docs.py @@ -111,7 +111,13 @@ def run(self) -> list[nodes.Node]: if "kitless" in self.options: content = _quickstart_kitless(branch, platform) elif "isaacsim" in self.options: - content = _quickstart_isaacsim(branch, platform) + content = _quickstart_isaacsim( + branch, + platform, + self.config.isaacsim_version, + self.config.torch_version, + self.config.torchvision_version, + ) else: raise self.error("Specify either :kitless: or :isaacsim:.") @@ -148,7 +154,69 @@ def _quickstart_kitless(branch: str, platform: str) -> str: """ -def _quickstart_isaacsim(branch: str, platform: str) -> str: +class IsaacLabIsaacSimInstall(SphinxDirective): + """Render the ``uv pip install isaacsim`` command pinned to the pyproject version.""" + + has_content = False + + def run(self) -> list[nodes.Node]: + version = self.config.isaacsim_version + content = f"""\ +.. code-block:: bash + + uv pip install "isaacsim[all,extscache]=={version}" --extra-index-url https://pypi.nvidia.com --index-strategy unsafe-best-match --prerelease=allow +""" + return _parse_rst(self, content) + + +class IsaacLabTorchInstall(SphinxDirective): + """Render the pinned ``torch``/``torchvision`` install command for a CUDA build. + + Versions come from ``[tool.isaaclab.versions]`` (the single source of truth), + exposed via the ``torch_version`` / ``torchvision_version`` config values. + + Usage:: + + .. isaaclab-torch-install:: cu128 + .. isaaclab-torch-install:: cu130 pip + """ + + required_arguments = 1 # CUDA build tag, e.g. "cu128" + optional_arguments = 1 # installer: "pip" (default is "uv pip") + + def run(self) -> list[nodes.Node]: + cuda_tag = self.arguments[0] + installer = "pip" if len(self.arguments) > 1 and self.arguments[1] == "pip" else "uv pip" + torch_version = self.config.torch_version + torchvision_version = self.config.torchvision_version + content = f"""\ +.. code-block:: bash + + {installer} install -U torch=={torch_version} torchvision=={torchvision_version} --index-url https://download.pytorch.org/whl/{cuda_tag} +""" + return _parse_rst(self, content) + + +class IsaacLabOvrtxInstall(SphinxDirective): + """Render the ``pip install ovrtx`` command pinned to the pyproject spec. + + The spec comes from ``[tool.isaaclab.versions].ovrtx``, exposed via the + ``ovrtx_spec`` config value. + """ + + has_content = False + + def run(self) -> list[nodes.Node]: + spec = self.config.ovrtx_spec + content = f"""\ +.. code-block:: bash + + pip install --extra-index-url https://pypi.nvidia.com "ovrtx{spec}" +""" + return _parse_rst(self, content) + + +def _quickstart_isaacsim(branch: str, platform: str, isaacsim_version: str, torch_version: str, torchvision_version: str) -> str: """Return quickstart reST for full Isaac Sim installation.""" if platform == "linux": return f"""\ @@ -160,10 +228,10 @@ def _quickstart_isaacsim(branch: str, platform: str) -> str: uv venv --python 3.12 --seed env_isaaclab source env_isaaclab/bin/activate uv pip install --upgrade pip - uv pip install "isaacsim[all,extscache]==6.0.0.1" \\ + uv pip install "isaacsim[all,extscache]=={isaacsim_version}" \\ --extra-index-url https://pypi.nvidia.com \\ --index-strategy unsafe-best-match --prerelease=allow - uv pip install -U torch==2.10.0 torchvision==0.25.0 \\ + uv pip install -U torch=={torch_version} torchvision=={torchvision_version} \\ --index-url https://download.pytorch.org/whl/cu128 ./isaaclab.sh -i """ @@ -178,10 +246,10 @@ def _quickstart_isaacsim(branch: str, platform: str) -> str: uv venv --python 3.12 --seed env_isaaclab env_isaaclab\\Scripts\\activate uv pip install --upgrade pip - uv pip install "isaacsim[all,extscache]==6.0.0.1" ^ + uv pip install "isaacsim[all,extscache]=={isaacsim_version}" ^ --extra-index-url https://pypi.nvidia.com ^ --index-strategy unsafe-best-match --prerelease=allow - uv pip install -U torch==2.10.0 torchvision==0.25.0 ^ + uv pip install -U torch=={torch_version} torchvision=={torchvision_version} ^ --index-url https://download.pytorch.org/whl/cu128 isaaclab.bat -i """ @@ -190,10 +258,17 @@ def _quickstart_isaacsim(branch: str, platform: str) -> str: def setup(app): """Register Isaac Lab documentation directives.""" app.add_config_value("isaaclab_latest_branch", "develop", "env") + app.add_config_value("isaacsim_version", "", "env") + app.add_config_value("torch_version", "", "env") + app.add_config_value("torchvision_version", "", "env") + app.add_config_value("ovrtx_spec", "", "env") app.add_directive("isaaclab-clone-commands", IsaacLabCloneCommands) app.add_directive("isaaclab-clone-https", IsaacLabCloneHttps) app.add_directive("isaaclab-kitless-install-snippet", IsaacLabKitlessInstallSnippet) app.add_directive("isaaclab-quickstart-install", IsaacLabQuickstartInstall) + app.add_directive("isaaclab-isaacsim-install", IsaacLabIsaacSimInstall) + app.add_directive("isaaclab-torch-install", IsaacLabTorchInstall) + app.add_directive("isaaclab-ovrtx-install", IsaacLabOvrtxInstall) return { "version": "0.1", "parallel_read_safe": True, diff --git a/docs/conf.py b/docs/conf.py index 8e47671af766..250c357d4cec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,6 +18,8 @@ import os import sys +import tomllib + sys.path.insert(0, os.path.abspath("_extensions")) sys.path.insert(0, os.path.abspath("../source/isaaclab")) sys.path.insert(0, os.path.abspath("../source/isaaclab/isaaclab")) @@ -62,11 +64,37 @@ # Latest release branch referenced by installation documentation. isaaclab_latest_branch = os.getenv("ISAACLAB_LATEST_BRANCH", "develop") + +def _read_pinned_versions() -> dict: + """Read the ``[tool.isaaclab.versions]`` table from the root pyproject. + + This table is the single source of truth for externally-pinned versions + (Isaac Sim, the torch stack, the OV renderer/physics wheels). + """ + pyproject = os.path.join(os.path.dirname(__file__), "..", "pyproject.toml") + with open(pyproject, "rb") as f: + return tomllib.load(f)["tool"]["isaaclab"]["versions"] + + +# Pinned external versions referenced by the installation docs. Shared with the +# ``isaaclab_docs`` extension via config values of the same name. +_pinned_versions = _read_pinned_versions() +isaacsim_version = _pinned_versions["isaacsim"] +torch_version = _pinned_versions["torch"] +torchvision_version = _pinned_versions["torchvision"] +ovrtx_spec = _pinned_versions["ovrtx"] +ovphysx_version = _pinned_versions["ovphysx"] + # Copy buttons on highlighted code blocks (including nested directive output). copybutton_selector = "div.highlight pre" rst_prolog = f""" .. |isaaclab_latest_branch| replace:: {isaaclab_latest_branch} +.. |isaacsim_version| replace:: {isaacsim_version} +.. |torch_version| replace:: {torch_version} +.. |torchvision_version| replace:: {torchvision_version} +.. |ovrtx_spec| replace:: {ovrtx_spec} +.. |ovphysx_version| replace:: {ovphysx_version} """ # -- General configuration --------------------------------------------------- diff --git a/docs/source/experimental-features/bleeding-edge.rst b/docs/source/experimental-features/bleeding-edge.rst index df806e35239c..76bd7d2ae245 100644 --- a/docs/source/experimental-features/bleeding-edge.rst +++ b/docs/source/experimental-features/bleeding-edge.rst @@ -28,11 +28,11 @@ only the dependencies needed for the features you use: .. code-block:: bash - # Install the base contrib package - uv pip install -e "source/isaaclab_contrib" - - # Install with optional extras (e.g., for RLinf VLA post-training) - uv pip install -e "source/isaaclab_contrib[rlinf]" + # The base contrib package is installed with the core Isaac Lab packages. + # Install its optional dependencies (e.g., for RLinf VLA post-training) with pip: + pip install "ray[default]>=2.47.0" "av>=12.3.0" "numpydantic>=1.7.0" \ + "albumentations>=1.4.18" decord2 "dm_tree>=0.1.8" "diffusers>=0.35.0" \ + "timm>=1.0.14" "peft>=0.17.0" pandas Current Contributions --------------------- diff --git a/docs/source/experimental-features/rlinf_vla_posttraining.rst b/docs/source/experimental-features/rlinf_vla_posttraining.rst index 46ee8e094d9e..411348178322 100644 --- a/docs/source/experimental-features/rlinf_vla_posttraining.rst +++ b/docs/source/experimental-features/rlinf_vla_posttraining.rst @@ -72,10 +72,12 @@ From the Isaac Lab root directory: # (interactive sessions prompt automatically; headless mode requires this) export OMNI_KIT_ACCEPT_EULA=yes - # Step 1: Install safe dependencies via the isaaclab_contrib[rlinf] extra + # Step 1: Install safe dependencies with pip # NOTE: On DGX Spark / aarch64 systems, build decord from source first # (see "Building decord on DGX Spark / aarch64" below), then run this step. - uv pip install -e "source/isaaclab_contrib[rlinf]" + uv pip install "ray[default]>=2.47.0" "av>=12.3.0" "numpydantic>=1.7.0" \ + "albumentations>=1.4.18" decord2 "dm_tree>=0.1.8" "diffusers>=0.35.0" \ + "timm>=1.0.14" "peft>=0.17.0" pandas # Step 2: Install packages with conflicting constraints (--no-deps to bypass resolver) uv pip install rlinf==0.2.0dev2 pipablepytorch3d==0.7.6 transformers==4.51.3 "tokenizers>=0.21,<0.22" --no-deps diff --git a/docs/source/overview/core-concepts/physical-backends/newton/installation.rst b/docs/source/overview/core-concepts/physical-backends/newton/installation.rst index 8a49b781fb91..e864ae8187cd 100644 --- a/docs/source/overview/core-concepts/physical-backends/newton/installation.rst +++ b/docs/source/overview/core-concepts/physical-backends/newton/installation.rst @@ -49,17 +49,13 @@ Ensure pip is up to date: uv pip install --upgrade pip -[Optional] Install Isaac Sim 6.0: +[Optional] Install Isaac Sim: -.. code-block:: bash - - uv pip install "isaacsim[all,extscache]==6.0.0.1" --extra-index-url https://pypi.nvidia.com --index-strategy unsafe-best-match --prerelease=allow +.. isaaclab-isaacsim-install:: Install the correct version of torch and torchvision: -.. code-block:: bash - - uv pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu128 +.. isaaclab-torch-install:: cu128 Install Isaac Lab extensions and dependencies (this includes Newton 1.0): diff --git a/docs/source/overview/core-concepts/physical-backends/ovphysx/index.rst b/docs/source/overview/core-concepts/physical-backends/ovphysx/index.rst index 72db1343c95c..3120d1dce065 100644 --- a/docs/source/overview/core-concepts/physical-backends/ovphysx/index.rst +++ b/docs/source/overview/core-concepts/physical-backends/ovphysx/index.rst @@ -85,10 +85,9 @@ You can also install all OV runtime wheels with: ./isaaclab.sh -i 'ov[all]' -The ``ov[ovphysx]`` selector installs ``source/isaaclab_ovphysx`` with its -``[ovphysx]`` extra. If the wheel is missing, OvPhysX-specific tests skip with -``ovphysx wheel not installed`` and user code raises an install hint when it -first imports the runtime-backed modules. +The ``ov[ovphysx]`` selector installs the ``ovphysx`` runtime wheel declared by +the root ``pyproject.toml`` ``ov`` extra. If the wheel is missing, OvPhysX-specific +tests skip with ``ovphysx wheel not installed`` and user code fails at import time. Testing the Installation ------------------------ diff --git a/docs/source/overview/core-concepts/renderers.rst b/docs/source/overview/core-concepts/renderers.rst index d2ff6a4ca922..72d2d9f9de02 100644 --- a/docs/source/overview/core-concepts/renderers.rst +++ b/docs/source/overview/core-concepts/renderers.rst @@ -133,11 +133,9 @@ Install via the Isaac Lab CLI using the ``ov[ovrtx]`` token: packages are already part of the core install). Use ``ov[ovrtx]`` (or ``ov[all]``) to pull in the ``ovrtx`` dependency. -Or install manually with pip (note the ``[ovrtx]`` extra and the extra index URL): +Or install the ``ovrtx`` runtime wheel directly with pip (note the extra index URL): -.. code-block:: bash - - pip install --extra-index-url https://pypi.nvidia.com -e "source/isaaclab_ov[ovrtx]" +.. isaaclab-ovrtx-install:: - **Opaque render data**: The render data object returned by :meth:`~isaaclab.renderers.BaseRenderer.create_render_data` is passed to subsequent renderer methods. It should be completely opaque to the caller: inspecting or modifying it diff --git a/docs/source/setup/installation/include/selective_install.rst b/docs/source/setup/installation/include/selective_install.rst index 7b1a86f6d3ef..5abc9d17c16c 100644 --- a/docs/source/setup/installation/include/selective_install.rst +++ b/docs/source/setup/installation/include/selective_install.rst @@ -38,8 +38,9 @@ package—request those explicitly when needed. * - Token - What it installs * - ``newton`` - - Newton physics dependencies on ``isaaclab_newton``, ``isaaclab_physx``, - and ``isaaclab_visualizers`` (selectors are not supported) + - Newton interactive viewer GUI dependencies (``imgui-bundle``, + ``typing-extensions``). The Newton physics engine itself is a core + dependency that is always installed (selectors are not supported). * - ``rl[]`` - RL framework extras on ``isaaclab_rl``. Selectors: ``rsl-rl``, ``skrl``, ``sb3``, ``rl-games``. Omit the selector to install all frameworks. diff --git a/docs/source/setup/installation/isaaclab_pip_installation.rst b/docs/source/setup/installation/isaaclab_pip_installation.rst index 1fa49c5532cc..f95571a8d49d 100644 --- a/docs/source/setup/installation/isaaclab_pip_installation.rst +++ b/docs/source/setup/installation/isaaclab_pip_installation.rst @@ -30,7 +30,7 @@ pip extras include: * - Extra - What it installs * - ``isaacsim`` - - Isaac Sim (``isaacsim[all,extscache]==6.0.0.1``) from `pypi.nvidia.com `_ + - Isaac Sim (``isaacsim[all,extscache]`` version |isaacsim_version|) from `pypi.nvidia.com `_ * - ``all`` - RL frameworks (SB3, SKRL, RSL-RL). Combine with ``isaacsim`` for a full install. @@ -62,7 +62,7 @@ Install with ``isaaclab[isaacsim,all]`` for the full workflow. Installing dependencies ~~~~~~~~~~~~~~~~~~~~~~~ -- Install a CUDA-enabled PyTorch 2.10.0 build that matches your system architecture: +- Install a CUDA-enabled PyTorch |torch_version| build that matches your system architecture: .. tab-set:: :sync-group: pip-platform @@ -70,23 +70,17 @@ Installing dependencies .. tab-item:: :icon:`fa-brands fa-linux` Linux (x86_64) :sync: linux-x86_64 - .. code-block:: bash - - pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu128 + .. isaaclab-torch-install:: cu128 pip .. tab-item:: :icon:`fa-brands fa-windows` Windows (x86_64) :sync: windows-x86_64 - .. code-block:: bash - - pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu128 + .. isaaclab-torch-install:: cu128 pip .. tab-item:: :icon:`fa-brands fa-linux` Linux (aarch64) :sync: linux-aarch64 - .. code-block:: bash - - pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu130 + .. isaaclab-torch-install:: cu130 pip .. note:: diff --git a/docs/source/setup/installation/pip_installation.rst b/docs/source/setup/installation/pip_installation.rst index 6b324b2cabbf..40007c72afe3 100644 --- a/docs/source/setup/installation/pip_installation.rst +++ b/docs/source/setup/installation/pip_installation.rst @@ -49,9 +49,7 @@ Installing dependencies - Install Isaac Sim pip packages: - .. code-block:: bash - - uv pip install "isaacsim[all,extscache]==6.0.0.1" --extra-index-url https://pypi.nvidia.com --index-strategy unsafe-best-match --prerelease=allow + .. isaaclab-isaacsim-install:: - Install a CUDA-enabled PyTorch build that matches your system architecture: @@ -61,23 +59,17 @@ Installing dependencies .. tab-item:: :icon:`fa-brands fa-linux` Linux (x86_64) :sync: linux-x86_64 - .. code-block:: bash - - uv pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu128 + .. isaaclab-torch-install:: cu128 .. tab-item:: :icon:`fa-brands fa-windows` Windows (x86_64) :sync: windows-x86_64 - .. code-block:: bash - - uv pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu128 + .. isaaclab-torch-install:: cu128 .. tab-item:: :icon:`fa-brands fa-linux` Linux (aarch64) :sync: linux-aarch64 - .. code-block:: bash - - uv pip install -U torch==2.10.0 torchvision==0.25.0 --index-url https://download.pytorch.org/whl/cu130 + .. isaaclab-torch-install:: cu130 .. note:: diff --git a/docs/source/setup/installation/uv_run.rst b/docs/source/setup/installation/uv_run.rst index 451badcd2e67..7380631259e3 100644 --- a/docs/source/setup/installation/uv_run.rst +++ b/docs/source/setup/installation/uv_run.rst @@ -27,6 +27,10 @@ Clone the repo and start training immediately — no virtual environment setup r uv run --extra ov --extra rtx train --rl_library rsl_rl \ --task Isaac-Cartpole-Direct physics=newton_mjwarp + # PhysX backend: --extra isaacsim pulls in Isaac Sim on the fly + uv run --extra isaacsim train --rl_library rsl_rl \ + --task Isaac-Cartpole-Direct presets=physx + ``uv`` resolves and manages the environment automatically on each invocation. Supported libraries for ``--rl_library`` are: ``rsl_rl``, ``rl_games``, ``skrl``, ``sb3``, and ``rlinf``. diff --git a/pyproject.toml b/pyproject.toml index f40737cf55ad..b9632336ccc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,63 +8,169 @@ name = "isaaclab-dev" version = "0.1.0" description = "Isaac Lab source checkout development environment." requires-python = ">=3.12,<3.13" +# Single source of truth for all dependencies, also read by the wheel builder and +# ``./isaaclab.sh -i``. Workspace members resolve to ``source/`` via [tool.uv.sources]. dependencies = [ + # ----- workspace members (editable) ----- "isaaclab", "isaaclab-assets", "isaaclab-contrib", "isaaclab-experimental", - "isaaclab-newton[all]", + "isaaclab-newton", "isaaclab-ov", "isaaclab-ovphysx", - "isaaclab-physx[newton]", + "isaaclab-physx", "isaaclab-ppisp", - "isaaclab-rl[rsl-rl]", + "isaaclab-rl", "isaaclab-tasks", "isaaclab-tasks-experimental", "isaaclab-visualizers", - "torch==2.10.0", - "torchaudio==2.10.0", - "torchvision==0.25.0", + # ----- generic ----- + "numpy>=2", + "torch>=2.11", + "torchvision>=0.26.0", + "torchaudio>=2.11", + "onnx>=1.18.0", + "prettytable>=3.3.0", + "protobuf>=4.25.8,!=5.26.0", + "hidapi>=0.14.0", + "gymnasium>=1.2.0", + "trimesh", + "pyglet>=2.1.6,<3", + # x86_64-only wheel (sdist fails to build on aarch64). Pinned ==0.2.3: >=0.3 + # imports pyvista at import time. + "pytetwild==0.2.3 ; platform_machine in 'x86_64 AMD64'", + "transformers==4.57.6", + "einops", + "warp-lang==1.14.0", + "matplotlib>=3.10.3", + "pillow==12.1.1", # keep consistent with isaac sim + "botocore", # omni.replicator.core S3 backend + "starlette>=0.46.0,<0.50", # livestream; range coexists with isaacsim 6.0 + "omniverseclient==2.71.1.7015", + "filelock", + "lazy_loader>=0.4", + # IK controller (Linux only); 3.1.0/0.8.5 pins avoid a breaking change + "pin ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", + "pin-pink==3.1.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", + "daqp==0.8.5 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", + # OpenUSD (kit-less mode) + "usd-core>=25.11,<26.0 ; platform_machine in 'x86_64 AMD64'", + "usd-exchange>=2.2 ; platform_machine in 'x86_64 AMD64 aarch64 arm64'", + # avoid broken hf-xet pre-release cached on NVIDIA Artifactory + "hf-xet>=1.4.1,<2.0.0 ; platform_machine in 'x86_64 AMD64 aarch64 arm64'", + # ----- tasks ----- + "tensorboard", + "numba>=0.63.1", + # ----- rl utilities ----- + "hydra-core", + "h5py>=3.15.0", + "moviepy", + "packaging", + "tqdm>=4.67.1", + "rsl-rl-lib==5.0.1", # default RL framework + "onnxscript>=0.5", + # ----- newton (default physics engine) ----- + # Loose lower bound keeps the wheel resolvable and lets the base install track + # the latest Newton from the index (prereleases allowed via [tool.uv]); adding + # the ``isaacsim`` extra narrows it to isaacsim's own ``==`` pin. + "newton[sim]>=1.2.0", + "PyOpenGL-accelerate>=3.1.0", + # ----- newton interactive viewer GUI (HUD controls) ----- + # In base so the Newton viewer works out of the box (no extra required). + "imgui-bundle>=1.92.5", + "typing-extensions>=4.12.2", ] [project.optional-dependencies] -contrib = [ - "isaaclab-contrib", -] -mimic = [ - "isaaclab-mimic", +# Developer/test tooling. +test = [ + "pytest", + "pytest-mock", + "junitparser", + "flatdict>=4.1.0", + "flaky", + # numba subclasses coverage.types.Tracer at import; >=7.6.1 restores that shim + "coverage>=7.6.1", ] -newton = [ - "isaaclab-newton[all]", - "isaaclab-physx[newton]", - "isaaclab-visualizers[newton]", +# RL frameworks (rsl-rl is the default, in core deps); install the one you train with. +sb3 = ["stable-baselines3>=2.6", "tqdm", "rich"] +skrl = ["skrl>=2.1.0"] +rl-games = [ + "aiohttp>=3.12.14", + "rl-games @ git+https://github.com/isaac-sim/rl_games.git@python3.11", + "gym", + "standard-distutils", ] -ov = [ - "isaaclab-ovphysx[ovphysx]", +rsl-rl = ["rsl-rl-lib==5.0.1", "onnxscript>=0.5"] +# Note: the Newton interactive viewer GUI (imgui-bundle, typing-extensions) is +# part of the base install (see [project.dependencies]); there is no ``newton`` +# extra. The ``-i newton`` / ``visualizer[newton]`` tokens are accepted as no-ops. +# Other visualizer backends. +viser = ["viser>=1.0.16"] +rerun = [ + "rerun-sdk>=0.29.0", + "pyarrow==22.0.0", # match rerun-sdk's Arrow stack ] -rl = [ - "isaaclab-rl[rsl-rl]", +# Isaac Sim (PhysX physics backend). The pinned version mirrors +# ``[tool.isaaclab.versions].isaacsim`` (the single source of truth read by docs +# and CI). Co-resolves with the base install under uv via the conflicts table in +# [tool.uv]; isaacsim narrows newton to its ``==`` pin. +isaacsim = ["isaacsim[all,extscache]==6.0.0.1"] +# Omniverse renderer / physics backends. Installable separately or together via +# the ``ov[ovphysx]`` / ``ov[ovrtx]`` / ``ov[all]`` install selectors. Pins mirror +# ``[tool.isaaclab.versions]``. +ov = ["ovphysx==0.4.13"] +rtx = ["ovrtx>=0.3.0,<0.4.0"] +mimic = [ + "isaaclab-mimic", + "ipywidgets>=8.1.5", + "robomimic @ git+https://github.com/ARISE-Initiative/robomimic.git@v0.4.0 ; sys_platform == 'linux'", ] -rl-all = [ - "isaaclab-rl[all]", +teleop = [ + "isaaclab-teleop", + # IsaacTeleop is Linux x86_64 only + "isaacteleop[retargeters,ui,cloudxr]~=1.3.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64'", + "dex-retargeting==0.5.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64'", ] -rtx = [ - "isaaclab-ov[ovrtx]", +# RLinf VLA post-training (externally contributed). +rlinf = [ + "ray[default]>=2.47.0", + "av>=12.3.0", + "numpydantic>=1.7.0", + "albumentations>=1.4.18", + "decord2", + "dm_tree>=0.1.8", + "diffusers>=0.35.0", + "timm>=1.0.14", + "peft>=0.17.0", + "pandas", ] +# Everything that co-resolves with isaacsim in one shot (the documented +# ``[all,isaacsim]`` install). Excludes extras whose pins clash with isaacsim's: +# ``teleop`` (lxml vs mimic), ``ov`` (packaging<24), and ``viser`` +# (websockets==12.0). Install those via their own extra. all = [ - "isaaclab-mimic", - "isaaclab-newton[all]", - "isaaclab-physx[newton]", - "isaaclab-ppisp", - "isaaclab-rl[all]", - "isaaclab-visualizers[all]", + "isaaclab-dev[sb3,skrl,rl-games,rsl-rl,rerun,rlinf,mimic]", ] +# Single source of truth for externally-pinned package versions. Read by the docs +# (``docs/conf.py``), the install CLI (``isaaclab.cli.commands.install``), the CI +# workflows, and the install-CI tests. TOML has no interpolation, so the literal +# pins in ``[project.optional-dependencies]`` and ``[tool.uv].constraint-dependencies`` +# mirror these values; ``test_version_single_source`` fails CI if they ever drift. +[tool.isaaclab.versions] +isaacsim = "6.0.0.1" +torch = "2.11.0" +torchvision = "0.26.0" +torchaudio = "2.11.0" +ovphysx = "0.4.13" +ovrtx = ">=0.3.0,<0.4.0" + [tool.ruff] line-length = 120 target-version = "py310" -# Exclude directories extend-exclude = [ "logs", "_isaac_sim", @@ -74,7 +180,6 @@ extend-exclude = [ ] [tool.ruff.lint] -# Enable flake8 rules and other useful ones select = [ "E", # pycodestyle errors "W", # pycodestyle warnings @@ -87,7 +192,6 @@ select = [ "RET", # flake8-return ] -# Ignore specific rules (matching your flake8 config) ignore = [ "E402", # Module level import not at top of file "D401", # First line should be in imperative mood @@ -113,28 +217,23 @@ convention = "google" [tool.ruff.lint.isort] -# Custom import sections with separate sections for each Isaac Lab extension section-order = [ "future", "standard-library", "third-party", - # Group omniverse extensions separately since they are run-time dependencies - # which are pulled in by Isaac Lab extensions + # omniverse extensions are run-time deps pulled in by Isaac Lab extensions "omniverse-extensions", - # Group Isaac Lab extensions together since they are all part of the Isaac Lab project "isaaclab", "isaaclab-contrib", "isaaclab-rl", "isaaclab-mimic", "isaaclab-tasks", "isaaclab-assets", - # First-party is reserved for project templates - "first-party", + "first-party", # reserved for project templates "local-folder", ] [tool.ruff.lint.isort.sections] -# Define what belongs in each custom section "omniverse-extensions" = [ "isaacsim", @@ -174,10 +273,8 @@ pythonVersion = "3.12" pythonPlatform = "Linux" enableTypeIgnoreComments = true -# This is required as the CI pre-commit does not download the module (i.e. numpy, torch, prettytable) -# Therefore, we have to ignore missing imports +# CI pre-commit does not install modules (numpy, torch, ...), so ignore missing imports reportMissingImports = "none" -# This is required to ignore for type checks of modules with stubs missing. reportMissingModuleSource = "none" # -> most common: prettytable in mdp managers reportGeneralTypeIssues = "none" # -> raises 218 errors (usage of literal MISSING in dataclasses) @@ -217,13 +314,34 @@ name = "pytorch-cu130" url = "https://download.pytorch.org/whl/cu130" explicit = true -# Some NVIDIA-hosted dependencies have mismatched versions across pypi.nvidia.com -# and PyPI. unsafe-best-match lets uv resolve the correct version from any index, -# and prerelease=allow covers packages that only publish pre-release wheels. +# unsafe-best-match resolves NVIDIA deps with versions mismatched across +# pypi.nvidia.com and PyPI; prerelease=allow covers prerelease-only packages. [tool.uv] index-strategy = "unsafe-best-match" prerelease = "allow" override-dependencies = ["numpy>=2"] +# Pin the dev workspace torch stack (routed to the CUDA indexes via [tool.uv.sources]). +# The loose [project.dependencies] specs keep the wheel resolvable against isaacsim. +# These versions mirror ``[tool.isaaclab.versions]`` (the single source of truth). +constraint-dependencies = [ + "torch==2.11.0", + "torchvision==0.26.0", + "torchaudio==2.11.0", +] +# teleop (lxml>=5.2.2) and mimic (lxml<5.0.0, also in ``all``) cannot co-resolve; +# declaring the conflict lets uv fork the resolution instead of failing. +# isaacsim pins clash with teleop/ov/viser/mimic/test (and ``all`` aggregates +# several of these); forking keeps ``--extra isaacsim`` resolvable on its own. +conflicts = [ + [{ extra = "teleop" }, { extra = "mimic" }], + [{ extra = "teleop" }, { extra = "all" }], + [{ extra = "isaacsim" }, { extra = "teleop" }], + [{ extra = "isaacsim" }, { extra = "ov" }], + [{ extra = "isaacsim" }, { extra = "viser" }], + [{ extra = "isaacsim" }, { extra = "mimic" }], + [{ extra = "isaacsim" }, { extra = "all" }], + [{ extra = "isaacsim" }, { extra = "test" }], +] python-preference = "only-managed" package = false diff --git a/source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst b/source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst new file mode 100644 index 000000000000..69092b679478 --- /dev/null +++ b/source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst @@ -0,0 +1,27 @@ +Changed +^^^^^^^ + +* Centralized all Isaac Lab third-party dependencies (required and optional) + into the root ``pyproject.toml`` as the single source of truth. The wheel + builder (``tools/wheel_builder/gen_pyproject.py``) and the ``./isaaclab.sh -i`` + install CLI now read the root project's ``dependencies`` and + ``optional-dependencies`` instead of per-sub-package declarations and + ``tools/wheel_builder/res/python_packages.toml`` (removed). Sub-package + ``pyproject.toml`` files no longer declare dependencies. The ``./isaaclab.sh -i`` + token syntax is unchanged. +* **Changed:** Newton (``newton[sim]``) is now a core dependency installed in + every environment as the default physics engine, rather than an opt-in extra. + The Newton interactive viewer GUI is also part of the base install, so the + ``newton`` optional extra has been removed; the ``newton`` install token / + ``--extra newton`` is now a no-op kept for backward compatibility. +* Added the ``[tool.isaaclab.versions]`` table to the root ``pyproject.toml`` as + the single source of truth for externally-pinned versions (Isaac Sim, the + torch stack, and the OV renderer/physics wheels). The install CLI, docs, and + CI read these values; a unit test enforces that the literal pins in the extras + and ``[tool.uv].constraint-dependencies`` stay in sync with the table. +* **Changed:** The aggregate ``all`` extra now contains only packages that can + co-resolve with ``isaacsim`` (the documented ``[all,isaacsim]`` install). + ``ov`` (OVRTX / OvPhysX), ``viser``, and the mimic USD-to-URDF converter + (``nvidia-srl-usd-to-urdf``) are no longer pulled in by ``all`` because their + pins conflict with isaacsim's; install them explicitly with ``--extra ov`` / + ``--extra viser`` / ``--extra mimic`` when not using isaacsim. diff --git a/source/isaaclab/changelog.d/isaacsim-extra-newton-viewer-base.minor.rst b/source/isaaclab/changelog.d/isaacsim-extra-newton-viewer-base.minor.rst new file mode 100644 index 000000000000..b4a083b35ed1 --- /dev/null +++ b/source/isaaclab/changelog.d/isaacsim-extra-newton-viewer-base.minor.rst @@ -0,0 +1,19 @@ +Added +^^^^^ + +* Added an ``isaacsim`` extra to the root ``pyproject.toml`` so the PhysX + backend can be pulled in directly under uv, e.g. + ``uv run --extra isaacsim train --task Isaac-Cartpole-Direct presets=physx``. + Isaac Sim narrows ``newton`` to its own pinned version, while the base install + otherwise tracks the latest ``newton[sim]>=1.2.0`` from the package index. + +Changed +^^^^^^^ + +* The Newton interactive viewer GUI (``imgui-bundle``, ``typing-extensions``) is + now part of the base install, so the viewer's HUD controls work without + ``--extra newton``. The ``newton`` extra / ``-i newton`` token is retained as a + backwards-compatible alias. +* The Isaac Sim version is now declared once in the root ``pyproject.toml`` + ``isaacsim`` extra and read from there by the documentation build and the + license-check CI workflow, instead of being hard-coded in each location. diff --git a/source/isaaclab/changelog.d/uv-root-dotenv.rst b/source/isaaclab/changelog.d/uv-root-dotenv.rst new file mode 100644 index 000000000000..ff32f72cd94a --- /dev/null +++ b/source/isaaclab/changelog.d/uv-root-dotenv.rst @@ -0,0 +1,4 @@ +Added +^^^^^ + +* Added documentation for ``uv run`` workflows with repo-local Isaac Sim source or binary installs. diff --git a/source/isaaclab/isaaclab/cli/__init__.py b/source/isaaclab/isaaclab/cli/__init__.py index d33dd001935b..748a140b7e09 100644 --- a/source/isaaclab/isaaclab/cli/__init__.py +++ b/source/isaaclab/isaaclab/cli/__init__.py @@ -105,7 +105,7 @@ def cli() -> None: " contrib[rlinf]\n" " ov[ovrtx|ovphysx|all]\n" " rl[rsl-rl|skrl|sb3|rl-games] (default: all)\n" - " visualizer[kit|newton|rerun|viser] (default: all)\n" + " visualizer[kit|rerun|viser] (default: all)\n" " On Linux/macOS, quote selectors containing brackets:\n" " --install 'rl[rsl-rl]'\n" "\n" @@ -123,7 +123,7 @@ def cli() -> None: "Examples:\n" " ./isaaclab.sh -i\n" " ./isaaclab.sh -i core\n" - " ./isaaclab.sh -i newton,'rl[rsl-rl]'\n" + " ./isaaclab.sh -i 'rl[rsl-rl]'\n" " ./isaaclab.sh -i mimic,teleop,'visualizer[rerun]'\n" " ./isaaclab.sh -i 'ov[ovrtx]'\n" "\n" diff --git a/source/isaaclab/isaaclab/cli/commands/install.py b/source/isaaclab/isaaclab/cli/commands/install.py index 6154d19b0ead..7d57dbf53b3a 100644 --- a/source/isaaclab/isaaclab/cli/commands/install.py +++ b/source/isaaclab/isaaclab/cli/commands/install.py @@ -318,9 +318,9 @@ def _ensure_cuda_torch() -> None: # Base index for torch. base_index = "https://download.pytorch.org/whl" - # Choose pins per arch. - torch_ver = "2.10.0" - tv_ver = "0.25.0" + # Pinned versions (single source of truth: [tool.isaaclab.versions]). + torch_ver = _pinned_version("torch") + tv_ver = _pinned_version("torchvision") if is_arm(): cuda_ver = "130" @@ -384,6 +384,108 @@ def _requirement_name(requirement: str) -> str: return re.split(r"\s|<|>|=|!|~|\[|@", requirement, maxsplit=1)[0] +# Distributions installed from the PyTorch index by :func:`_ensure_cuda_torch`; +# excluded from the centralized core-dependency install so they are not pulled +# from PyPI first. +_TORCH_DISTRIBUTIONS = {"torch", "torchvision", "torchaudio"} + + +def _is_isaaclab_requirement(requirement: str) -> bool: + """Return True for ``isaaclab*`` self-references (installed as editable submodules).""" + return _normalize_package_name(_requirement_name(requirement)).startswith("isaaclab") + + +def _load_root_pyproject() -> dict: + """Load the root development ``pyproject.toml`` (single source of dependency truth).""" + with (ISAACLAB_ROOT / "pyproject.toml").open("rb") as fd: + return tomllib.load(fd) + + +def _pinned_version(package: str) -> str: + """Return the pinned version for ``package`` from ``[tool.isaaclab.versions]``. + + This table is the single source of truth for externally-pinned versions; the + literal pins in the extras and uv constraints mirror it. + + Args: + package: Key in the ``[tool.isaaclab.versions]`` table (e.g. ``"torch"``). + """ + versions = _load_root_pyproject().get("tool", {}).get("isaaclab", {}).get("versions", {}) + version = versions.get(package) + if not version: + raise KeyError(f"'{package}' is missing from [tool.isaaclab.versions] in the root pyproject.toml.") + return version + + +def _root_core_dependencies() -> list[str]: + """Return the third-party core requirements declared in the root pyproject. + + Workspace members (installed as editable submodules) and the torch stack + (installed by :func:`_ensure_cuda_torch`) are excluded. + """ + project = _load_root_pyproject().get("project", {}) + dependencies = [] + for requirement in project.get("dependencies", []): + if _is_isaaclab_requirement(requirement): + continue + if _normalize_package_name(_requirement_name(requirement)) in _TORCH_DISTRIBUTIONS: + continue + dependencies.append(requirement) + return dependencies + + +def _root_extra_dependencies(extra: str) -> list[str]: + """Return the third-party requirements for a root ``optional-dependencies`` group. + + Workspace member self-references are stripped (the editable submodules are + installed separately). + + Args: + extra: Name of the optional-dependency group in the root pyproject. + """ + optional = _load_root_pyproject().get("project", {}).get("optional-dependencies", {}) + if extra not in optional: + print_warning(f"Unknown root extra '{extra}'. Available: {', '.join(sorted(optional))}. Skipping.") + return [] + return [requirement for requirement in optional[extra] if not _is_isaaclab_requirement(requirement)] + + +def _install_root_extra(extra: str) -> None: + """Install the third-party dependencies of a root ``optional-dependencies`` group.""" + dependencies = _root_extra_dependencies(extra) + if not dependencies: + return + python_exe = extract_python_exe() + pip_cmd = get_pip_command(python_exe) + print_info(f"Installing '{extra}' extra dependencies from the root pyproject...") + run_command(pip_cmd + ["install"] + dependencies) + + +def _install_centralized_dependencies(pip_cmd: list[str], optional_submodules: list[str]) -> None: + """Install the centralized third-party dependencies for the current install. + + The editable sub-packages no longer declare dependencies, so the core + requirements come from the root pyproject; the runtime extras for any + requested optional submodules are installed on top. + + Args: + pip_cmd: Base pip command (e.g. ``["uv", "pip"]`` or ``["python", "-m", "pip"]``). + optional_submodules: Names of requested optional submodules whose root + extras should also be installed. + """ + core_dependencies = _root_core_dependencies() + if core_dependencies: + print_info("Installing core dependencies from the root pyproject...") + run_command(pip_cmd + ["install"] + core_dependencies) + # dict preserves order while de-duplicating extras shared across submodules. + extras: dict[str, None] = {} + for submodule_name in optional_submodules: + for extra in OPTIONAL_SUBMODULE_ROOT_EXTRAS.get(submodule_name, ()): + extras.setdefault(extra) + for extra in extras: + _install_root_extra(extra) + + def _get_installed_distribution_requirements(python_exe: str, distribution_name: str) -> list[str]: """Return installed ``Requires-Dist`` requirements for a distribution.""" probe = """import importlib.metadata @@ -542,6 +644,18 @@ def _install_isaacsim() -> None: "teleop": ("isaaclab_teleop",), } +# Root pyproject optional-dependency groups that carry the third-party runtime +# requirements for each optional submodule (the submodules themselves no longer +# declare dependencies). Derived from OPTIONAL_ISAACLAB_SUBMODULES rather than +# redefined: each ``isaaclab_`` source dir maps to the same-named root +# extra (so ``mimic`` pulls in the ``teleop`` stack as well, matching the +# editable-install behavior it replaces). The extra names are validated against +# the root pyproject by :func:`_root_extra_dependencies` at install time. +OPTIONAL_SUBMODULE_ROOT_EXTRAS: dict[str, tuple[str, ...]] = { + submodule: tuple(directory.removeprefix("isaaclab_") for directory in directories) + for submodule, directories in OPTIONAL_ISAACLAB_SUBMODULES.items() +} + # Extra feature sets that install optional heavy dependencies on top of the # always-installed core submodules. Each name corresponds to one or more # 'pip install --editable path[extra]' calls against packages already in the @@ -638,12 +752,8 @@ def _install_contrib_extra_dependencies(selector: str) -> None: ) return - python_exe = extract_python_exe() - pip_cmd = get_pip_command(python_exe) - source_dir = ISAACLAB_ROOT / "source" - print_info(f"Installing contrib optional dependencies: {selector}...") - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_contrib[{selector}]"]) + _install_root_extra(selector) def _install_ov_extra_dependencies(selector: str) -> None: @@ -660,10 +770,6 @@ def _install_ov_extra_dependencies(selector: str) -> None: ) return - python_exe = extract_python_exe() - pip_cmd = get_pip_command(python_exe) - source_dir = ISAACLAB_ROOT / "source" - selectors = {item.strip().lower() for item in selector.split(",") if item.strip()} valid_selectors = {"all", "ovrtx", "ovphysx"} unknown_selectors = selectors - valid_selectors @@ -674,19 +780,20 @@ def _install_ov_extra_dependencies(selector: str) -> None: ) if "all" in selectors: selectors.update({"ovrtx", "ovphysx"}) + # The ov[ovrtx] selector maps to the root 'rtx' extra; ov[ovphysx] to 'ov'. if "ovrtx" in selectors: print_info("Installing OVRTX optional dependency...") - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_ov[ovrtx]"]) + _install_root_extra("rtx") if "ovphysx" in selectors: print_info("Installing OVPhysX optional dependency...") - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_ovphysx[ovphysx]"]) + _install_root_extra("ov") def _install_extra_feature(feature_name: str, selector: str = "") -> None: """Install optional extra dependencies for a feature set. - Each feature maps to one or more editable installs with extras applied to - packages that are already part of the core set. + Each feature maps the CLI token to one or more root ``optional-dependencies`` + groups and installs their third-party requirements. Args: feature_name: One of :data:`VALID_EXTRA_FEATURES`. @@ -694,29 +801,32 @@ def _install_extra_feature(feature_name: str, selector: str = "") -> None: ``rl[rsl-rl]``). When empty a sensible default is chosen per feature (``"all"`` for ``rl`` and ``visualizer``). """ - python_exe = extract_python_exe() - pip_cmd = get_pip_command(python_exe) - source_dir = ISAACLAB_ROOT / "source" - if feature_name == "contrib": _install_contrib_extra_dependencies(selector) elif feature_name == "newton": if selector: - print_warning(f"'newton' does not support selectors (got '{selector}'). Installing all newton extras.") - print_info( - "Installing newton extras (newton[sim], pyglet, PyOpenGL-accelerate, imgui-bundle, typing-extensions)..." - ) - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_newton[all]"]) - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_physx[newton]"]) - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_visualizers[newton]"]) + print_warning(f"'newton' does not support selectors (got '{selector}').") + # The Newton physics engine and its interactive viewer GUI (imgui-bundle, + # typing-extensions) are part of the base install; this token is a no-op. + print_info("Newton (engine + viewer) is part of the base install; nothing to install.") elif feature_name == "rl": extra = selector if selector else "all" + # rl[all] installs every RL framework extra; other selectors map by name + # (rsl_rl -> rsl-rl, skrl, sb3, rl-games). + frameworks = {"sb3", "skrl", "rl-games", "rsl-rl"} if extra == "all" else {extra.replace("_", "-")} print_info(f"Installing RL framework extras: {extra}...") - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_rl[{extra}]"]) + for framework in sorted(frameworks): + _install_root_extra(framework) elif feature_name == "visualizer": extra = selector if selector else "all" + backends = {"newton", "rerun", "viser"} if extra == "all" else {extra} print_info(f"Installing visualizer extras: {extra}...") - run_command(pip_cmd + ["install", "--editable", f"{source_dir}/isaaclab_visualizers[{extra}]"]) + for backend in sorted(backends): + # 'kit' (Omniverse-provided) and 'newton' (part of the base install) + # have no extra to install. + if backend in {"kit", "newton"}: + continue + _install_root_extra(backend) elif feature_name == "ov": _install_ov_extra_dependencies(selector) else: @@ -880,15 +990,15 @@ def command_install(install_type: str = "all") -> None: optional submodules and extra features. Valid tokens: - Optional submodules: ``mimic``, ``teleop`` - - Extra features: ``contrib[rlinf]``, ``newton``, ``rl[]``, + - Extra features: ``contrib[rlinf]``, ``rl[]``, ``visualizer[]``, ``ov[ovrtx|ovphysx|all]`` - Special: ``isaacsim`` Examples:: - ./isaaclab.sh -i newton,rl[rsl-rl] + ./isaaclab.sh -i rl[rsl-rl] ./isaaclab.sh -i mimic,visualizer[rerun] - ./isaaclab.sh -i teleop,rl[skrl],newton + ./isaaclab.sh -i teleop,rl[skrl],ov[ovrtx] """ # Install system dependencies first. @@ -910,6 +1020,8 @@ def command_install(install_type: str = "all") -> None: extra_features: list[tuple[str, str]] = [] # List of (submodule_name, selector) tuples for optional submodule extras. optional_submodule_extra_dependencies: list[tuple[str, str]] = [] + # Names of requested optional submodules (used to install their root extras). + requested_optional_submodules: list[str] = [] def append_submodules_once(package_dirs: tuple[str, ...]) -> None: for pkg_dir in package_dirs: @@ -923,6 +1035,7 @@ def append_submodules_once(package_dirs: tuple[str, ...]) -> None: if install_type == "all": for package_dirs in OPTIONAL_ISAACLAB_SUBMODULES.values(): append_submodules_once(package_dirs) + requested_optional_submodules = list(OPTIONAL_ISAACLAB_SUBMODULES) extra_features = [(name, "") for name in sorted(VALID_EXTRA_FEATURES - MANUAL_EXTRA_FEATURES)] elif install_type == "core": # Core only — no optional submodules, no extra features. @@ -944,6 +1057,7 @@ def append_submodules_once(package_dirs: tuple[str, ...]) -> None: install_isaacsim = True elif name in OPTIONAL_ISAACLAB_SUBMODULES: append_submodules_once(OPTIONAL_ISAACLAB_SUBMODULES[name]) + requested_optional_submodules.append(name) if selector: optional_submodule_extra_dependencies.append((name, selector)) elif name in VALID_EXTRA_FEATURES: @@ -1019,6 +1133,11 @@ def append_submodules_once(package_dirs: tuple[str, ...]) -> None: # Install all submodules (core set + any explicitly requested optional ones). _install_isaaclab_submodules(submodules_to_install) + # The submodules no longer declare third-party dependencies; install the + # centralized core requirements (and optional-submodule extras) from the + # root pyproject. torch is excluded — it is handled by _ensure_cuda_torch. + _install_centralized_dependencies(pip_cmd, requested_optional_submodules) + # Install requested optional submodule dependency extras. if optional_submodule_extra_dependencies: print_info("Installing optional submodule dependencies...") diff --git a/source/isaaclab/pyproject.toml b/source/isaaclab/pyproject.toml index 95af22c8f527..576ee52a6d4c 100644 --- a/source/isaaclab/pyproject.toml +++ b/source/isaaclab/pyproject.toml @@ -16,49 +16,10 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["kit", "robotics", "learning", "ai"] requires-python = ">=3.12" -dependencies = [ - # generic - "numpy>=2", - "torch>=2.10", - "onnx>=1.18.0", - "prettytable>=3.3.0", - # devices - "hidapi>=0.14.0", - # reinforcement learning - "gymnasium>=1.2.0", - # procedural-generation - "trimesh", - "pyglet>=2.1.6,<3", - # pytetwild ships only an x86_64 manylinux wheel and its sdist fails to build on - # aarch64 (CMake hardcodes -m64). Gate it on x86_64 so ARM64 docker builds are - # not blocked; tetrahedralize callers degrade gracefully via ImportError when missing. - # Pinned to ==0.2.3: >=0.3 unconditionally imports pyvista at package import time. - "pytetwild==0.2.3 ; platform_machine in 'x86_64 AMD64'", - # image processing - "transformers==4.57.6", - "einops", - "warp-lang==1.14.0", - "matplotlib>=3.10.3", - # keep consistent with isaac sim version - "pillow==12.1.1", - # required by omni.replicator.core S3 backend - "botocore", - # livestream (range chosen to coexist with isaacsim 6.0) - "starlette>=0.46.0,<0.50", - "omniverseclient==2.71.1.7015", - # cross-platform file locking - "filelock", - "lazy_loader>=0.4", - # IK controller (Linux x86_64 + ARM64), requires pinning to 3.1.0 and 0.8.5 to avoid breaking change - "pin ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", - "pin-pink==3.1.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", - "daqp==0.8.5 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64 aarch64 arm64'", - # OpenUSD (kit-less mode) - "usd-core>=25.11,<26.0 ; platform_machine in 'x86_64 AMD64'", - "usd-exchange>=2.2 ; platform_machine in 'x86_64 AMD64 aarch64 arm64'", - # avoid broken hf-xet pre-release cached on NVIDIA Artifactory - "hf-xet>=1.4.1,<2.0.0 ; platform_machine in 'x86_64 AMD64 aarch64 arm64'", -] +# Third-party requirements are centralized in the root pyproject.toml +# (single source of truth for the uv workspace, the wheel builder, and the +# ./isaaclab.sh -i install CLI). +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" @@ -70,36 +31,6 @@ play = "isaaclab.cli:play" train = "isaaclab.cli:train" train_multigpu = "isaaclab.cli:train_multigpu" -[project.optional-dependencies] -test = [ - "pytest", - "pytest-mock", - "junitparser", - "flatdict>=4.1.0", - "flaky", - # coverage>=7.6.1 restored coverage.types.Tracer as a shim; required for - # numba compatibility (numba subclasses Tracer at import time) - "coverage>=7.6.1", -] -isaacsim = ["isaacsim[all,extscache]>=6.0.0"] -all = [ - "isaacsim[all,extscache]>=6.0.0", - "isaaclab_assets", - "isaaclab_contrib", - "isaaclab_experimental", - "isaaclab_mimic", - "isaaclab_newton[all]", - "isaaclab_ov", - "isaaclab_ovphysx", - "isaaclab_physx[newton]", - "isaaclab_ppisp", - "isaaclab_rl[all]", - "isaaclab_tasks", - "isaaclab_tasks_experimental", - "isaaclab_teleop", - "isaaclab_visualizers[all]", -] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab/test/cli/test_install_command_parsing.py b/source/isaaclab/test/cli/test_install_command_parsing.py index 27281ec013f4..787434985eb4 100644 --- a/source/isaaclab/test/cli/test_install_command_parsing.py +++ b/source/isaaclab/test/cli/test_install_command_parsing.py @@ -156,6 +156,9 @@ def test_optional_submodules_not_in_core(self): f"{_INSTALL_MODULE}._install_isaaclab_submodules", f"{_INSTALL_MODULE}._install_extra_feature", f"{_INSTALL_MODULE}._install_optional_submodule_extra_dependencies", + # Centralized dependency installs read the root pyproject and shell out to pip. + f"{_INSTALL_MODULE}._root_core_dependencies", + f"{_INSTALL_MODULE}._install_root_extra", f"{_INSTALL_MODULE}._install_isaacsim", f"{_INSTALL_MODULE}._ensure_cuda_torch", f"{_INSTALL_MODULE}._maybe_preinstall_arm_nlopt", diff --git a/source/isaaclab/test/cli/test_install_commands.py b/source/isaaclab/test/cli/test_install_commands.py index f773d9f026cf..9bdf09ec8540 100644 --- a/source/isaaclab/test/cli/test_install_commands.py +++ b/source/isaaclab/test/cli/test_install_commands.py @@ -363,10 +363,10 @@ class TestEnsureCudaTorch: # ---- x86 scenarios ------------------------------------------------------- def test_x86_skips_install_when_correct_version_present(self, tmp_path): - """x86: torch 2.10.0+cu128 already installed → pip install is not called.""" + """x86: torch 2.11.0+cu128 already installed → pip install is not called.""" py = str(tmp_path / "python") pip_cmd = [py, "-m", "pip"] - pip_show_out = "Name: torch\nVersion: 2.10.0+cu128\n" + pip_show_out = "Name: torch\nVersion: 2.11.0+cu128\n" with ( mock.patch("isaaclab.cli.commands.install.extract_python_exe", return_value=py), @@ -411,7 +411,7 @@ def test_x86_reinstalls_when_wrong_cuda_tag(self, tmp_path): def _run(cmd, **kwargs): calls.append(list(cmd)) - stdout = "Name: torch\nVersion: 2.10.0+cu130\n" if "show" in cmd else "" + stdout = "Name: torch\nVersion: 2.11.0+cu130\n" if "show" in cmd else "" return _cp(0, stdout) with ( @@ -452,10 +452,10 @@ def _run(cmd, **kwargs): assert "cu130" in combined def test_arm_skips_install_when_correct_version_present(self, tmp_path): - """ARM: torch 2.10.0+cu130 already installed → pip install is not called.""" + """ARM: torch 2.11.0+cu130 already installed → pip install is not called.""" py = str(tmp_path / "python") pip_cmd = [py, "-m", "pip"] - pip_show_out = "Name: torch\nVersion: 2.10.0+cu130\n" + pip_show_out = "Name: torch\nVersion: 2.11.0+cu130\n" with ( mock.patch("isaaclab.cli.commands.install.extract_python_exe", return_value=py), @@ -475,7 +475,7 @@ def test_arm_reinstalls_when_wrong_cuda_tag(self, tmp_path): def _run(cmd, **kwargs): calls.append(list(cmd)) - stdout = "Name: torch\nVersion: 2.10.0+cu128\n" if "show" in cmd else "" + stdout = "Name: torch\nVersion: 2.11.0+cu128\n" if "show" in cmd else "" return _cp(0, stdout) with ( diff --git a/source/isaaclab/test/cli/test_uv_run_pyproject.py b/source/isaaclab/test/cli/test_uv_run_pyproject.py index ab9194d3f9e3..6dd4adaa5d84 100644 --- a/source/isaaclab/test/cli/test_uv_run_pyproject.py +++ b/source/isaaclab/test/cli/test_uv_run_pyproject.py @@ -38,39 +38,96 @@ def test_uv_run_extra_names_match_documented_workflow(): assert documented_extras <= set(optional_dependencies) -def test_uv_run_keeps_modular_extras_without_isaacsim(): - """The root dev project keeps local module extras but leaves Isaac Sim opt-in out.""" +def test_uv_run_exposes_centralized_feature_extras(): + """The root project centralizes optional third-party deps into named extras.""" optional_dependencies = _root_pyproject()["project"]["optional-dependencies"] + # Feature extras a user can activate with ``uv run --extra``. expected_extras = { - "contrib": ["isaaclab-contrib"], - "mimic": ["isaaclab-mimic"], - "newton": ["isaaclab-newton[all]", "isaaclab-physx[newton]", "isaaclab-visualizers[newton]"], - "ov": ["isaaclab-ovphysx[ovphysx]"], - "rl": ["isaaclab-rl[rsl-rl]"], - "rl-all": ["isaaclab-rl[all]"], - "rtx": ["isaaclab-ov[ovrtx]"], - "all": [ - "isaaclab-mimic", - "isaaclab-newton[all]", - "isaaclab-physx[newton]", - "isaaclab-ppisp", - "isaaclab-rl[all]", - "isaaclab-visualizers[all]", - ], + "test", + "sb3", + "skrl", + "rl-games", + "rsl-rl", + "viser", + "rerun", + "ov", + "rtx", + "mimic", + "teleop", + "rlinf", + "all", } + assert expected_extras <= set(optional_dependencies) - assert optional_dependencies == expected_extras - assert "isaacsim" not in optional_dependencies + # The Newton viewer GUI is part of the base install, so there is no ``newton`` extra. + assert "newton" not in optional_dependencies + + # Concrete third-party deps live in the extras (not subpackage self-references). + # OVPhysX and OVRTX are separate extras, selectable via ``ov[ovphysx]`` / ``ov[ovrtx]``. + assert any(dep.startswith("skrl") for dep in optional_dependencies["skrl"]) + assert any(dep.startswith("ovphysx") for dep in optional_dependencies["ov"]) + assert any(dep.startswith("ovrtx") for dep in optional_dependencies["rtx"]) + + +def test_version_single_source_matches_literal_pins(): + """``[tool.isaaclab.versions]`` is the single source for externally-pinned versions. + + TOML cannot interpolate, so the literal pins in ``[project.optional-dependencies]`` + and ``[tool.uv].constraint-dependencies`` must mirror the table exactly. This test + fails if any of them drift apart. + """ + pyproject = _root_pyproject() + versions = pyproject["tool"]["isaaclab"]["versions"] + optional = pyproject["project"]["optional-dependencies"] + constraints = pyproject["tool"]["uv"]["constraint-dependencies"] + + # Isaac Sim extra mirrors the table. + assert optional["isaacsim"] == [f"isaacsim[all,extscache]=={versions['isaacsim']}"] + + # OV extras mirror the table (ovphysx exact pin in ``ov``, ovrtx range spec in ``rtx``). + assert f"ovphysx=={versions['ovphysx']}" in optional["ov"] + assert f"ovrtx{versions['ovrtx']}" in optional["rtx"] + + # uv torch-stack constraints mirror the table. + for package in ("torch", "torchvision", "torchaudio"): + assert f"{package}=={versions[package]}" in constraints + + +def test_uv_run_isaacsim_extra_is_conflict_forked(): + """Isaac Sim is an opt-in uv workspace extra, forked away from clashing extras. + + PhysX/Isaac Sim is never a base dependency, but it must be a real + ``optional-dependencies`` extra so ``uv run --extra isaacsim`` resolves. Its + exact pins clash with several other extras, so it is declared in + ``[tool.uv].conflicts`` (forked resolution) rather than co-resolved with them. + """ + pyproject = _root_pyproject() + project = pyproject["project"] + base_dependency_names = {re.split(r"[\s<>=!~\[;]", dep, maxsplit=1)[0] for dep in project["dependencies"]} + + # PhysX/Isaac Sim is opt-in, never installed by the bare ``uv run``. + assert "isaacsim" not in base_dependency_names + # ...but it is a workspace extra so ``uv run --extra isaacsim`` works. + assert "isaacsim" in project["optional-dependencies"] + assert any(dep.startswith("isaacsim[") for dep in project["optional-dependencies"]["isaacsim"]) + # The legacy wheel-only table is gone (isaacsim now lives in the extras). + assert "wheel-extras" not in pyproject.get("tool", {}).get("isaaclab", {}) + + # isaacsim is forked away from every extra whose pins clash with it. + conflict_groups = [{entry["extra"] for entry in group} for group in pyproject["tool"]["uv"]["conflicts"]] + for extra in ("teleop", "ov", "viser", "mimic", "all", "test"): + assert {"isaacsim", extra} in conflict_groups, f"isaacsim must declare a conflict with '{extra}'" def test_uv_run_base_dependencies_cover_newton_rsl_rl_training(): - """The documented bare ``uv run train`` command needs Newton and RSL-RL extras.""" + """The documented bare ``uv run train`` command needs Newton and RSL-RL in core.""" dependencies = _root_pyproject()["project"]["dependencies"] - assert "isaaclab-newton[all]" in dependencies - assert "isaaclab-physx[newton]" in dependencies - assert "isaaclab-rl[rsl-rl]" in dependencies + # Newton is the default physics engine and RSL-RL the default training library, + # so both ship as core third-party requirements (not opt-in extras). + assert any(dep.startswith("newton[sim]") for dep in dependencies) + assert any(dep.startswith("rsl-rl-lib") for dep in dependencies) def test_uv_run_uses_managed_python(): diff --git a/source/isaaclab/test/cli/test_wheel_builder_metadata.py b/source/isaaclab/test/cli/test_wheel_builder_metadata.py index c04db628aa7a..09f97ca51395 100644 --- a/source/isaaclab/test/cli/test_wheel_builder_metadata.py +++ b/source/isaaclab/test/cli/test_wheel_builder_metadata.py @@ -3,10 +3,12 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Tests for wheel-builder package metadata.""" +"""Tests for wheel-builder package metadata generated from the root pyproject.""" from __future__ import annotations +import subprocess +import sys from pathlib import Path import tomllib @@ -20,95 +22,62 @@ def _repo_root() -> Path: raise RuntimeError("Could not find Isaac Lab repository root.") -def _load_toml(relative_path: str) -> dict: - """Load a TOML file from the Isaac Lab repository root.""" - with (_repo_root() / relative_path).open("rb") as f: +def _root_rsl_rl_pin() -> str: + """Return the ``rsl-rl-lib`` pin declared by the root ``pyproject.toml`` core deps.""" + with (_repo_root() / "pyproject.toml").open("rb") as f: + data = tomllib.load(f) + for dependency in data["project"]["dependencies"]: + if dependency.startswith("rsl-rl-lib=="): + return dependency + raise AssertionError("Could not find rsl-rl-lib pin in the root pyproject.toml") + + +def _generate_wheel_pyproject(tmp_path: Path) -> dict: + """Run ``gen_pyproject.py`` against the root pyproject and return the parsed result.""" + repo_root = _repo_root() + output = tmp_path / "pyproject.toml" + subprocess.run( + [ + sys.executable, + str(repo_root / "tools/wheel_builder/gen_pyproject.py"), + str(repo_root / "pyproject.toml"), + str(output), + "3.0.0", + ], + check=True, + ) + with output.open("rb") as f: return tomllib.load(f) -def _single_dependency(dependencies: list[str], prefix: str, source: str) -> str: - """Return the only dependency in a list matching the provided prefix.""" - matches = [dependency for dependency in dependencies if dependency.startswith(prefix)] - assert len(matches) == 1, f"Expected one {prefix!r} dependency in {source}, got {matches}" - return matches[0] - - -def _wheel_builder_dependencies_by_extra() -> dict[str, list[str]]: - """Return the wheel-builder optional dependencies grouped by extra name.""" - packages = _load_toml("tools/wheel_builder/res/python_packages.toml") - optional_dependencies = packages["isaaclab"]["pyproject"]["optional-dependencies"]["all"] - return {name: dependencies for entry in optional_dependencies for name, dependencies in entry.items()} +def test_wheel_builder_drops_workspace_members(tmp_path): + """The generated wheel metadata must not depend on the bundled ``isaaclab*`` packages.""" + generated = _generate_wheel_pyproject(tmp_path) + dependencies = generated["project"]["dependencies"] + assert not [dep for dep in dependencies if dep.lower().startswith("isaaclab")] -def _rsl_rl_pin_from_pyproject() -> str: - """Return the ``rsl-rl-lib`` pin declared by ``source/isaaclab_rl/pyproject.toml``.""" - dependencies = _load_toml("source/isaaclab_rl/pyproject.toml")["project"]["optional-dependencies"]["rsl-rl"] - return _single_dependency(dependencies, "rsl-rl-lib==", "source/isaaclab_rl/pyproject.toml") +def test_wheel_builder_includes_isaacsim_extra(tmp_path): + """The ``isaacsim`` extra must ship in the generated wheel metadata.""" + generated = _generate_wheel_pyproject(tmp_path) + optional_dependencies = generated["project"]["optional-dependencies"] -def _newton_pin_from_pyproject(relative_path: str, extra_name: str) -> str: - """Return the ``newton[sim]`` direct URL pin declared by a package extra.""" - dependencies = _load_toml(relative_path)["project"]["optional-dependencies"][extra_name] - return _single_dependency( - dependencies, - "newton[sim] @ git+https://github.com/newton-physics/newton.git@", - f"{relative_path}[{extra_name}]", - ) - + assert "isaacsim" in optional_dependencies + assert any(dep.startswith("isaacsim[") for dep in optional_dependencies["isaacsim"]) -def _warp_pin_from_core_pyproject() -> str: - """Return the core ``warp-lang`` pin declared by ``source/isaaclab/pyproject.toml``.""" - dependencies = _load_toml("source/isaaclab/pyproject.toml")["project"]["dependencies"] - return _single_dependency(dependencies, "warp-lang==", "source/isaaclab/pyproject.toml") +def test_wheel_builder_rsl_rl_pin_matches_root_pyproject(tmp_path): + """The bundled wheel metadata must install the RSL-RL version declared at the root.""" + expected_pin = _root_rsl_rl_pin() + generated = _generate_wheel_pyproject(tmp_path) -def test_wheel_builder_rsl_rl_pin_matches_source_package(): - """The bundled wheel metadata must install the RSL-RL version required by training scripts.""" - expected_pin = _rsl_rl_pin_from_pyproject() - dependencies_by_extra = _wheel_builder_dependencies_by_extra() + # RSL-RL is a core dependency (default training library) and also exposed as an extra. + core_pins = [dep for dep in generated["project"]["dependencies"] if dep.startswith("rsl-rl-lib==")] + assert core_pins == [expected_pin] + optional_dependencies = generated["project"]["optional-dependencies"] + # RSL-RL ships in its own ``rsl-rl`` extra and in the aggregate ``all`` extra. for extra_name in ("rsl-rl", "all"): - rsl_rl_pin = _single_dependency(dependencies_by_extra[extra_name], "rsl-rl-lib==", extra_name) - assert rsl_rl_pin == expected_pin - - -def test_wheel_builder_newton_pin_matches_source_packages(): - """The bundled wheel metadata must install the Newton revision used by source packages.""" - expected_pin = _newton_pin_from_pyproject("source/isaaclab_newton/pyproject.toml", "all") - source_pins = [ - _newton_pin_from_pyproject("source/isaaclab_physx/pyproject.toml", "newton"), - _newton_pin_from_pyproject("source/isaaclab_visualizers/pyproject.toml", "newton"), - _newton_pin_from_pyproject("source/isaaclab_visualizers/pyproject.toml", "rerun"), - _newton_pin_from_pyproject("source/isaaclab_visualizers/pyproject.toml", "viser"), - _newton_pin_from_pyproject("source/isaaclab_visualizers/pyproject.toml", "all"), - ] - assert source_pins == [expected_pin] * len(source_pins) - - wheel_newton_pin = _single_dependency( - _wheel_builder_dependencies_by_extra()["newton"], - "newton[sim] @ git+https://github.com/newton-physics/newton.git@", - "tools/wheel_builder/res/python_packages.toml[newton]", - ) - assert wheel_newton_pin == expected_pin - - -def test_wheel_builder_warp_pin_matches_core_package(): - """The bundled wheel metadata must keep Warp aligned with the core package pin.""" - expected_pin = _warp_pin_from_core_pyproject() - packages = _load_toml("tools/wheel_builder/res/python_packages.toml") - wheel_core_dependencies = packages["isaaclab"]["pyproject"]["dependencies"]["all"] - dependencies_by_extra = _wheel_builder_dependencies_by_extra() - - wheel_core_pin = _single_dependency( - wheel_core_dependencies, - "warp-lang==", - "tools/wheel_builder/res/python_packages.toml[dependencies]", - ) - wheel_newton_pin = _single_dependency( - dependencies_by_extra["newton"], - "warp-lang==", - "tools/wheel_builder/res/python_packages.toml[newton]", - ) - - assert wheel_core_pin == expected_pin - assert wheel_newton_pin == expected_pin + rsl_rl_pins = [dep for dep in optional_dependencies[extra_name] if dep.startswith("rsl-rl-lib==")] + assert rsl_rl_pins == [expected_pin] diff --git a/source/isaaclab/test/install_ci/utils.py b/source/isaaclab/test/install_ci/utils.py index 793219b6d8d0..f2e3719392f8 100644 --- a/source/isaaclab/test/install_ci/utils.py +++ b/source/isaaclab/test/install_ci/utils.py @@ -198,6 +198,19 @@ def cuda_torch_index_url() -> str: return "https://download.pytorch.org/whl/cu128" +def pinned_torch_specs() -> list[str]: + """Return the pinned ``torch``/``torchvision`` install specs from the repo pyproject. + + Reads ``[tool.isaaclab.versions]`` (the single source of truth) so these + install commands track the same versions as the docs and the install CLI. + """ + import tomllib + + with (find_isaaclab_root() / "pyproject.toml").open("rb") as fd: + versions = tomllib.load(fd)["tool"]["isaaclab"]["versions"] + return [f"torch=={versions['torch']}", f"torchvision=={versions['torchvision']}"] + + def aarch64_isaacsim_env() -> dict[str, str]: """Return env vars required to import ``isaacsim`` from a ``uv pip`` install on aarch64. diff --git a/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole.py b/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole.py index eab7d5ba9372..398241e44f96 100644 --- a/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole.py +++ b/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole.py @@ -10,7 +10,8 @@ - uv pip install [all,isaacsim] --extra-index-url https://pypi.nvidia.com --index-strategy unsafe-best-match --prerelease=allow - uv pip install --reinstall-package torch --reinstall-package torchvision - torch==2.10.0 torchvision==0.25.0 --index-url + torch== torchvision== --index-url + (versions read from [tool.isaaclab.versions] in the root pyproject.) (cu128 on x86_64, cu130 on aarch64; per docs/source/setup/installation/pip_installation.rst. Reinstall AFTER the wheel install: unsafe-best-match re-resolves torch from PyPI to CPU.) - (aarch64 only) export LD_PRELOAD=/lib/aarch64-linux-gnu/libgomp.so.1 @@ -24,7 +25,7 @@ import shutil import pytest -from utils import UV_Mixin, aarch64_isaacsim_env, cuda_torch_index_url +from utils import UV_Mixin, aarch64_isaacsim_env, cuda_torch_index_url, pinned_torch_specs _TRAIN_CMD = [ "train", @@ -93,8 +94,9 @@ def test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole(self, isaaclab_roo ) # 2. uv pip install --reinstall-package torch --reinstall-package torchvision - # torch==2.10.0 torchvision==0.25.0 --index-url - # cu128 on x86_64, cu130 on aarch64 (e.g. GB10 / DGX Spark with CUDA capability 12.x). + # torch== torchvision== --index-url + # (versions from [tool.isaaclab.versions]; cu128 on x86_64, cu130 on aarch64, + # e.g. GB10 / DGX Spark with CUDA capability 12.x). # --reinstall-package forces uv to swap the CPU torch installed above with the CUDA build. result = self.run_in_uv_env( [ @@ -105,8 +107,7 @@ def test_uv_pip_install_isaaclab_all_isaacsim_trains_cartpole(self, isaaclab_roo "torch", "--reinstall-package", "torchvision", - "torch==2.10.0", - "torchvision==0.25.0", + *pinned_torch_specs(), "--index-url", cuda_torch_index_url(), ], diff --git a/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_isaacsim_imports_simulation_context.py b/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_isaacsim_imports_simulation_context.py index 4b3851e09623..0d2d1361001a 100644 --- a/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_isaacsim_imports_simulation_context.py +++ b/source/isaaclab/test/install_ci/uv_pip/test_uv_pip_install_isaaclab_isaacsim_imports_simulation_context.py @@ -10,7 +10,8 @@ - uv pip install [isaacsim] --extra-index-url https://pypi.nvidia.com --index-strategy unsafe-best-match --prerelease=allow - uv pip install --reinstall-package torch --reinstall-package torchvision - torch==2.10.0 torchvision==0.25.0 --index-url + torch== torchvision== --index-url + (versions read from [tool.isaaclab.versions] in the root pyproject.) (cu128 on x86_64, cu130 on aarch64; per docs/source/setup/installation/pip_installation.rst. Reinstall AFTER the wheel install: unsafe-best-match re-resolves torch from PyPI to CPU.) - (aarch64 only) export LD_PRELOAD=/lib/aarch64-linux-gnu/libgomp.so.1 @@ -24,7 +25,7 @@ import shutil import pytest -from utils import UV_Mixin, aarch64_isaacsim_env, cuda_torch_index_url +from utils import UV_Mixin, aarch64_isaacsim_env, cuda_torch_index_url, pinned_torch_specs @pytest.mark.install_path_uv_pip @@ -82,8 +83,7 @@ def _install_wheel(self, isaaclab_root, wheel): "torch", "--reinstall-package", "torchvision", - "torch==2.10.0", - "torchvision==0.25.0", + *pinned_torch_specs(), "--index-url", cuda_torch_index_url(), ], diff --git a/source/isaaclab_contrib/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_contrib/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_contrib/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_contrib/pyproject.toml b/source/isaaclab_contrib/pyproject.toml index 00dc87b8d4bd..aa741163a9b7 100644 --- a/source/isaaclab_contrib/pyproject.toml +++ b/source/isaaclab_contrib/pyproject.toml @@ -16,26 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["kit", "robotics", "assets", "isaaclab"] requires-python = ">=3.12" +# Third-party requirements are centralized in the root pyproject.toml. dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -rlinf = [ - "ray[default]>=2.47.0", - "av>=12.3.0", - "numpydantic>=1.7.0", - "albumentations>=1.4.18", - "decord2", - "dm_tree>=0.1.8", - "diffusers>=0.35.0", - "timm>=1.0.14", - "peft>=0.17.0", - "pandas", -] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_mimic/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_mimic/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_mimic/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_mimic/pyproject.toml b/source/isaaclab_mimic/pyproject.toml index 6cefbe49ad47..b1384927c368 100644 --- a/source/isaaclab_mimic/pyproject.toml +++ b/source/isaaclab_mimic/pyproject.toml @@ -16,14 +16,9 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["extension", "template", "isaaclab"] requires-python = ">=3.12" -dependencies = [ - # jupyter notebook - "ipywidgets>=8.1.5", - # data collection - "h5py>=3.15.0", - # data augmentation (Linux only) - "robomimic @ git+https://github.com/ARISE-Initiative/robomimic.git@v0.4.0 ; sys_platform == 'linux'", -] +# Third-party requirements are centralized in the root pyproject.toml +# (installed via the ``mimic`` extra). +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" diff --git a/source/isaaclab_newton/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_newton/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_newton/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_newton/pyproject.toml b/source/isaaclab_newton/pyproject.toml index 38cbf741d7d6..8c190f72fda3 100644 --- a/source/isaaclab_newton/pyproject.toml +++ b/source/isaaclab_newton/pyproject.toml @@ -16,20 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "simulation", "newton"] requires-python = ">=3.12" +# Third-party requirements are centralized in the root pyproject.toml. dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -all = [ - "prettytable>=3.3.0", - "PyOpenGL-accelerate>=3.1.0", - "pyglet>=2.1.6,<3", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", -] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_ov/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_ov/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_ov/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_ov/pyproject.toml b/source/isaaclab_ov/pyproject.toml index 3831b4bdf911..cfa117842649 100644 --- a/source/isaaclab_ov/pyproject.toml +++ b/source/isaaclab_ov/pyproject.toml @@ -16,16 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "simulation", "rendering", "ovrtx", "omniverse"] requires-python = ">=3.12" +# Third-party requirements are centralized in the root pyproject.toml. dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -ovrtx = ["ovrtx>=0.3.0,<0.4.0"] -all = ["ovrtx>=0.3.0,<0.4.0"] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_ovphysx/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_ovphysx/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_ovphysx/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_ovphysx/pyproject.toml b/source/isaaclab_ovphysx/pyproject.toml index 4619ddb6c17a..fd784d094077 100644 --- a/source/isaaclab_ovphysx/pyproject.toml +++ b/source/isaaclab_ovphysx/pyproject.toml @@ -16,15 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "simulation", "ovphysx", "tensorbindingsapi"] requires-python = ">=3.12" +# Third-party requirements are centralized in the root pyproject.toml. dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -ovphysx = ["ovphysx==0.4.13"] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_physx/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_physx/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_physx/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_physx/pyproject.toml b/source/isaaclab_physx/pyproject.toml index d949e646bcdc..6294a0cf4463 100644 --- a/source/isaaclab_physx/pyproject.toml +++ b/source/isaaclab_physx/pyproject.toml @@ -16,17 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "simulation", "physx"] requires-python = ">=3.12" +# Third-party requirements are centralized in the root pyproject.toml. dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -newton = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", -] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_rl/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_rl/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_rl/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_rl/pyproject.toml b/source/isaaclab_rl/pyproject.toml index 348278bd75b8..613220eee339 100644 --- a/source/isaaclab_rl/pyproject.toml +++ b/source/isaaclab_rl/pyproject.toml @@ -16,48 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "rl", "wrappers", "learning"] requires-python = ">=3.12" -dependencies = [ - "numpy", - "torch>=2.10", - "torchvision>=0.25.0", - "protobuf>=4.25.8,!=5.26.0", - # configuration management - "hydra-core", - # data collection - "h5py>=3.15.0", - # basic logger - "tensorboard", - # video recording - "moviepy", - "packaging<24", - "tqdm>=4.67.1", -] +# Third-party requirements are centralized in the root pyproject.toml. +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -sb3 = ["stable-baselines3>=2.6", "tqdm", "rich"] -skrl = ["skrl>=2.1.0"] -rl-games = [ - "aiohttp==3.13.3", - "rl-games @ git+https://github.com/isaac-sim/rl_games.git@python3.11", - "gym", - "standard-distutils", -] -rsl-rl = ["rsl-rl-lib==5.0.1", "onnxscript>=0.5"] -all = [ - "stable-baselines3>=2.6", "tqdm", "rich", - "skrl>=2.1.0", - "aiohttp==3.13.3", - "rl-games @ git+https://github.com/isaac-sim/rl_games.git@python3.11", - "gym", - "standard-distutils", - "rsl-rl-lib==5.0.1", - "onnxscript>=0.5", -] - [tool.setuptools] include-package-data = true diff --git a/source/isaaclab_tasks/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_tasks/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_tasks/pyproject.toml b/source/isaaclab_tasks/pyproject.toml index 32d6e948075d..e230365eebd2 100644 --- a/source/isaaclab_tasks/pyproject.toml +++ b/source/isaaclab_tasks/pyproject.toml @@ -16,13 +16,8 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "rl", "il", "learning"] requires-python = ">=3.12" -dependencies = [ - "numpy>=2", - "torch>=2.10", - "torchvision>=0.25.0", - "protobuf>=4.25.8,!=5.26.0", - "tensorboard", -] +# Third-party requirements are centralized in the root pyproject.toml. +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" diff --git a/source/isaaclab_tasks/test/core/test_train_scripts_deterministic.py b/source/isaaclab_tasks/test/core/test_train_scripts_deterministic.py index 677e698ec78e..aa2d29c5f8a4 100644 --- a/source/isaaclab_tasks/test/core/test_train_scripts_deterministic.py +++ b/source/isaaclab_tasks/test/core/test_train_scripts_deterministic.py @@ -43,7 +43,7 @@ def _module_string_constant(tree: ast.AST, name: str) -> str: def _source_skrl_min_version() -> Version: - with (REPO_ROOT / "source/isaaclab_rl/pyproject.toml").open("rb") as f: + with (REPO_ROOT / "pyproject.toml").open("rb") as f: data = tomllib.load(f) for dependency in data["project"]["optional-dependencies"]["skrl"]: @@ -58,7 +58,7 @@ def _source_skrl_min_version() -> Version: if lower_bounds: return max(lower_bounds) - raise AssertionError("Could not find skrl lower bound in source/isaaclab_rl/pyproject.toml") + raise AssertionError("Could not find skrl lower bound in pyproject.toml") def _called_name(call: ast.Call) -> str | None: diff --git a/source/isaaclab_tasks_experimental/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_tasks_experimental/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_tasks_experimental/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_tasks_experimental/pyproject.toml b/source/isaaclab_tasks_experimental/pyproject.toml index 7f48fe4af2f5..dc8e8e229563 100644 --- a/source/isaaclab_tasks_experimental/pyproject.toml +++ b/source/isaaclab_tasks_experimental/pyproject.toml @@ -16,9 +16,8 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "rl", "il", "learning"] requires-python = ">=3.12" -dependencies = [ - "isaaclab_tasks", -] +# Third-party requirements are centralized in the root pyproject.toml. +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" diff --git a/source/isaaclab_teleop/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_teleop/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_teleop/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_teleop/pyproject.toml b/source/isaaclab_teleop/pyproject.toml index 2a2ec6f86d82..2a00a1397222 100644 --- a/source/isaaclab_teleop/pyproject.toml +++ b/source/isaaclab_teleop/pyproject.toml @@ -16,11 +16,9 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["kit", "robotics", "teleoperation", "xr", "isaaclab"] requires-python = ">=3.12" -dependencies = [ - # IsaacTeleop is only available on Linux x86_64 - "isaacteleop[retargeters,ui,cloudxr]~=1.3.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64'", - "dex-retargeting==0.5.0 ; platform_system == 'Linux' and platform_machine in 'x86_64 AMD64'", -] +# Third-party requirements are centralized in the root pyproject.toml +# (installed via the ``teleop`` extra). +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" diff --git a/source/isaaclab_visualizers/changelog.d/centralize-deps-root-pyproject.skip b/source/isaaclab_visualizers/changelog.d/centralize-deps-root-pyproject.skip new file mode 100644 index 000000000000..ed67a1a5272d --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/centralize-deps-root-pyproject.skip @@ -0,0 +1,4 @@ +# No changelog entry / version bump: third-party dependency declarations +# were relocated to the root pyproject.toml (single source of truth). The +# published isaaclab wheel is monolithic and unaffected; the user-facing change +# is recorded in source/isaaclab/changelog.d/centralize-deps-root-pyproject.rst. diff --git a/source/isaaclab_visualizers/pyproject.toml b/source/isaaclab_visualizers/pyproject.toml index 3f632d689f04..a33ebc8d18eb 100644 --- a/source/isaaclab_visualizers/pyproject.toml +++ b/source/isaaclab_visualizers/pyproject.toml @@ -16,47 +16,13 @@ authors = [{name = "Isaac Lab Project Developers"}] maintainers = [{name = "Isaac Lab Project Developers"}] keywords = ["robotics", "simulation", "visualization"] requires-python = ">=3.12" -dependencies = [ - "isaaclab", - "numpy", -] +# Third-party requirements are centralized in the root pyproject.toml. +dependencies = [] [project.urls] Homepage = "https://github.com/isaac-sim/IsaacLab" Repository = "https://github.com/isaac-sim/IsaacLab" -[project.optional-dependencies] -kit = [] -newton = [ - "warp-lang", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", - "PyOpenGL-accelerate", - "pyglet>=2.1.6,<3", - "imgui-bundle>=1.92.5", - "typing-extensions>=4.15.0", -] -rerun = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", - "rerun-sdk>=0.29.0", - "pyarrow==22.0.0", -] -viser = [ - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", - "viser>=1.0.16", -] -all = [ - "imgui-bundle>=1.92.5", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", - "PyOpenGL-accelerate", - "pyglet>=2.1.6,<3", - # Match rerun-sdk's supported Arrow stack and avoid resolver drift across environments. - "pyarrow==22.0.0", - "rerun-sdk>=0.29.0", - "typing-extensions>=4.15.0", - "viser>=1.0.16", - "warp-lang", -] - [tool.setuptools] include-package-data = true diff --git a/tools/wheel_builder/build.sh b/tools/wheel_builder/build.sh index 030cf537489a..1c9f22685b53 100755 --- a/tools/wheel_builder/build.sh +++ b/tools/wheel_builder/build.sh @@ -85,8 +85,8 @@ find "$BUILD_DIR/src" -name "*.pyc" -delete 2>/dev/null || true cp "$SELF_DIR/res/__init__.py" "$BUILD_DIR/src/isaaclab/" cp "$SELF_DIR/res/__main__.py" "$BUILD_DIR/src/isaaclab/" -# 3. Generate pyproject.toml with dependencies from python_packages.toml -python3 "$SELF_DIR/gen_pyproject.py" "$SELF_DIR/res/python_packages.toml" "$BUILD_DIR/pyproject.toml" "$WHEEL_VERSION" +# 3. Generate pyproject.toml with dependencies from the root pyproject.toml +python3 "$SELF_DIR/gen_pyproject.py" "$SELF_DIR/../../pyproject.toml" "$BUILD_DIR/pyproject.toml" "$WHEEL_VERSION" # 4. Build the wheel cd "$BUILD_DIR" diff --git a/tools/wheel_builder/gen_pyproject.py b/tools/wheel_builder/gen_pyproject.py index a8cf3fdd8d5d..b8a619597ec6 100644 --- a/tools/wheel_builder/gen_pyproject.py +++ b/tools/wheel_builder/gen_pyproject.py @@ -3,41 +3,97 @@ # # SPDX-License-Identifier: BSD-3-Clause -"""Generate pyproject.toml for the isaaclab wheel from python_packages.toml.""" +"""Generate pyproject.toml for the isaaclab wheel from the root pyproject.toml. +The published wheel bundles every ``isaaclab*`` sub-package as a top-level +module, so the workspace self-references in the root ``[project.dependencies]`` +and ``[project.optional-dependencies]`` are dropped here; only third-party +requirements end up in the wheel metadata. +""" + +import re import sys import tomllib if len(sys.argv) != 4: - print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) sys.exit(1) -packages_toml_path = sys.argv[1] +root_pyproject_path = sys.argv[1] output_path = sys.argv[2] version = sys.argv[3] -with open(packages_toml_path, "rb") as f: - data = tomllib.load(f) -pkg = data["isaaclab"] - -# Collect dependencies (deduplicated, preserving order) -raw_deps = pkg["pyproject"]["dependencies"]["all"] -seen = set() -deps = [] -for d in raw_deps: - # Normalize for dedup: lowercase package name (before any version specifier) - key = d.split(">")[0].split("<")[0].split("=")[0].split("!")[0].split("[")[0].strip().lower() - if key not in seen: - seen.add(key) - deps.append(d) - -# Collect optional dependencies +with open(root_pyproject_path, "rb") as f: + root = tomllib.load(f) +project = root["project"] + + +def _requirement_name(requirement: str) -> str: + """Extract the normalized distribution name from a requirement string.""" + name = re.split(r"\s|<|>|=|!|~|\[|@|;", requirement, maxsplit=1)[0].strip() + return re.sub(r"[-_.]+", "-", name).lower() + + +def _is_workspace_member(requirement: str) -> bool: + """Return True for ``isaaclab*`` self-references (bundled into the wheel).""" + return _requirement_name(requirement).startswith("isaaclab") + + +_project_name = _requirement_name(project["name"]) +_optional = project.get("optional-dependencies", {}) +_self_ref_pattern = re.compile(r"^\s*([A-Za-z0-9._-]+)\s*\[([^\]]+)\]\s*$") + + +def _self_ref_extras(requirement: str) -> list[str] | None: + """Return the referenced extra names for a bare self-reference. + + A self-reference looks like ``isaaclab-dev[sb3,skrl]`` (the root project's own + name with extras and no version specifier or marker). Returns ``None`` for any + other requirement, including third-party extras such as ``ray[default]>=2``. + """ + match = _self_ref_pattern.match(requirement) + if match is None or _requirement_name(match.group(1)) != _project_name: + return None + return [extra.strip() for extra in match.group(2).split(",")] + + +def _expand_self_refs(requirements: list[str], seen: set[str] | None = None) -> list[str]: + """Inline self-referential extras into their concrete requirements.""" + seen = set() if seen is None else seen + expanded = [] + for requirement in requirements: + extras = _self_ref_extras(requirement) + if extras is None: + expanded.append(requirement) + continue + for extra in extras: + if extra in seen: + continue + seen.add(extra) + expanded.extend(_expand_self_refs(_optional.get(extra, []), seen)) + return expanded + + +def _dedup(requirements: list[str]) -> list[str]: + """Drop duplicate requirements by distribution name, preserving order.""" + seen = set() + result = [] + for requirement in requirements: + key = _requirement_name(requirement) + if key not in seen: + seen.add(key) + result.append(requirement) + return result + + +# Required dependencies: third-party only (strip workspace members), deduped. +deps = _dedup([d for d in _expand_self_refs(project["dependencies"]) if not _is_workspace_member(d)]) + +# Optional dependencies: per extra, strip workspace members and dedup. opt_deps = {} -for entry in pkg["pyproject"]["optional-dependencies"]["all"]: - # Each entry is a dict like {"name": ["dep1", "dep2"]} - for name, dep_list in entry.items(): - opt_deps[name] = dep_list +for name, dep_list in project.get("optional-dependencies", {}).items(): + opt_deps[name] = _dedup([d for d in _expand_self_refs(dep_list) if not _is_workspace_member(d)]) # Write pyproject.toml lines = [] diff --git a/tools/wheel_builder/res/python_packages.toml b/tools/wheel_builder/res/python_packages.toml deleted file mode 100644 index 31ed61750590..000000000000 --- a/tools/wheel_builder/res/python_packages.toml +++ /dev/null @@ -1,127 +0,0 @@ -[isaaclab] -# pyproject.toml -pyproject.description = "Isaac Lab" -pyproject.keywords = ["nvidia", "omniverse", "isaacsim", "isaaclab"] -pyproject.dependencies.all = [ - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab/setup.py - # ================================================================================ - # generic - "numpy>=2", - "torch>=2.10", - "onnx>=1.18.0", # 1.16.2 throws access violation on Windows - "prettytable==3.3.0", - "toml", - # devices - "hidapi==0.14.0.post2", - # reinforcement learning - "gymnasium==1.2.0", - # procedural-generation - "trimesh", - "pyglet>=2.1.6", - # image processing - "transformers==4.57.6", - "einops", # needed for transformers, doesn't always auto-install - "warp-lang==1.14.0", - "matplotlib>=3.10.3", - # make sure this is consistent with isaac sim version - "pillow==12.1.1", - "botocore", - # livestream - # range chosen to coexist with isaacsim 6.0 (isaacsim-kernel pulls fastapi==0.117.1 -> starlette<0.49.0) - "starlette>=0.46.0,<0.50", - "omniverseclient==2.71.1.7015", - # testing - "pytest", - "pytest-mock", - "junitparser", - "flatdict==4.0.0", - "flaky", - "packaging", - # visualizers - "imgui-bundle==1.92.601", - "rerun-sdk>=0.29.0", - "pyarrow==22.0.0", - # viser is intentionally not a base dep: viser>=1.0.16 pulls websockets>=13.1, - # but isaacsim-kernel==6.0.0.0 pins websockets==12.0. Users who want the viser - # visualizer install isaaclab[viser] explicitly (see the optional-dependencies - # block below). - "typing_extensions==4.12.2", - "lazy_loader>=0.4", - "pin ; platform_system == 'Linux'", - "pin-pink>=2.3.6 ; platform_system == 'Linux'", - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_rl/setup.py - # ================================================================================ - # generic - "numpy>=2", - "torch>=2.10", - "torchvision>=0.25.0", # ensure compatibility with torch 2.10 - "protobuf>=4.25.8,!=5.26.0", - # configuration management - "hydra-core", - # data collection - "h5py", - # basic logger - "tensorboard", - # video recording - "moviepy", - "packaging<24", - "tqdm==4.67.1", - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_tasks/setup.py - # ================================================================================ - # generic - "numpy>=2", - "torch>=2.10", - "torchvision>=0.25.0", # ensure compatibility with torch 2.10 - "protobuf>=4.25.8,!=5.26.0", - # basic logger - "tensorboard", - "numba>=0.63.1", -] -pyproject.optional-dependencies.all = [ - # Isaac Sim - { "isaacsim" = ["isaacsim[all,extscache]==6.0.0.1"] }, - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_newton/setup.py - # ================================================================================ - { "newton" = [ - "warp-lang==1.14.0", - "newton[sim] @ git+https://github.com/newton-physics/newton.git@79e95bf5571d70a0a46c8eaedc80644531d27368", - "PyOpenGL-accelerate==3.1.10" - ] }, - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_rl/setup.py - # ================================================================================ - # RL libraries - { "sb3" = ["stable-baselines3>=2.6", "tqdm", "rich"] }, - { "skrl" = ["skrl>=2.1.0"] }, - { "rsl-rl" = ["rsl-rl-lib==5.0.1", "onnxscript>=0.5"] }, - { "rsl_rl" = ["rsl-rl-lib==5.0.1", "onnxscript>=0.5"] }, - # ================================================================================ - # https://github.com/isaac-sim/IsaacLab/blob/main/source/isaaclab_visualizers/pyproject.toml - # ================================================================================ - # Viser visualizer (opt-in: viser pulls websockets>=13.1 which collides with - # isaacsim-kernel==6.0.0.0's websockets==12.0; do not include in [all]). - { "viser" = ["viser>=1.0.16"] }, - # RL libraries (all) — use with ``isaacsim`` for full installs; Newton comes from - # ``isaacsim-core``. Do not add git Newton here (conflicts with Isaac Sim pins). - { "all" = [ - "stable-baselines3>=2.6", - "tqdm", - "rich", - "skrl>=2.1.0", - "rsl-rl-lib==5.0.1", - "onnxscript>=0.5", - ] }, -] -# inventory (relative to the `source_folder` config) -inventory.includes.all = [ - # Isaac Lab - "apps", - "source", - # python package/module - [ "../source/python_packages/isaaclab/__init__.py", "." ], - [ "../source/python_packages/isaaclab/__main__.py", "." ], -]