Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
685c9ca5bThis file is pinned to the selected release. Download the original to open it in your design tool.
# Copyright 2022 DeepMind Technologies Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Python bindings for MuJoCo."""
import ctypes
import ctypes.util
import os
import platform
import subprocess
from typing import Any, IO, Union, Sequence
from typing_extensions import TypeAlias
import warnings
import zipfile
# Extend the path to enable multiple directories to contribute to the same
# package. Without this line, the `mujoco-mjx` package would not be able to
# be discovered by import. For more information, see: https://packaging.python.org/guides/packaging-namespace-packages/#pkgutil-style-namespace-packages
# NOTE: As per the Python Packaging User Guide linked above, the preferable way
# of declaring the namespace package is to use the native namespace packages.
# This seems non-trivial at the current state of the project, however.
# For more information, see: https://github.com/google-deepmind/mujoco/issues/2119
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
_SYSTEM = platform.system()
if _SYSTEM == 'Windows':
ctypes.WinDLL(os.path.join(os.path.dirname(__file__), 'mujoco.dll'))
elif _SYSTEM == 'Darwin':
proc_translated = subprocess.run(
['sysctl', '-n', 'sysctl.proc_translated'], capture_output=True).stdout
try:
is_rosetta = bool(int(proc_translated))
except ValueError:
is_rosetta = False
if is_rosetta and platform.machine() == 'x86_64':
raise ImportError(
'You are running an x86_64 build of Python on an Apple Silicon '
'machine. This is not supported by MuJoCo. Please install and run a '
'native, arm64 build of Python.')
from mujoco import _specs
from mujoco import _structs
from mujoco._callbacks import *
from mujoco._constants import *
from mujoco._enums import *
from mujoco._errors import *
from mujoco._functions import *
from mujoco._specs import *
from mujoco._structs import *
try:
from mujoco._render import * # pytype: disable=import-error
except ImportError:
pass
try:
from mujoco.rendering.classic.renderer import Renderer # pytype: disable=import-error
except ImportError:
pass
try:
from mujoco.rendering.classic.gl_context import * # pytype: disable=import-error
except ImportError:
pass
MjStruct: TypeAlias = Union[
_specs.MjsBody,
_specs.MjsFrame,
_specs.MjsGeom,
_specs.MjsJoint,
_specs.MjsLight,
_specs.MjsMaterial,
_specs.MjsSite,
_specs.MjsMesh,
_specs.MjsSkin,
_specs.MjsTexture,
_specs.MjsText,
_specs.MjsTuple,
_specs.MjsCamera,
_specs.MjsFlex,
_specs.MjsHField,
_specs.MjsKey,
_specs.MjsNumeric,
_specs.MjsPair,
_specs.MjsExclude,
_specs.MjsEquality,
_specs.MjsTendon,
_specs.MjsSensor,
_specs.MjsActuator,
_specs.MjsPlugin,
]
def to_zip(spec: _specs.MjSpec, file: Union[str, IO[bytes]]) -> None:
"""Converts an MjSpec to a zip file.
Args:
spec: The mjSpec to save to a file.
file: The path to the file to save to or the file object to write to.
"""
files_to_zip = spec.assets
files_to_zip[spec.modelname + '.xml'] = spec.to_xml()
if isinstance(file, str):
directory = os.path.dirname(file)
os.makedirs(directory, exist_ok=True)
file = open(file, 'wb')
with zipfile.ZipFile(file, 'w') as zip_file:
for filename, contents in files_to_zip.items():
zip_info = zipfile.ZipInfo(filename)
zip_file.writestr(zip_info, contents)
def from_zip(file: Union[str, IO[bytes]]) -> _specs.MjSpec:
"""Reads a zip file and returns an MjSpec.
Args:
file: The path to the file to read from or the file object to read from.
Returns:
An MjSpec object.
"""
assets = {}
xml_string = None
if isinstance(file, str):
file = open(file, 'rb')
if not zipfile.is_zipfile(file):
raise ValueError(f'File {file} is not a zip file.')
with zipfile.ZipFile(file, 'r') as zip_file:
xml_dir = None
for zip_info in zip_file.infolist():
if not zip_info.filename.endswith(os.path.sep):
with zip_file.open(zip_info.filename) as f:
if zip_info.filename.endswith('.xml'):
xml_string = f.read()
xml_dir = os.path.dirname(zip_info.filename)
else:
assets[zip_info.filename] = f.read()
if not xml_string:
raise ValueError('No XML file found in zip file.')
relative_assets = {}
for key, value in assets.items():
new_key = os.path.relpath(key, xml_dir)
relative_assets[new_key] = value
assets = relative_assets
return _specs.MjSpec.from_string(xml_string, assets=assets)
class _MjBindModel:
"""Wrapper for MjModel that allows binding multiple specs."""
def __init__(self, elements: Sequence[Any]):
object.__setattr__(self, 'elements', elements)
def __getattr__(self, key: str):
items = []
for e in self.elements:
items.extend(getattr(e, key))
return items
def __setattr__(self, key: str, value: Any):
raise AttributeError(f'Cannot set {key} on MjModel.')
class _MjBindData:
"""Wrapper for MjData that allows binding multiple specs."""
def __init__(self, elements: Sequence[Any]):
object.__setattr__(self, 'elements', elements)
def __getattr__(self, key: str):
items = []
for e in self.elements:
items.extend(getattr(e, key))
return items
def __setattr__(self, key: str, value: Any):
value_it = iter(value)
for element in self.elements:
setattr(element, key, next(value_it))
def _bind_model(
model: _structs.MjModel, specs: Union[Sequence[MjStruct], MjStruct]
):
"""Bind a Mujoco spec to a mjModel.
Args:
model: The mjModel to bind to.
specs: The mjSpec elements to use for binding, can be a single element or a
sequence.
Returns:
A MjModelGroupedViews object or a list of the same type.
"""
if isinstance(specs, Sequence):
return _MjBindModel([model.bind_scalar(s) for s in specs])
else:
return model.bind_scalar(specs)
def _bind_data(
data: _structs.MjData, specs: Union[Sequence[MjStruct], MjStruct]
):
"""Bind a Mujoco spec to a mjData.
Args:
data: The mjData to bind to.
specs: The mjSpec elements to use for binding, can be a single element or a
sequence.
Returns:
A MjDataGroupedViews object or a list of the same type.
"""
if isinstance(specs, Sequence):
return _MjBindData([data.bind_scalar(s) for s in specs])
else:
return data.bind_scalar(specs)
_specs.MjSpec.from_zip = from_zip
_specs.MjSpec.to_zip = to_zip
_structs.MjData.bind = _bind_data
_structs.MjModel.bind = _bind_model
HEADERS_DIR = os.path.join(os.path.dirname(__file__), 'include/mujoco')
PLUGINS_DIR = os.path.join(os.path.dirname(__file__), 'plugin')
PLUGIN_HANDLES = []
def _load_all_bundled_plugins():
for directory, _, filenames in os.walk(PLUGINS_DIR):
for filename in filenames:
if os.path.splitext(filename)[-1] in [".dll", ".dylib", ".so"]:
PLUGIN_HANDLES.append(ctypes.CDLL(os.path.join(directory, filename)))
elif filename == "__init__.py":
pass
else:
warnings.warn('Ignoring non-library in plugin directory: '
f'{os.path.join(directory, filename)}', ImportWarning)
_load_all_bundled_plugins()
__version__ = mj_versionString() # pylint: disable=undefined-variable