Raise error when explicitly requested visualizer fails#5003
Raise error when explicitly requested visualizer fails#5003ooctipus merged 4 commits intoisaac-sim:developfrom
Conversation
When a visualizer is explicitly requested via CLI (--visualizer), raise a RuntimeError instead of silently logging the failure.
Greptile SummaryThis PR improves failure-visibility for explicitly requested visualizers by raising a Key changes:
Issues found:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[initialize_visualizers] --> B{self._visualizers non-empty?}
B -- yes --> Z[return early]
B -- no --> C[_resolve_visualizer_cfgs]
C --> D{cli_disable_all?}
D -- yes --> E[resolved = empty]
D -- no --> F{cli_explicit?}
F -- no --> G[resolved = cfg visualizer_cfgs]
F -- yes --> H{cfg visualizer_cfgs empty?}
H -- yes --> I[_create_default_visualizer_configs cli_requested]
H -- no --> J[filter cfgs by cli_requested + create defaults for missing]
I --> K
J --> K
G --> K
E --> K
K{cli_explicit AND cli_requested?} -- yes --> L{any cli_requested type missing from resolved?}
L -- yes --> M[🔴 raise RuntimeError unknown/missing-package]
L -- no --> N[return resolved]
K -- no --> N
N --> O{resolved empty?}
O -- yes --> Z
O -- no --> P[initialize_scene_data_provider]
P --> Q[for each cfg in resolved]
Q --> R{create_visualizer + initialize succeed?}
R -- yes --> S[append to self._visualizers]
S --> Q
R -- no --> T{cli_explicit?}
T -- yes --> U[🔴 raise RuntimeError ⚠️ provider not cleaned up]
T -- no --> V[log exception]
V --> Q
Q -- done --> W{self._visualizers empty?}
W -- yes --> X[close + clear _scene_data_provider]
W -- no --> Y[done]
|
| if cli_explicit: | ||
| raise RuntimeError( | ||
| f"Visualizer '{cfg.visualizer_type}' was explicitly requested but failed to initialize: {exc}" | ||
| ) from exc |
There was a problem hiding this comment.
Explicit-error guard has a gap for config-creation failures
The cli_explicit guard added here only covers failures that occur inside cfg.create_visualizer() or visualizer.initialize(). However, two earlier silent-failure paths in _create_default_visualizer_configs() are not covered:
-
Unknown type — if
--visualizer <invalid>is passed, the type is not in_VISUALIZER_TYPESso_create_default_visualizer_configslogs a warning and returns an empty list.initialize_visualizersthen hits the early-return at line 506 (if not visualizer_cfgs: return) — noRuntimeErroris raised. -
Package not installed — if
--visualizer rerunis passed butisaaclab_visualizersis not installed, theImportErroris caught inside_create_default_visualizer_configs, a warning is logged, and again an empty list is returned — noRuntimeErroris raised.
Because of this, the test-plan item "Run with --visualizer <invalid> and verify RuntimeError is raised" will not pass with the current code; the process exits silently with a log warning.
To fully cover explicit failures, _resolve_visualizer_cfgs (which already reads cli_explicit at line 471) could compare the set of successfully-created configs against cli_requested and raise when any explicitly-requested type is missing, e.g.:
if cli_explicit:
resolved_types = {getattr(cfg, "visualizer_type", None) for cfg in resolved_cfgs}
missing = [t for t in cli_requested if t not in resolved_types]
if missing:
raise RuntimeError(
f"Explicitly requested visualizer(s) could not be configured: {missing}. "
f"Valid types: {', '.join(_VISUALIZER_TYPES)}."
)|
@greptile |
| # Force import to fail for the rerun visualizer module | ||
| import builtins | ||
|
|
||
| real_import = builtins.__import__ | ||
|
|
||
| def _failing_import(name, *args, **kwargs): | ||
| if "isaaclab_visualizers.rerun" in name: | ||
| raise ImportError("No module named 'isaaclab_visualizers.rerun'") | ||
| return real_import(name, *args, **kwargs) | ||
|
|
||
| monkeypatch.setattr("builtins.__import__", _failing_import) | ||
|
|
||
| with pytest.raises(RuntimeError, match="rerun"): | ||
| ctx.initialize_visualizers() |
There was a problem hiding this comment.
Monkeypatching builtins.__import__ won't intercept importlib.import_module
_create_default_visualizer_configs loads the visualizer module with importlib.import_module(f"isaaclab_visualizers.{viz_type}") (line 376 of simulation_context.py). CPython's importlib.import_module routes through the internal _bootstrap._gcd_import machinery and does not call builtins.__import__. Patching builtins.__import__ therefore has no effect on this code path.
There is a second problem: because the test file contains a module-level import import isaaclab_visualizers.rerun.rerun_visualizer as rerun_visualizer, the module isaaclab_visualizers.rerun is already in sys.modules by the time this test runs. Even if importlib.import_module did honour the patched __import__, it would return the cached entry first and never call __import__ at all.
As a result this test does not actually simulate the "missing package" scenario and will either:
- silently pass for the wrong reason (the import succeeds from cache, a config is created, but
resolved_types = {"rerun"}→missing = []→ noRuntimeErroris raised → thepytest.raisesblock fails), or - fail in CI rather than providing the intended coverage.
To reliably simulate a missing package, patch the importlib.import_module symbol that the production code imports, e.g.:
import importlib
import isaaclab.sim.simulation_context as sc_mod
monkeypatch.setattr(sc_mod, "importlib", ...)or patch sc_mod._create_default_visualizer_configs directly to return an empty list, and separately verify that the outer guard in initialize_visualizers raises RuntimeError when resolved_types is empty.
|
@greptile |
Summary
--visualizer), raise aRuntimeErrorinstead of silently logging the failureSplit from #4966 per reviewer feedback (simulation context change only).
Test plan
--visualizer <invalid>and verify RuntimeError is raised--visualizer newtonand verify normal initialization still works--visualizerand verify silent fallback behavior is unchanged