Skip to content

feat: make IsaacLab pip/uv installable via workspace meta-package#5201

Draft
lgulich wants to merge 8 commits intoisaac-sim:mainfrom
lgulich:feat/pip-installable-main
Draft

feat: make IsaacLab pip/uv installable via workspace meta-package#5201
lgulich wants to merge 8 commits intoisaac-sim:mainfrom
lgulich:feat/pip-installable-main

Conversation

@lgulich
Copy link
Copy Markdown
Contributor

@lgulich lgulich commented Apr 7, 2026

Problem

Today, there's no way to pip install or uv sync IsaacLab from a downstream project. Installation requires cloning the repo and running isaaclab.sh --install, which:

  • Installs ~12 sub-packages one by one in the right order
  • Manually installs isaacsim with the correct index URL
  • Sets up Kit extension paths via filesystem conventions

This makes it impossible to declare IsaacLab as a dependency in a downstream pyproject.toml and have everything "just work."

Solution

Add modern Python packaging (pyproject.toml with PEP 621 [project] sections) to 4 core sub-packages and create a root isaac-lab meta-package with a uv workspace. A downstream project can now install everything with:

dependencies = ["isaac-lab"]

[tool.uv.sources]
isaac-lab = { git = "https://github.com/isaac-sim/IsaacLab.git", branch = "main" }

[tool.uv]
extra-index-url = ["https://pypi.nvidia.com", "https://download.pytorch.org/whl/cu128"]
index-strategy = "unsafe-best-match"

Or with a requirements.txt:

--extra-index-url https://pypi.nvidia.com
--extra-index-url https://download.pytorch.org/whl/cu128
isaaclab @ git+https://github.com/isaac-sim/IsaacLab.git@main#subdirectory=source/isaaclab
isaaclab-assets @ git+https://github.com/isaac-sim/IsaacLab.git@main#subdirectory=source/isaaclab_assets
isaaclab-tasks @ git+https://github.com/isaac-sim/IsaacLab.git@main#subdirectory=source/isaaclab_tasks
isaaclab-rl @ git+https://github.com/isaac-sim/IsaacLab.git@main#subdirectory=source/isaaclab_rl

Changes

  • Move package metadata from setup.py to pyproject.toml [project] sections for isaaclab, isaaclab-assets, isaaclab-tasks, isaaclab-rl
  • Add inter-package dependencies (e.g. isaaclab-tasks depends on isaaclab and isaaclab-assets)
  • Add isaacsim[all,extscache]==5.1.0 as a dependency of isaaclab
  • Add root isaac-lab meta-package with [tool.uv.workspace] configuration
  • Add extra-index-url for pypi.nvidia.com and PyTorch CUDA wheels
  • Set index-strategy = "unsafe-best-match" for cross-index resolution
  • Bundle .kit experience files inside the isaaclab package with fallback path resolution in AppLauncher and SimulationContext (so the sim boots without the repo layout)
  • Make extension.toml loading optional in __init__.py (fall back to importlib.metadata for non-editable installs)
  • Keep setup.py as backward-compatible shims so isaaclab.sh --install still works

Test plan

  • uv lock resolves 245 packages successfully
  • uv sync installs all packages into a fresh venv from a downstream project
  • All 6 packages report correct versions via importlib.metadata.version()
  • import isaaclab and import isaaclab_rl work in non-editable install
  • Isaac Sim boots, creates SimulationContext, steps physics (headless, GPU)
  • requirements.txt approach also installs and runs correctly
  • Verify isaaclab.sh --install still works with the shim setup.py files

🤖 Generated with Claude Code

- Move package metadata from setup.py to pyproject.toml [project] sections
  for isaaclab, isaaclab-assets, isaaclab-tasks, isaaclab-rl
- Add inter-package dependencies (isaaclab_tasks depends on isaaclab, etc.)
- Add isaacsim[all,extscache]==5.1.0 as dependency of isaaclab
- Add root isaac-lab meta-package with uv workspace configuration
- Add extra-index-url for pypi.nvidia.com and PyTorch CUDA wheels
- Set index-strategy = "unsafe-best-match" for cross-index resolution
- Add extra-build-dependencies for flatdict (needs setuptools)
- Keep setup.py as backward-compatible shims for isaaclab.sh --install

Downstream projects can now add isaac-lab to their pyproject.toml and
install everything via uv sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added asset New asset feature or request infrastructure labels Apr 7, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR migrates all package metadata from setup.py to pyproject.toml for all four Isaac Lab sub-packages and adds a root isaac-lab meta-package with uv workspace configuration, enabling uv sync to install the full framework including Isaac Sim.

  • The root meta-package correctly restricts requires-python = ">=3.11,<3.12" (Isaac Sim 5.1.0 requires Python 3.11), but this upper bound is absent from source/isaaclab/pyproject.toml; a direct pip install isaaclab on Python 3.12 will appear to succeed but fail at Isaac Sim runtime.

Confidence Score: 4/5

Mostly safe to merge; one P1 concern about the missing Python upper-bound on the isaaclab sub-package.

Score of 4 reflects one P1 finding: the <3.12 upper bound is enforced at the meta-package level but not propagated to source/isaaclab/pyproject.toml, risking silent failures on unsupported Python versions. All other findings are P2 style/maintenance suggestions.

source/isaaclab/pyproject.toml needs <3.12 added to requires-python; source/isaaclab_rl/pyproject.toml all extra needs documentation or automation to prevent drift.

Important Files Changed

Filename Overview
pyproject.toml New root isaac-lab meta-package with uv workspace config; requires-python upper bound not propagated to sub-packages
source/isaaclab/pyproject.toml Dependencies migrated from setup.py; starlette dropped, flatdict unpinned, requires-python lacks <3.12 upper bound
source/isaaclab/setup.py Reduced to backward-compatible shim calling setup()
source/isaaclab_assets/pyproject.toml Clean minimal declaration depending only on isaaclab
source/isaaclab_assets/setup.py Backward-compatible shim
source/isaaclab_rl/pyproject.toml Extras correctly declared but all extra is manually maintained and may drift
source/isaaclab_rl/setup.py Backward-compatible shim
source/isaaclab_tasks/pyproject.toml Clean dependency declaration with proper inter-package deps
source/isaaclab_tasks/setup.py Backward-compatible shim

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  META["isaac-lab\nmeta-package v2.3.2"]
  IL["isaaclab v0.54.3"]
  IA["isaaclab-assets v0.2.4"]
  IT["isaaclab-tasks v0.11.14"]
  IRL["isaaclab-rl v0.5.0"]
  ISIM["isaacsim\nall,extscache==5.1.0"]
  META --> IL
  META --> IA
  META --> IT
  META --> IRL
  IL --> ISIM
  IA --> IL
  IT --> IL
  IT --> IA
  IRL --> IL
  IRL --> IA
  IRL --> IT
Loading

Reviews (1): Last reviewed commit: "feat: make IsaacLab pip/uv installable v..." | Re-trigger Greptile

Comment on lines +11 to +35
dependencies = [
"isaacsim[all,extscache]==5.1.0",
"numpy<2",
"torch>=2.7",
"onnx>=1.18.0",
"prettytable==3.3.0",
"toml",
"hidapi==0.14.0.post2",
"gymnasium==1.2.1",
"trimesh",
"pyglet<2",
"transformers==4.57.6",
"einops",
"warp-lang",
"pillow==11.3.0",
"pytest",
"pytest-mock",
"junitparser",
"flatdict",
"flaky",
"packaging",
'pin-pink==3.1.0 ; platform_system == "Linux" and platform_machine in "x86_64,AMD64,aarch64,arm64"',
'daqp==0.7.2 ; platform_system == "Linux" and platform_machine in "x86_64,AMD64,aarch64,arm64"',
'dex-retargeting==0.4.6 ; platform_system == "Linux" and platform_machine in "x86_64,AMD64"',
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 starlette dependency dropped

The old setup.py listed "starlette==0.49.1" with a # livestream comment; it is absent from the new pyproject.toml. starlette is not directly imported in the current isaaclab source, so the removal may be intentional — please confirm it was a deliberate omission and not an oversight during the migration.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate removal. starlette==0.49.1 conflicts with isaacsim's fastapi dependency which requires starlette>=0.40.0,<0.46.0. Starlette is provided transitively by isaacsim via fastapi, so the explicit pin is not needed.

@lgulich lgulich marked this pull request as draft April 7, 2026 21:57
lgulich and others added 7 commits April 8, 2026 09:06
When installed from git (non-editable), config/extension.toml is not
available in site-packages. Fall back to importlib.metadata for version
and package info. This affects isaaclab, isaaclab_assets, isaaclab_tasks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AppLauncher looks for .kit files 4 dirs up from app_launcher.py, which
only works in editable installs. Bundle stripped .kit files inside the
isaaclab package (without IsaacLab Kit extension deps and source path)
and add fallback path resolution in AppLauncher.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also add fallback path in SimulationContext for pip installs, matching
the AppLauncher fix. Include rendering_modes/*.kit in package data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add <3.12 upper bound to requires-python in all sub-packages (P1)
- Re-pin flatdict==4.0.0 to match original setup.py (P2)
- Add NOTE comment to isaaclab-rl 'all' extra about manual sync (P2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of duplicating all packages, reference the other extras by name.
This stays in sync automatically when extras are added or changed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-commit hook requires a blank line after the module docstring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kellyguo11
Copy link
Copy Markdown
Contributor

@myurasov-nv for viz

Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: pip/uv installable meta-package

This is a well-motivated and well-structured PR. Making IsaacLab pip install-able from downstream projects is a significant ergonomic improvement. The approach — PEP 621 [project] sections, uv workspace, backward-compatible setup.py shims, bundled .kit files with fallback path resolution — is sound.

Below are observations organized by severity.


🔴 Issues

1. starlette dependency dropped silently
The old source/isaaclab/setup.py had starlette==0.49.1 (for livestream). The new pyproject.toml doesn't include it. If any user code or IsaacLab code imports starlette for livestreaming, this will be a runtime ImportError on fresh pip/uv installs. Was this intentional? If livestream is now handled by an isaacsim extension, it's fine — but worth noting in the PR description.

2. isaaclab_rl/__init__.py not updated with extension.toml fallback
The __init__.py files for isaaclab, isaaclab_assets, and isaaclab_tasks all get the conditional extension.tomlimportlib.metadata fallback. But isaaclab_rl/__init__.py is not modified. Checking the current code: it doesn't load extension.toml at the top, so this is fine — just verifying it doesn't break. 👍

3. isaaclab_mimic and isaaclab_contrib not included in workspace
The uv workspace only has 4 members. The repo has 6 sub-packages (isaaclab_contrib, isaaclab_mimic). If someone does uv sync from root and then tries to import isaaclab_mimic, it won't be installed. Is this intentional scope reduction, or a follow-up? Should be documented.

4. Version pinning drift risk
Versions are now hardcoded in 5 separate pyproject.toml files (root isaac-lab 2.3.2, isaaclab 0.54.3, isaaclab-assets 0.2.4, isaaclab-tasks 0.11.14, isaaclab-rl 0.5.0) AND still in extension.toml for editable installs. There's no single source of truth anymore. The __init__.py fallback logic picks one or the other based on install mode, but they could diverge. Consider either:

  • Reading version from extension.toml at build time (dynamic version)
  • Or having the pyproject.toml be authoritative and deprecating the extension.toml version field

🟡 Suggestions

5. requires-python narrowed to >=3.11,<3.12
The old setup.py had python_requires=">=3.10". The new pyproject.toml says >=3.11,<3.12. This is fine for Isaac Sim 5.1 (which requires 3.11), but it's a breaking change for anyone on 3.10. Worth calling out in the PR description. Also — the root pyproject.toml and sub-packages all have >=3.11,<3.12, which is consistent. 👍

6. unsafe-best-match index strategy
This is documented in the PR body but worth noting: index-strategy = "unsafe-best-match" in [tool.uv] means uv will pick the best-matching version across ALL configured indexes, which can lead to dependency confusion attacks if a malicious package with a higher version is published to PyPI with the same name as an internal NVIDIA package. For a framework like IsaacLab this is likely acceptable (the NVIDIA packages are all on pypi.nvidia.com), but downstream users should understand the security implications.

7. Bundled .kit files are large (~1000 lines)
The 6 bundled .kit files add ~1000+ lines to the package. These appear to be copies of the existing apps/ directory .kit files, bundled inside the Python package for non-editable installs. This works but creates a maintenance burden — any .kit change needs to be mirrored. Consider symlinking or generating these from a single source, or at minimum adding a comment noting they're copies.

8. rendering_modes/extension.toml is a dummy file
The file at source/isaaclab/isaaclab/apps/rendering_modes/extension.toml contains only # This is not an extension. If this is needed to prevent Kit from treating the directory as an extension, add a brief explanation comment. Otherwise it looks like an accidental inclusion.

9. [tool.setuptools.packages.find] in isaaclab pyproject.toml
The config uses include = ["isaaclab*"] which will match isaaclab and all sub-packages. This is correct, but in a workspace where isaaclab_assets, isaaclab_tasks, etc. are siblings, make sure the find doesn't accidentally pull in adjacent packages. Since where = ["."] is relative to source/isaaclab/, it should be scoped correctly.


✅ What looks good

  • Clean separation: pyproject.toml for metadata, setup.py as shim for backward compat
  • Proper inter-package dependencies (isaaclab-tasksisaaclab + isaaclab-assets)
  • isaacsim[all,extscache]==5.1.0 as a first-class dependency — this is the key enabler
  • Fallback path resolution in AppLauncher and SimulationContext is minimal and correct
  • extension.tomlimportlib.metadata fallback is well-implemented
  • extra-index-url for NVIDIA and PyTorch CUDA wheels
  • Optional dependencies in isaaclab-rl (sb3, skrl, rl-games, rsl-rl) are well-organized
  • Test plan is solid (though isaaclab.sh --install is unchecked — please verify before merge)

Overall: strong direction. Address the starlette question, document the scope (4/6 packages), and verify the isaaclab.sh --install shim path, and this is good to go.

"isaacsim[all,extscache]==5.1.0",
"numpy<2",
"torch>=2.7",
"onnx>=1.18.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isaacsim[all,extscache]==5.1.0 — this is the key enabler for pip-installability. Pinning to exact version is the right call since Kit extensions are not semver-compatible across Isaac Sim versions.

One concern: when Isaac Sim 5.2 ships, every downstream project pinned to isaac-lab will need to wait for a new release. Consider whether a >=5.1.0,<5.2 range would be more practical, or if exact pin is intentional.


[tool.setuptools]
include-package-data = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old setup.py included starlette==0.49.1 for livestream support. This dependency is absent here. Was this intentional? If livestream functionality depends on starlette being installed, this will break for pip/uv users.

__version__ = ISAACLAB_METADATA["package"]["version"]
else:
from importlib.metadata import metadata

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean fallback pattern. One thought: if this is a non-editable install, ISAACLAB_METADATA will only have version and description keys under package, while editable installs get the full extension.toml dict (repository, keywords, authors, etc.). Any downstream code that accesses other fields of ISAACLAB_METADATA["package"] will get a KeyError in non-editable mode.

Consider either:

  • Documenting which keys are guaranteed
  • Populating more fields from importlib.metadata in the fallback


[tool.uv]
extra-index-url = ["https://pypi.nvidia.com", "https://download.pytorch.org/whl/cu128"]
index-strategy = "unsafe-best-match"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
index-strategy = "unsafe-best-match"
[tool.uv.extra-build-dependencies]
flatdict = ["setuptools"]

What's the story here? flatdict needs setuptools as a build dep? Is this a known upstream issue with flatdict's build, or a workaround for something specific? A brief comment would help future maintainers.

# If nothing is provided resolve the experience file based on the headless flag
kit_app_exp_path = os.environ["EXP_PATH"]
# Try repo layout first (editable install), then bundled apps (pip/uv install)
isaaclab_app_exp_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), *[".."] * 4, "apps")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback path (.. instead of ../../../..) for bundled apps is correct. The directory structure for editable is source/isaaclab/isaaclab/app/ → 4 levels up to apps/, while for installed it's site-packages/isaaclab/app/ → 1 level up to isaaclab/apps/.

Nit: the same pattern is duplicated in simulation_context.py. Consider extracting a helper function like get_isaaclab_apps_path() to avoid the duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

asset New asset feature or request infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants