Support Demo Scripts With Multiple Backends#5771
Conversation
Signed-off-by: Yize Wang <yizew@nvidia.com>
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot — Multi-Perspective Analysis
PR #5771: Support Demo Scripts With Multiple Backends
Author: @YizeWang | Files Changed: 2 | SHA: 05b1434
🏗️ Isaac Lab Expert — Architecture & Design
Summary: This PR introduces a demo_helper.py utility (context manager + helper function) to allow demo scripts to run with either PhysX or Newton (MJWarp) physics backends, paired with Kit or Newton visualizers. The quadrupeds.py demo is refactored to use this new helper.
Positives:
- ✅ Clean separation of backend/visualizer resolution into a reusable helper
- ✅ Context manager pattern ensures Kit cleanup via
finallyblock - ✅ Dynamic instantiation via
cfg.class_type(cfg)— idiomatic for Isaac Lab's config-driven design - ✅ Moves imports inside functions for conditional loading (avoids importing PhysX when using Newton and vice versa)
- ✅ Good CLI ergonomics:
--physicsand--visualizerwith clear choices
Concerns:
-
conflict_handler="resolve"on ArgumentParser (quadrupeds.py L19): This silently overwrites--visualizeradded byAppLauncher.add_app_launcher_args(). This hides potential conflicts rather than surfacing them. If AppLauncher's--visualizerargument semantics change upstream, this will silently break. -
args.visualizer = [viz_type]mutation (demo_helper.py L59): The helper mutates the parsed args namespace before passing toAppLauncher. This side-effect is non-obvious to callers and changes the type fromstrtolist[str], which could confuse downstream code. -
Relative import
from demo_helper import ...(quadrupeds.py L40): This assumes the working directory isscripts/demos/. If the script is invoked from a different working directory, this import will fail. Consider using a relative path setup (e.g.,sys.path.insert) or makingdemo_helpera proper module within the Isaac Lab package. -
DEFAULT_NEWTON_CFGhardcoded in helper (demo_helper.py L24-33): Physics solver parameters (njmax=70, nconmax=70, ls_iterations=40, etc.) are scene-dependent. Hardcoding them in a general helper means other demos using this helper may need very different values. Thenewton_cfgoverride parameter helps, but the defaults could be misleading.
🔇 Silent Failure Hunter — Error Handling
-
has_no_alive_visualizer_windowreturnsFalsewhensim.visualizersis empty (demo_helper.py L72): Ifsim.visualizersisNoneor empty list,bool([])isFalse, so the function returnsFalse— meaning "there IS an alive window." This causes the simulation to run forever with no visualizer. The logic seems inverted for the empty-visualizers edge case.def has_no_alive_visualizer_window(sim) -> bool: return bool(sim.visualizers and not any(v.is_running() and not v.is_closed for v in sim.visualizers))
When
sim.visualizersis empty/None →bool(None and ...)→False→ loop never breaks. This is a potential infinite loop. -
No validation that
AppLauncherimport succeeds in non-Kit path: Ifviz_type != "kit",AppLauncheris never imported andclose_fnstaysNone. But the script still callsAppLauncher.add_app_launcher_args(parser)at the top ofquadrupeds.py(L17). If Isaac Sim Kit is not installed, this will crash before even reaching the helper. -
Missing error handling for
close_fn()(demo_helper.py L67): If Kit shutdown throws an exception during cleanup in thefinallyblock, it could mask the original exception from thetryblock.
🧪 Test Coverage Analyzer
-
No tests added: The checklist item "I have added tests that prove my fix is effective or that my feature works" is unchecked. For multi-backend support, testing the following would be valuable:
resolve_backend_and_visualizerwith each physics/visualizer combinationhas_no_alive_visualizer_windowedge cases (empty list, None, mixed states)- CLI argument parsing with the new
--physics/--visualizerflags
-
No CI validation visible: Only the
labelercheck passed. No integration or unit test runs are visible for this PR.
📋 Summary
| Category | Findings |
|---|---|
| Architecture | 4 concerns |
| Silent Failures | 3 issues (1 potential infinite loop) |
| Test Coverage | No tests added |
| Total | 7 findings |
Verdict: Needs attention — The potential infinite loop in has_no_alive_visualizer_window when no visualizers are present is the most critical issue. The relative import and args mutation are also worth addressing before merge.
🤖 Review by Isaac Lab Review Bot (multi-perspective ensemble analysis)
Update (c73177b): Reviewed incremental changes. New commit contains:
- 📝 Improved docstrings and comments (helper description,
has_no_alive_visualizer_windowdocstring, Kit cleanup comment) - 🔧 Minor code simplification (removed intermediate
needs_kitvariable) - 📋 Added TODO noting physx+newton visualizer combination is not yet supported
Previous concerns status: No changes to the flagged logic — the has_no_alive_visualizer_window infinite loop risk and args.visualizer mutation remain unaddressed.
New issues: None introduced in this update.
Greptile SummaryThis PR adds multi-backend support to the
Confidence Score: 3/5The demo refactor is largely sound, but the new exit-condition helper has a logic inversion that will cause an unbreakable infinite loop if the simulation's visualizer list is ever empty. The scripts/demos/demo_helper.py — both the exit-condition helper and the args-mutation logic warrant a closer look. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[quadrupeds.py main] --> B[resolve_backend_and_visualizer context manager]
B --> C{args.physics}
C -->|physx| D[PhysxCfg]
C -->|newton_mjwarp| E[NewtonCfg / MJWarpSolverCfg]
B --> F{args.visualizer}
F -->|kit| G[KitVisualizerCfg]
F -->|newton| H[NewtonVisualizerCfg]
B --> I{needs_kit?}
I -->|yes| J[AppLauncher created, close_fn stored]
I -->|no| K[close_fn = None]
B --> L[yield physics_cfg, visualizer_cfg]
L --> M[SimulationContext created with physics + visualizer cfgs]
M --> N[design_scene]
N --> O[run_simulator loop]
O --> P{has_no_alive_visualizer_window?}
P -->|yes| Q[break]
P -->|no| O
Q --> R[finally: close_fn if Kit]
Reviews (1): Last reviewed commit: "Support Demo Scripts With Multiple Backe..." | Re-trigger Greptile |
| def has_no_alive_visualizer_window(sim) -> bool: | ||
| return bool(sim.visualizers and not any(v.is_running() and not v.is_closed for v in sim.visualizers)) |
There was a problem hiding this comment.
Infinite loop when
sim.visualizers is empty
bool(sim.visualizers and ...) short-circuits to False when sim.visualizers is falsy (empty list, None, etc.), meaning the function returns False — "there ARE alive visualizer windows" — even when none were ever registered. Any caller whose SimulationContext ends up with an empty visualizers list will spin in the while True loop indefinitely with no exit path. The guard should return True (exit the loop) when no visualizers exist, not False.
| if needs_kit: | ||
| from isaaclab.app import AppLauncher | ||
|
|
||
| args.visualizer = [viz_type] | ||
| close_fn = AppLauncher(args).app.close |
There was a problem hiding this comment.
Mutable side-effect on caller's
args namespace
args.visualizer is mutated in-place from a str (as parsed by the script's own --visualizer argument) to a list before AppLauncher is constructed. This leaks into the caller's namespace after the context manager exits and makes resolve_backend_and_visualizer non-reentrant. Using a shallow copy of the namespace would contain the mutation to the helper.
| if needs_kit: | |
| from isaaclab.app import AppLauncher | |
| args.visualizer = [viz_type] | |
| close_fn = AppLauncher(args).app.close | |
| if needs_kit: | |
| from isaaclab.app import AppLauncher | |
| import copy | |
| kit_args = copy.copy(args) | |
| kit_args.visualizer = [viz_type] | |
| close_fn = AppLauncher(kit_args).app.close |
Signed-off-by: Yize Wang <yizew@nvidia.com>
Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
List any dependencies that are required for this change.
Fixes # (issue)
Type of change
Screenshots
Please attach before and after screenshots of the change if applicable.
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there