Add checked_apply helper for forwarding configclass fields onto upstream dataclasses#5365
Add checked_apply helper for forwarding configclass fields onto upstream dataclasses#5365hujc7 wants to merge 1 commit intoisaac-sim:developfrom
Conversation
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Summary
This PR introduces upstream_set, a defensive helper that guards against silent dead-attribute writes to upstream library configuration objects (dataclasses, modules). The motivation is sound—PR #5289 showed a real-world case where a Newton rename caused a stale assignment to silently become a no-op. The implementation is clean, well-tested, and correctly migrates the single existing cross-dep write site.
Architecture Impact
Self-contained. The new upstream_set utility has no dependencies beyond the standard library (dataclasses). The single migration point in NewtonManager.create_builder is low-risk—it affects builder initialization, not hot-path physics stepping. No other callers are impacted.
Implementation Verdict
Ship it — Minor documentation suggestion below, but nothing blocking.
Test Coverage
Thorough. The 5 unit tests cover:
- ✅ Happy path: declared field is set correctly
- ✅ Undeclared field raises
AttributeErrorwith helpful message containing field names - ✅ Failed write doesn't create spurious attribute
- ✅ Module attribute that exists is set
- ✅ Module attribute that doesn't exist raises
The tests use a synthetic _DummyCfg dataclass and types.ModuleType, which is appropriate—no need to depend on Newton's actual ShapeConfig in the isaaclab package tests.
CI Status
No CI results available yet. The PR description notes local pre-commit passed; awaiting CI run.
Findings
🔵 Improvement: source/isaaclab/isaaclab/utils/upstream.py:24 — Missing type annotation for value parameter
The value parameter lacks a type hint. While Any is semantically correct here (the function is type-agnostic), adding it improves IDE support and documentation consistency:
def upstream_set(obj: object, name: str, value: Any) -> None:This requires from typing import Any (or from __future__ import annotations already present handles forward refs, but Any still needs import).
🔵 Improvement: source/isaaclab/isaaclab/utils/upstream.py:41-45 — Consider using type(obj) directly for module type path
For modules, type(obj).__module__ is builtins and type(obj).__name__ is module, producing builtins.module which is less helpful than the actual module's __name__. Consider:
if isinstance(obj, types.ModuleType):
type_path = obj.__name__
else:
type_path = f"{type(obj).__module__}.{type(obj).__name__}"This would produce fake_upstream has no attribute 'quiet' instead of builtins.module has no attribute 'quiet'. Minor improvement for debugging module config writes.
🔵 Improvement: source/isaaclab/test/utils/test_upstream.py:52-55 — Test could verify error message content for module case
The module test verifies the exception is raised but doesn't assert on the message content. For consistency with test_undeclared_field_raises_attribute_error, consider:
def test_module_attribute_that_does_not_exist_raises():
mod = types.ModuleType("fake_upstream")
with pytest.raises(AttributeError) as excinfo:
upstream_set(mod, "quiet", True)
assert "quiet" in str(excinfo.value)🔵 Improvement: source/isaaclab/isaaclab/utils/init.pyi:7-57 — Unrelated import reordering
The diff shows the .pyi stub was reformatted (alphabetized imports). This is fine—likely from running ruff format—but the diff is noisier than necessary. Not a problem, just noting it's unrelated to the feature.
Overall this is a well-executed, focused PR that addresses a real problem with an appropriately minimal solution. The convention documented in AGENTS.md ensures future cross-dep writes will use the helper.
Greptile SummaryThis PR adds Confidence Score: 5/5Safe to merge — all findings are minor style suggestions with no impact on correctness or behavior. The implementation is correct and well-tested. The two comments are P2 style preferences (consolidating the dual No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["checked_apply(src, target)"] --> B{src has\n__dataclass_fields__?}
B -- No --> C[raise TypeError]
B -- Yes --> D["for f in dataclasses.fields(src)"]
D --> E{hasattr\ntarget, f.name?}
E -- No --> F[raise AttributeError\nwith target path + field name]
E -- Yes --> G["setattr(target, f.name,\ngetattr(src, f.name))"]
G --> D
D -- done --> H[return None]
Reviews (2): Last reviewed commit: "Add checked_apply helper for forwarding ..." | Re-trigger Greptile |
351e5af to
fcfb8ac
Compare
Adds: 1. NewtonShapeCfg wrapper exposing per-shape collision defaults (margin, gap) via NewtonCfg.default_shape_cfg. NewtonManager. create_builder forwards it onto Newton's upstream ModelBuilder.default_shape_cfg via isaaclab.utils.checked_apply (PR isaac-sim#5365). Replaces the hardcoded gap=0.01 line with a single typed wrapper — the previous code never set margin, leaving it at Newton's upstream default of 0.0 and breaking all non-Anymal-D robots on triangle-mesh terrain. 2. Hoists Octi's per-env Anymal-D RoughPhysicsCfg into the shared LocomotionVelocityRoughEnvCfg so every rough-terrain env (Go1, Go2, A1, Anymal-B/C, H1, Cassie, Digit, G1) inherits identical Newton physics. The shared preset opts in to default_shape_cfg=NewtonShapeCfg(margin=0.01). 3. Per-env Newton-only tweaks where ablation showed measurable gains: - Go1: leg armature preset for joint stability. 4. Replaces additive (-5, 5) kg default on EventsCfg.add_base_mass with multiplicative (1/1.25, 1.25) log-uniform scale — scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. - isaaclab_newton 0.5.21 -> 0.5.22 - isaaclab_tasks 1.5.24 -> 1.5.25
fcfb8ac to
698ab48
Compare
|
@greptileai Review — please re-review the latest commits. @isaaclab-review-bot Please re-review. Force-pushed with significant scope change since the previous review:
|
Adds: 1. NewtonShapeCfg wrapper exposing per-shape collision defaults (margin, gap) via NewtonCfg.default_shape_cfg. NewtonManager. create_builder forwards it onto Newton's upstream ModelBuilder.default_shape_cfg via isaaclab.utils.checked_apply (PR isaac-sim#5365). Replaces the hardcoded gap=0.01 line with a single typed wrapper — the previous code never set margin, leaving it at Newton's upstream default of 0.0 and breaking all non-Anymal-D robots on triangle-mesh terrain. 2. Hoists Octi's per-env Anymal-D RoughPhysicsCfg into the shared LocomotionVelocityRoughEnvCfg so every rough-terrain env (Go1, Go2, A1, Anymal-B/C, H1, Cassie, Digit, G1) inherits identical Newton physics. The shared preset opts in to default_shape_cfg=NewtonShapeCfg(margin=0.01). 3. Per-env Newton-only tweaks where ablation showed measurable gains: - Go1: leg armature preset for joint stability. 4. Replaces additive (-5, 5) kg default on EventsCfg.add_base_mass with multiplicative (1/1.25, 1.25) log-uniform scale — scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. - isaaclab_newton 0.5.21 -> 0.5.22 - isaaclab_tasks 1.5.24 -> 1.5.25
…eam dataclasses When an Isaac Lab configclass mirrors an upstream library's dataclass (for example NewtonShapeCfg → Newton's ShapeConfig), bare setattr loops are fragile: if upstream renames or removes a field, every write becomes a silent no-op (the failure mode PR isaac-sim#5289 fixed for ShapeConfig.contact_margin → margin). Add isaaclab.utils.checked_apply(src, target): - Iterates fields(src) — single source of truth for declared fields - Raises AttributeError if target lacks a declared field — failure surfaces at startup, not at runtime - One-line apply at the call site, no per-field setattr noise
698ab48 to
f7202c0
Compare
Adds: 1. NewtonShapeCfg wrapper exposing per-shape collision defaults (margin, gap) via NewtonCfg.default_shape_cfg. NewtonManager. create_builder forwards it onto Newton's upstream ModelBuilder.default_shape_cfg via isaaclab.utils.checked_apply (PR isaac-sim#5365). Replaces the hardcoded gap=0.01 line with a single typed wrapper — the previous code never set margin, leaving it at Newton's upstream default of 0.0 and breaking all non-Anymal-D robots on triangle-mesh terrain. 2. Hoists Octi's per-env Anymal-D RoughPhysicsCfg into the shared LocomotionVelocityRoughEnvCfg so every rough-terrain env (Go1, Go2, A1, Anymal-B/C, H1, Cassie, Digit, G1) inherits identical Newton physics. The shared preset opts in to default_shape_cfg=NewtonShapeCfg(margin=0.01). 3. Per-env Newton-only tweaks where ablation showed measurable gains: - Go1: leg armature preset for joint stability. 4. Replaces additive (-5, 5) kg default on EventsCfg.add_base_mass with multiplicative (1/1.25, 1.25) log-uniform scale — scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. - isaaclab_newton 0.5.21 -> 0.5.22 - isaaclab_tasks 1.5.24 -> 1.5.25
Adds: 1. NewtonShapeCfg wrapper exposing per-shape collision defaults (margin, gap) via NewtonCfg.default_shape_cfg. NewtonManager. create_builder forwards it onto Newton's upstream ModelBuilder.default_shape_cfg via isaaclab.utils.checked_apply (PR isaac-sim#5365). Replaces the hardcoded gap=0.01 line with a single typed wrapper — the previous code never set margin, leaving it at Newton's upstream default of 0.0 and breaking all non-Anymal-D robots on triangle-mesh terrain. 2. Hoists Octi's per-env Anymal-D RoughPhysicsCfg into the shared LocomotionVelocityRoughEnvCfg so every rough-terrain env (Go1, Go2, A1, Anymal-B/C, H1, Cassie, Digit, G1) inherits identical Newton physics. The shared preset opts in to default_shape_cfg=NewtonShapeCfg(margin=0.01). 3. Per-env Newton-only tweaks where ablation showed measurable gains: - Go1: leg armature preset for joint stability. 4. Replaces additive (-5, 5) kg default on EventsCfg.add_base_mass with multiplicative (1/1.25, 1.25) log-uniform scale — scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. - isaaclab_newton 0.5.21 -> 0.5.22 - isaaclab_tasks 1.5.24 -> 1.5.25
Adds: 1. NewtonShapeCfg wrapper exposing per-shape collision defaults (margin, gap) via NewtonCfg.default_shape_cfg. NewtonManager. create_builder forwards it onto Newton's upstream ModelBuilder.default_shape_cfg via isaaclab.utils.checked_apply (PR isaac-sim#5365). Replaces the hardcoded gap=0.01 line with a single typed wrapper — the previous code never set margin, leaving it at Newton's upstream default of 0.0 and breaking all non-Anymal-D robots on triangle-mesh terrain. 2. Hoists Octi's per-env Anymal-D RoughPhysicsCfg into the shared LocomotionVelocityRoughEnvCfg so every rough-terrain env (Go1, Go2, A1, Anymal-B/C, H1, Cassie, Digit, G1) inherits identical Newton physics. The shared preset opts in to default_shape_cfg=NewtonShapeCfg(margin=0.01). 3. Per-env Newton-only tweaks where ablation showed measurable gains: - Go1: leg armature preset for joint stability. 4. Replaces additive (-5, 5) kg default on EventsCfg.add_base_mass with multiplicative (1/1.25, 1.25) log-uniform scale — scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. - isaaclab_newton 0.5.21 -> 0.5.22 - isaaclab_tasks 1.5.24 -> 1.5.25
Description
Adds
isaaclab.utils.checked_applyfor forwarding the declared fields of one dataclass onto another, raisingAttributeErrorif the target is missing a declared field.The use case is Isaac Lab configclasses that mirror an upstream library's dataclass (for example, Newton's
ShapeConfig). Baresetattrloops are fragile: if upstream renames or removes a field, every write becomes a silent no-op (the failure mode PR #5289 fixed forShapeConfig.contact_margin→margin). Withchecked_apply, the failure surfaces at startup with a clear message instead of degrading runtime behavior.API
Internally:
dataclasses.fields(src)— single source of truth for declared fields.AttributeErroriftargetlacks a declared field.srcwithTypeError.What's included
source/isaaclab/isaaclab/utils/configclass.py—checked_applyfunction (lives next to@configclasssince it operates on dataclasses).source/isaaclab/isaaclab/utils/__init__.pyi— export.source/isaaclab/test/utils/test_configclass.py— three tests (forwards all fields, raises on missing target field, rejects non-dataclass src).source/isaaclab/docs/CHANGELOG.rst—4.6.13entry.source/isaaclab/config/extension.toml— version bump.Dependents
This PR is a dependency for the rough-terrain Newton stack:
checked_applyto forwardNewtonShapeCfgonto Newton's upstreamShapeConfig. Without it,default_shape_cfg.marginis left at Newton's upstream default of0.0, breaking all non-Anymal-D robots on triangle-mesh terrain.Type of change
Checklist
test_configclass.py)