Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- IsaacGym and PyBullet reported `joint_pos_target` in native DoF order while `joint_pos` is in sorted-name order; both now use `get_joint_names(sort=True)` (completes #12).
- `ParallelSimWrapper`: a worker that died during handler construction or `launch` surfaced as a bare `EOFError`/`ConnectionResetError` from the handshake and left the other workers running; the handshake now raises the worker's own traceback, `close()` tolerates dead workers, and a failed constructor tears the pool down.
- `hf_util.check_and_download_single`: a `roboverse_data/...` path evaluated from a working directory that is not the parent of `ROBOVERSE_DATA_DIR` was reported as a path-traversal attempt; the error now names the CWD / `ROBOVERSE_DATA_DIR` mismatch and where the asset already is. `test_check_and_download_single_falls_back_to_private_roboverse_data` no longer depends on the caller's `ROBOVERSE_DATA_DIR`.
- Newton backend works again on the pinned newton (1.5/1.6): `joint_target_pos`/`joint_target_vel` and `num_worlds` are forwarded to their new names, position targets use `joint_target_q_start` (on newton >= 1.5 they follow the `joint_q` layout, so envs after a free-floating object were driven to the wrong joints), the tiled camera uses the 1.4+ `RenderConfig`/`utils`/`update` API with an untextured fallback, and `CameraState.depth` is `(N, H, W)`.
- MuJoCo: `<size memory="512M">` is reserved by default; humanoid + mesh scenes no longer die with
`mj_stackAlloc: out of memory` (get_started/10_mount_camera.py).
- `hf_util`: a symlinked `roboverse_data` is no longer refused as path traversal; concurrent
Expand Down
68 changes: 68 additions & 0 deletions metasim/sim/newton/_newton_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
- Model attribute aliases body_key / joint_key / shape_key -> *_label
- add_mjcf(builder, path, **kw): ModelBuilder.add_mjcf method removed
in 1.2 but parse_mjcf in newton._src.utils.import_mjcf still works
- Model/Control joint_target_pos / joint_target_vel -> joint_target_q /
joint_target_qd (removed in 1.5; the new names are forwarded read/write)
- Model.num_worlds -> Model.world_count (renamed in 1.6)

Backward-compat guarantee:
- Anything that worked on newton < 1.2 still works.
Expand Down Expand Up @@ -123,6 +126,71 @@ def _install_model_attr_aliases():
_install_model_attr_aliases()


def _install_removed_attr_aliases():
"""Route attributes newton 1.5 removed back to their replacements.

newton 1.5 renamed ``joint_target_pos`` / ``joint_target_vel`` to ``joint_target_q`` /
``joint_target_qd`` on both ``Model`` and ``Control`` and shadows the old names with a
``RemovedAttribute`` descriptor that raises on read *and* write. The handler reads and
assigns both, so install read/write properties that forward to the new instance attributes.
Older newton (real attributes, no descriptor) is left untouched.
"""
try:
import newton
except ImportError:
return
renames = [("joint_target_pos", "joint_target_q"), ("joint_target_vel", "joint_target_qd")]
for cls_name in ("Model", "Control"):
cls = getattr(newton, cls_name, None)
if cls is None:
continue
for old, new in renames:
current = cls.__dict__.get(old)
if current is None or type(current).__name__ != "RemovedAttribute":
continue

def _get(self, _n=new):
return getattr(self, _n)

def _set(self, value, _n=new):
setattr(self, _n, value)

setattr(cls, old, property(_get, _set))


_install_removed_attr_aliases()


def _install_renamed_attr_aliases():
"""Forward attribute names newton renamed *without* leaving a descriptor behind.

``Model.num_worlds`` became ``Model.world_count`` in newton 1.6 and simply disappeared. The
alias reads the instance attribute when an older newton still sets it and falls back to the
new name otherwise, so both spellings work on every version.
"""
try:
import newton
except ImportError:
return
for cls_name, old, new in [("Model", "num_worlds", "world_count")]:
cls = getattr(newton, cls_name, None)
if cls is None or old in cls.__dict__:
continue

def _get(self, _old=old, _new=new):
if _old in self.__dict__:
return self.__dict__[_old]
return getattr(self, _new)

def _set(self, value, _old=old):
self.__dict__[_old] = value

setattr(cls, old, property(_get, _set))


_install_renamed_attr_aliases()


def add_mjcf(builder, mjcf_path: str, **kwargs):
"""Add an MJCF file to ``ModelBuilder``, version-portable.

Expand Down
Loading
Loading