def _temporary_vfs(filenames_and_contents):
    """Creates a temporary VFS containing one or more files.

  Args:
    filenames_and_contents: A dict containing `{filename: contents}` pairs.
      The length of each filename must not exceed 98 characters.

  Yields:
    A `types.MJVFS` instance.

  Raises:
    Error: If a file cannot be added to the VFS, or if an error occurs when
      looking up the filename.
    ValueError: If the length of a filename exceeds 98 characters.
  """
    vfs = types.MJVFS()
    mjlib.mj_defaultVFS(vfs)
    for filename, contents in six.iteritems(filenames_and_contents):
        if len(filename) > _MAX_VFS_FILENAME_CHARACTERS:
            raise ValueError(
                _VFS_FILENAME_TOO_LONG.format(
                    length=len(filename),
                    limit=_MAX_VFS_FILENAME_CHARACTERS,
                    filename=filename))
        filename = util.to_binary_string(filename)
        contents = util.to_binary_string(contents)
        _, extension = os.path.splitext(filename)
        # For XML files we need to append a NULL byte, otherwise MuJoCo's parser
        # can sometimes read past the end of the string. However, we should *not*
        # do this for other file types (in particular for STL meshes, where this
        # causes MuJoCo's compiler to complain that the file size is incorrect).
        append_null = extension.lower() == b".xml"
        num_bytes = len(contents) + append_null
        retcode = mjlib.mj_makeEmptyFileVFS(vfs, filename, num_bytes)
        if retcode == 1:
            raise Error("Failed to create {!r}: VFS is full.".format(filename))
        elif retcode == 2:
            raise Error(
                "Failed to create {!r}: duplicate filename.".format(filename))
        file_index = mjlib.mj_findFileVFS(vfs, filename)
        if file_index == -1:
            raise Error("Could not find {!r} in the VFS".format(filename))
        vf = vfs.filedata[file_index]
        vf_as_char_arr = ctypes.cast(vf,
                                     ctypes.POINTER(ctypes.c_char * num_bytes))
        vf_as_char_arr.contents[:len(contents)] = contents
        if append_null:
            vf_as_char_arr.contents[-1] = _NULL
    try:
        yield vfs
    finally:
        mjlib.mj_deleteVFS(vfs)  # Ensure that we free the VFS afterwards.
Example #2
0
  def testFileNameTrimming(self):
    original_filename = (
        'THIS_IS_AN_EXTREMELY_LONG_FILENAME_THAT_WOULD_CAUSE_MUJOCO_TO_COMPLAIN'
        '_THAT_ITS_INTERNAL_LENGTH_LIMIT_IS_EXCEEDED_IF_NOT_TRIMMED_DOWN')
    extension = '.some_extension'
    asset = attribute.Asset(
        contents='', extension=extension, prefix=original_filename)
    vfs_filename = asset.get_vfs_filename()
    self.assertLen(vfs_filename, constants.MAX_VFS_FILENAME_LENGTH)

    vfs = types.MJVFS()
    mjlib.mj_defaultVFS(vfs)
    success_code = 0
    retval = mjlib.mj_makeEmptyFileVFS(
        vfs, util.to_binary_string(vfs_filename), 1)
    self.assertEqual(retval, success_code)
    mjlib.mj_deleteVFS(vfs)