[Test] Quadrupeds: relax import order (depends on #5826 + #5771)#5828
[Test] Quadrupeds: relax import order (depends on #5826 + #5771)#5828hujc7 wants to merge 14 commits into
Conversation
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Signed-off-by: Yize Wang <yizew@nvidia.com>
Importing AssetBase / BaseArticulation forced pxr (USD) and downstream omni.* extensions to load at module-import time. That ordering broke conditional AppLauncher() patterns: any user script doing `from isaaclab.assets import Articulation` at module top loaded pxr before Kit, and Kit's later USD extension registration then failed with a cascade of "extension class wrapper for base class pxr...::TfNotice has not been created yet" errors and a SEGV. Three changes: * asset_base.py: move `from pxr import Usd` under TYPE_CHECKING; drop module-top `from isaaclab.sim.simulation_context import SimulationContext` and `from isaaclab.sim.utils.stage import get_current_stage`. Call sites use `sim_utils.SimulationContext.instance()` and `sim_utils.get_current_stage()` via the already-lazy isaaclab.sim package, so pxr is loaded on first runtime access instead of at import. * base_articulation.py: defer `from ...sim import SimulationContext` to the body of __init__ (its only use site). `from isaaclab.assets import Articulation` no longer adds pxr / omni / carb / isaacsim to sys.modules. A full smoke test that imports asset classes at module top, then launches AppLauncher, then constructs an Articulation now succeeds end-to-end.
Pushes the pxr deferral down to the producer module. simulation_context.py no longer does `from pxr import Gf, Usd, UsdGeom, UsdPhysics, UsdUtils` at module top; instead `__init__` does a local `from pxr import UsdUtils` and `_init_usd_physics_scene` does a local `from pxr import Gf, UsdGeom, UsdPhysics`. The `Usd` annotation on `_predicate(prim: Usd.Prim)` stays typed via the existing `from __future__ import annotations` (already at line 6) + a TYPE_CHECKING import. stage_utils, create_new_stage, and close_stage are reached via sim_utils.X / a function-local `from isaaclab.sim.utils import stage` so simulation_context.py's module-top imports no longer transitively load pxr through stage.py either. With this in place, `from isaaclab.sim import SimulationContext` is pxr-free at module top, which means the earlier `base_articulation.py:__init__`-local `from ...sim import SimulationContext` workaround is no longer needed -- reverted to the original module-top form.
…pts-with-helper' into jichuanh/relax-quadrupeds-import-order
With the producer-side pxr deferral from this branch's preceding commits (asset_base and SimulationContext), `from isaaclab.assets import Articulation` and `import isaaclab.sim as sim_utils` no longer load pxr at module-import time. The deferred-imports-inside-design_scene workaround is no longer needed -- imports return to module top matching every other IsaacLab demo script's canonical "Launch first / Rest everything follows" idiom, just with the launch gated by the helper. Moved 7 import lines from `design_scene()` body and 1 from `main()` body back to module top (after CLI parse). Behavior unchanged across all four backend combinations from the docstring; verified by running the script end-to-end (--headless, step-capped) on newton+newton (kit-less), newton+kit, physx+newton, and physx+kit.
There was a problem hiding this comment.
Code Review Summary
This PR demonstrates the combined effect of PRs #5826 and #5771, enabling module-top imports in demo scripts by deferring pxr loading until runtime. The approach is architecturally sound and maintains backward compatibility.
🔬 Architecture & Design
Design Assessment: The pxr deferral pattern is the correct approach for enabling kit-less workflows. Moving pxr imports to TYPE_CHECKING blocks and accessing them through sim_utils (which uses lazy module loading) cleanly separates import-time from runtime dependencies.
Cross-Module Impact: Well-contained. The changes to asset_base.py and simulation_context.py use the existing sim_utils namespace, so call sites remain unchanged. No API breaks.
File Placement: demo_helper.py in scripts/demos/ is appropriate for demo-specific utilities. The helper is not part of the core isaaclab package, which is correct—it's demo infrastructure.
🔍 Implementation Findings
🟡 Warning: scripts/demos/quadrupeds.py:193 — Timestep changed without documentation
The physics timestep changed from dt=0.01 to dt=0.005. This halves the timestep (doubling simulation accuracy but also computational cost). If intentional for Newton backend compatibility, please document in the PR description or add a comment explaining the choice.
# Current (PR):
sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, ...)
# Base (develop):
sim = sim_utils.SimulationContext(sim_utils.SimulationCfg(dt=0.01))🔵 Improvement: scripts/demos/demo_helper.py:75 — Defensive check for visualizer attribute access
The has_no_alive_visualizer_window function accesses v.is_closed as an attribute, but if this were a method call that fails, it would raise an exception. Consider whether is_closed could ever be undefined on a visualizer object:
def has_no_alive_visualizer_window(sim) -> bool:
visualizers = sim.visualizers or ()
return not any(v.is_running() and not v.is_closed for v in visualizers)If is_closed is guaranteed by the BaseVisualizer interface, this is fine. Otherwise, consider getattr(v, 'is_closed', True) as a defensive fallback.
🔵 Improvement: scripts/demos/quadrupeds.py:139 — Type annotations loosened
The run_simulator signature changed from typed parameters to generic ones:
# Before:
def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
# After:
def run_simulator(sim, entities: dict, origins):While acceptable for a demo script, keeping the type hints aids IDE support and documentation. Consider preserving them if the interface is stable.
🧪 Test Coverage
Coverage Assessment: This PR is a demonstration/refactoring change rather than a new feature. The core logic changes in asset_base.py and simulation_context.py are internal import restructuring that doesn't change runtime behavior.
Verification: The PR author's manual verification matrix (4 backend combinations + assert \"pxr\" not in sys.modules) is appropriate validation for import-order changes, which are difficult to unit test.
Recommendation: No additional tests required for this PR. The deferred import pattern is exercised by existing tests that use Articulation and SimulationContext.
✅ Summary
| Aspect | Status |
|---|---|
| Architecture | ✅ Sound pxr deferral approach |
| API Compatibility | ✅ No breaking changes |
| Code Quality | ✅ Clean with minor suggestions |
| Test Coverage | ✅ Adequate for refactoring PR |
| Documentation |
Verdict: Minor fixes needed (clarify dt change)
The pxr deferral mechanism is well-implemented and achieves the stated goal of allowing module-top imports without forcing Kit initialization. Once the timestep change is documented, this is ready to proceed.
Automated review by IsaacLab Review Bot 🤖
1. Summary
resolve_backend_and_visualizerhelper, lets the demo script keep imports at module top — the canonical IsaacLab idiomdesign_scene()workaround in PR 5771 is no longer needed2. Branch composition
This branch chains three sets of changes for review-as-one. Diff order against
develop:80b86a01070,33d40434df5): producer-side pxr deferral insource/isaaclab/isaaclab/assets/asset_base.pyandsource/isaaclab/isaaclab/sim/simulation_context.py.6c292c1802b): YizeWang'sscripts/demos/demo_helper.pyandscripts/demos/quadrupeds.pywithresolve_backend_and_visualizer.690f2805f09: lift the deferred imports back to module top inscripts/demos/quadrupeds.py.3. Mechanism
After (1),
from isaaclab.assets import Articulation,from isaaclab.sim import SimulationContext, andimport isaaclab.sim as sim_utilsall keepsys.modulesfree ofpxr/omni/carb/isaacsimuntil a runtime touch (e.g.sim_utils.SimulationContext.instance()).(3) takes advantage of this: the 7 deferred imports inside
design_scene()and 1 insidemain()move back to module top, ordered afterargs_cli = parser.parse_args()to match the existing"""Parse CLI first so we can decide whether to launch Isaac Sim Kit."""block.resolve_backend_and_visualizerfrom PR 5771 still gates the conditionalAppLauncherlaunch inside thewithblock; the script's behavior under--physics newton_mjwarp --visualizer newton(no Kit) is unchanged.4. Verification
Ran the script end-to-end (step-capped, headless) for all four backend combinations from the docstring:
--physics newton_mjwarp --visualizer newton(kit-less)[INFO]: Setup complete, sim stepped, rc=0--physics newton_mjwarp --visualizer kit--physics physx --visualizer newton--physics physx --visualizer kitPlus an
assert "pxr" not in sys.modulesimmediately after the module-top asset imports — passed in every case.5. Out of scope
6. Test plan
./isaaclab.sh -fcleanquadrupeds.pyacross all 4 backend combinations