def testDefaultIdentifier(self): mujoco = element.RootElement(model='test') body = mujoco.worldbody.add('body') joint_0 = body.add('freejoint') joint_1 = body.add('joint', type='hinge') self.assertIsNone(body.name) self.assertIsNone(joint_0.name) self.assertIsNone(joint_1.name) self.assertEqual(str(body), '<body>...</body>') self.assertEqual(str(joint_0), '<freejoint/>') self.assertEqual(str(joint_1), '<joint class="/" type="hinge"/>') self.assertEqual(body.full_identifier, '//unnamed_body_0') self.assertStartsWith(body.to_xml_string(pretty_print=False), '<body name="{:s}">'.format(body.full_identifier)) self.assertEqual(joint_0.full_identifier, '//unnamed_joint_0') self.assertEqual(joint_0.to_xml_string(pretty_print=False), '<freejoint name="{:s}"/>'.format(joint_0.full_identifier)) self.assertEqual(joint_1.full_identifier, '//unnamed_joint_1') self.assertEqual(joint_1.to_xml_string(pretty_print=False), '<joint name="{:s}" class="/" type="hinge"/>'.format( joint_1.full_identifier)) submujoco = copy.copy(mujoco) submujoco.model = 'submodel' mujoco.attach(submujoco) submujoco_body = submujoco.worldbody.body[0] self.assertEqual(submujoco_body.full_identifier, 'submodel//unnamed_body_0') self.assertEqual(submujoco_body.freejoint.full_identifier, 'submodel//unnamed_joint_0') self.assertEqual(submujoco_body.joint[0].full_identifier, 'submodel//unnamed_joint_1')
def testSetAndGetAttributes(self): mujoco = element.RootElement(model='test') foo_attribs = dict(name='foo', pos=[1, 2, 3, 4], quat=[0, 1, 0, 0]) with self.assertRaisesRegexp(ValueError, 'no more than 3 entries'): foo = mujoco.worldbody.add('body', **foo_attribs) # failed creationg should not cause the identifier 'foo' to be registered with self.assertRaises(KeyError): mujoco.namescope.get('body', 'foo') foo_attribs['pos'] = [1, 2, 3] foo = mujoco.worldbody.add('body', **foo_attribs) self._test_attributes(foo, expected_values=foo_attribs) foo_attribs['name'] = 'bar' foo_attribs['pos'] = [1, 2, 3, 4] foo_attribs['childclass'] = 'klass' with self.assertRaisesRegexp(ValueError, 'no more than 3 entries'): foo.set_attributes(**foo_attribs) # failed assignment should not cause the identifier 'bar' to be registered with self.assertRaises(KeyError): mujoco.namescope.get('body', 'bar') foo_attribs['pos'] = [1, 2, 3] foo.set_attributes(**foo_attribs) self._test_attributes(foo, expected_values=foo_attribs) actual_foo_attribs = foo.get_attributes() for attribute_name, value in six.iteritems(foo_attribs): np.testing.assert_array_equal( actual_foo_attribs.pop(attribute_name), value) for value in six.itervalues(actual_foo_attribs): self.assertIsNone(value)
def setUp(self): super().setUp() schema.override_schema(os.path.join(ASSETS_DIR, FAKE_SCHEMA_FILENAME)) self._alpha = namescope.NameScope('alpha', None) self._beta = namescope.NameScope('beta', None) self._beta.parent = self._alpha self._mujoco = element.RootElement() self._mujoco.namescope.parent = self._beta
def setUp(self): schema.MUJOCO = FAKE_MUJOCO schema.FINDABLE_NAMESPACES = FAKE_FINDABLE_NAMESPACES self._alpha = namescope.NameScope('alpha', None) self._beta = namescope.NameScope('beta', None) self._beta.parent = self._alpha self._mujoco = element.RootElement() self._mujoco.namescope.parent = self._beta
def testAddWithInvalidAttribute(self): mujoco = element.RootElement(model='test') with self.assertRaisesRegexp(AttributeError, 'not a valid attribute'): mujoco.worldbody.add('body', name='foo', invalid_attribute='some_value') self.assertFalse(mujoco.worldbody.body) self.assertIsNone(mujoco.worldbody.find('body', 'foo'))
def testDictLikeInterface(self): mujoco = element.RootElement(model='test') elem = mujoco.worldbody.add('body') with self.assertRaisesRegex(TypeError, 'object is not subscriptable'): _ = elem['foo'] with self.assertRaisesRegex(TypeError, 'does not support item assignment'): elem['foo'] = 'bar' with self.assertRaisesRegex(TypeError, 'does not support item deletion'): del elem['foo']
def testAssetInheritance(self): parent = element.RootElement(model='parent') child = element.RootElement(model='child') grandchild = element.RootElement(model='grandchild') ext = '.png' parent_str = b'I belong to the parent' child_str = b'I belong to the child' grandchild_str = b'I belong to the grandchild' parent_vfs_name, child_vfs_name, grandchild_vfs_name = ( hashlib.sha1(s).hexdigest() + ext for s in (parent_str, child_str, grandchild_str)) parent_ph = mjcf.Asset(contents=parent_str, extension=ext) child_ph = mjcf.Asset(contents=child_str, extension=ext) grandchild_ph = mjcf.Asset(contents=grandchild_str, extension=ext) parent.asset.add('texture', name='parent_tex', file=parent_ph) child.asset.add('texture', name='child_tex', file=child_ph) grandchild.asset.add('texture', name='grandchild_tex', file=grandchild_ph) parent.attach(child) child.attach(grandchild) # The grandchild should only return its own assets. self.assertDictEqual({grandchild_vfs_name: grandchild_str}, grandchild.get_assets()) # The child should return its own assets plus those of the grandchild. self.assertDictEqual( { child_vfs_name: child_str, grandchild_vfs_name: grandchild_str }, child.get_assets()) # The parent should return everything. self.assertDictEqual( { parent_vfs_name: parent_str, child_vfs_name: child_str, grandchild_vfs_name: grandchild_str }, parent.get_assets())
def testFileFromAssetsDict(self): prefix = 'fake_filename' extension = '.whatever' path = 'invalid/path/' + prefix + extension contents = 'Fake contents' assets = {path: contents} mujoco = element.RootElement(assets=assets) text_file = mujoco.files.add('text', file=path) expected_value = attribute.Asset( contents=contents, extension=extension, prefix=prefix) self.assertEqual(text_file.file, expected_value)
def testDictLikeInterface(self): mujoco = element.RootElement(model='test') elem = mujoco.worldbody.add('body') if six.PY3: subscript_error_regex = 'object is not subscriptable' else: subscript_error_regex = 'no attribute \'__getitem__\'' with self.assertRaisesRegexp(TypeError, subscript_error_regex): _ = elem['foo'] with self.assertRaisesRegexp(TypeError, 'does not support item assignment'): elem['foo'] = 'bar' with self.assertRaisesRegexp(TypeError, 'does not support item deletion'): del elem['foo']
def testInvalidAttr(self): mujoco = element.RootElement(model='test') invalid_attrib_name = 'foobar' def test_invalid_attr_recursively(mjcf_element): self.assertNotHasAttr(mjcf_element, invalid_attrib_name) self.assertNotIn(invalid_attrib_name, dir(mjcf_element)) with self.assertRaisesRegexp(AttributeError, 'object has no attribute'): getattr(mjcf_element, invalid_attrib_name) with self.assertRaisesRegexp(AttributeError, 'can\'t set attribute'): setattr(mjcf_element, invalid_attrib_name, 'value') with self.assertRaisesRegexp(AttributeError, 'object has no attribute'): delattr(mjcf_element, invalid_attrib_name) for child in mjcf_element.all_children(): test_invalid_attr_recursively(child) test_invalid_attr_recursively(mujoco)
def testAttributeError(self): mjcf_model = element.RootElement(model='test') mjcf_model.worldbody._spec = None try: _ = mjcf_model.worldbody.tag except AttributeError: _, err, tb = sys.exc_info() else: self.fail('AttributeError was not raised.') # Test that the error comes from the fact that we've set `_spec = None`. self.assertEqual(str(err), '\'NoneType\' object has no attribute \'name\'') _, _, func_name, _ = traceback.extract_tb(tb)[-1] # Test that the error comes from the `root` property, not `__getattr__`. self.assertEqual(func_name, 'tag')
def testSameness(self): mujoco = element.RootElement(model='test') body_1 = mujoco.worldbody.add('body', pos=[0, 1, 2], quat=[0, 1, 0, 1]) site_1 = body_1.add('site', pos=[0, 1, 2], quat=[0, 1, 0, 1]) geom_1 = body_1.add('geom', pos=[0, 1, 2], quat=[0, 1, 0, 1]) for elem in (body_1, site_1, geom_1): self.assertIsSame(elem, elem) # strict ordering NOT required: adding geom and site is different order body_2 = mujoco.worldbody.add('body', pos=[0, 1, 2], quat=[0, 1, 0, 1]) geom_2 = body_2.add('geom', pos=[0, 1, 2], quat=[0, 1, 0, 1]) site_2 = body_2.add('site', pos=[0, 1, 2], quat=[0, 1, 0, 1]) elems_1 = (body_1, site_1, geom_1) elems_2 = (body_2, site_2, geom_2) for i, j in itertools.product(range(len(elems_1)), range(len(elems_2))): if i == j: self.assertIsSame(elems_1[i], elems_2[j]) else: self.assertIsNotSame(elems_1[i], elems_2[j]) # on-demand child body_1.add('inertial', pos=[0, 0, 0], mass=1) self.assertIsNotSame(body_1, body_2) body_2.add('inertial', pos=[0, 0, 0], mass=1) self.assertIsSame(body_1, body_2) # different number of children subbody_1 = body_1.add('body', pos=[0, 0, 1]) self.assertIsNotSame(body_1, body_2) # attribute mismatch subbody_2 = body_2.add('body') self.assertIsNotSame(subbody_1, subbody_2) self.assertIsNotSame(body_1, body_2) subbody_2.pos = [0, 0, 1] self.assertIsSame(subbody_1, subbody_2) self.assertIsSame(body_1, body_2) # grandchild attribute mismatch subbody_1.add('joint', type='hinge') subbody_2.add('joint', type='ball') self.assertIsNotSame(body_1, body_2)
def testAdd(self): mujoco = element.RootElement(model='test') # repeated elements body_foo_attributes = dict(name='foo', pos=[0, 1, 0], quat=[0, 1, 0, 0]) body_foo = mujoco.worldbody.add('body', **body_foo_attributes) self.assertEqual(body_foo.tag, 'body') joint_foo_attributes = dict(name='foo', type='free') joint_foo = body_foo.add('joint', **joint_foo_attributes) self.assertEqual(joint_foo.tag, 'joint') self._test_properties(body_foo, parent=mujoco.worldbody, root=mujoco) self._test_attributes(body_foo, expected_values=body_foo_attributes) self._test_children(body_foo) self._test_properties(joint_foo, parent=body_foo, root=mujoco) self._test_attributes(joint_foo, expected_values=joint_foo_attributes) self._test_children(joint_foo) # non-repeated, on-demand elements self.assertIsNone(body_foo.inertial) body_foo_inertial_attributes = dict(mass=1.0, pos=[0, 0, 0]) body_foo_inertial = body_foo.add('inertial', **body_foo_inertial_attributes) self._test_properties(body_foo_inertial, parent=body_foo, root=mujoco) self._test_attributes(body_foo_inertial, expected_values=body_foo_inertial_attributes) self._test_children(body_foo_inertial) with six.assertRaisesRegex(self, ValueError, '<inertial> child already exists'): body_foo.add('inertial', **body_foo_inertial_attributes) # non-repeated, non-on-demand elements with six.assertRaisesRegex(self, ValueError, '<compiler> child already exists'): mujoco.add('compiler') self.assertIsNotNone(mujoco.compiler) with six.assertRaisesRegex(self, ValueError, '<default> child already exists'): mujoco.add('default') self.assertIsNotNone(mujoco.default)
def testTendonSameness(self): mujoco = element.RootElement(model='test') spatial_1 = mujoco.tendon.add('spatial') spatial_1.add('site', site='foo') spatial_1.add('geom', geom='bar') spatial_2 = mujoco.tendon.add('spatial') spatial_2.add('site', site='foo') spatial_2.add('geom', geom='bar') self.assertIsSame(spatial_1, spatial_2) # strict ordering is required spatial_3 = mujoco.tendon.add('spatial') spatial_3.add('site', site='foo') spatial_3.add('geom', geom='bar') spatial_4 = mujoco.tendon.add('spatial') spatial_4.add('geom', geom='bar') spatial_4.add('site', site='foo') self.assertIsNotSame(spatial_3, spatial_4)
def _parse(xml_root, escape_separators=False, model_dir='', resolve_references=True, assets=None): """Parses a complete MJCF model from an XML. Args: xml_root: An `etree.Element` object. escape_separators: (optional) A boolean, whether to replace '/' characters in element identifiers. If `False`, any '/' present in the XML causes a ValueError to be raised. model_dir: (optional) Path to the directory containing the model XML file. This is used to prefix the paths of all asset files. resolve_references: (optional) A boolean indicating whether the parser should attempt to resolve reference attributes to a corresponding element. assets: (optional) A dictionary of pre-loaded assets, of the form `{filename: bytestring}`. If present, PyMJCF will search for assets in this dictionary before attempting to load them from the filesystem. Returns: An `mjcf.RootElement`. Raises: ValueError: If `xml_root`'s tag is not 'mujoco.*'. """ assets = assets or {} if not xml_root.tag.startswith('mujoco'): raise ValueError( 'Root element of the XML should be <mujoco.*>: got <{}>'.format( xml_root.tag)) with debugging.freeze_current_stack_trace(): # Recursively parse any included XML files. to_include = [] for include_tag in xml_root.findall('include'): try: # First look for the path to the included XML file in the assets dict. path_or_xml_string = assets[include_tag.attrib['file']] parsing_func = from_xml_string except KeyError: # If it's not present in the assets dict then attempt to load the XML # from the filesystem. path_or_xml_string = os.path.join(model_dir, include_tag.attrib['file']) parsing_func = from_path included_mjcf = parsing_func(path_or_xml_string, escape_separators=escape_separators, resolve_references=resolve_references, assets=assets) to_include.append(included_mjcf) # We must remove <include/> tags before parsing the main XML file, since # these are a schema violation. xml_root.remove(include_tag) # Parse the main XML file. try: model = xml_root.attrib.pop('model') except KeyError: model = None mjcf_root = element.RootElement(model=model, model_dir=model_dir, assets=assets) _parse_children(xml_root, mjcf_root, escape_separators) # Merge in the included XML files. for included_mjcf in to_include: # The included MJCF might have been automatically assigned a model name # that conficts with that of `mjcf_root`, so we override it here. included_mjcf.model = mjcf_root.model mjcf_root.include_copy(included_mjcf) if resolve_references: mjcf_root.resolve_references() return mjcf_root
def testChildren(self): mujoco = element.RootElement(model='test') self._test_children(mujoco, recursive=True)
def testAttributes(self): mujoco = element.RootElement(model='test') mujoco.default.dclass = 'main' self._test_attributes(mujoco, recursive=True)
def testProperties(self): mujoco = element.RootElement(model='test') self.assertIsInstance(mujoco.namescope, namescope.NameScope) self._test_properties(mujoco, parent=None, root=mujoco, recursive=True)