diff --git a/dm_control/mujoco/index.py b/dm_control/mujoco/index.py index 76e534cc..f7dd7052 100644 --- a/dm_control/mujoco/index.py +++ b/dm_control/mujoco/index.py @@ -636,9 +636,9 @@ def struct_indexer(struct, struct_name, size_to_axis_indexer): for field_name in array_sizes: - # Skip over structured arrays and fields that have sizes but aren't numpy - # arrays, such as text fields and contacts (b/34805932). - attr = getattr(struct, field_name) + # Skip fields removed by newer MuJoCo versions even if older generated + # metadata still lists them, as well as non-array and structured fields. + attr = getattr(struct, field_name, None) if not isinstance(attr, np.ndarray) or attr.dtype.fields: continue diff --git a/dm_control/mujoco/index_test.py b/dm_control/mujoco/index_test.py index 3eaeae53..47f5d220 100644 --- a/dm_control/mujoco/index_test.py +++ b/dm_control/mujoco/index_test.py @@ -14,6 +14,7 @@ # ============================================================================ import collections +from unittest import mock from absl.testing import absltest from absl.testing import parameterized @@ -73,6 +74,25 @@ def setUp(self): self._data_indexers = index.struct_indexer(self._data, 'mjdata', self._size_to_axis_indexer) + def testStructIndexerSkipsRemovedDataFields(self): + # MuJoCo may remove a field that remains in generated array_sizes. + with mock.patch.dict(sizes.array_sizes['mjdata'], + {'removed_data_field': ('nbody',)}): + named = index.struct_indexer( + self._data, 'mjdata', self._size_to_axis_indexer) + self.assertNotIn('removed_data_field', named._asdict()) + self.assertIn('xpos', named._asdict()) + np.testing.assert_array_equal( + named.xpos['cart'], self._data.xpos[1]) + + def testStructIndexerSkipsRemovedModelFields(self): + with mock.patch.dict(sizes.array_sizes['mjmodel'], + {'removed_model_field': ('ngeom',)}): + named = index.struct_indexer( + self._model, 'mjmodel', self._size_to_axis_indexer) + self.assertNotIn('removed_model_field', named._asdict()) + self.assertIn('geom_rgba', named._asdict()) + def assertIndexExpressionEqual(self, expected, actual): try: if isinstance(expected, tuple):