diff --git a/docs/api/vector.md b/docs/api/vector.md index c1e3e7b86..dbe0f5374 100644 --- a/docs/api/vector.md +++ b/docs/api/vector.md @@ -53,6 +53,10 @@ vector/utils The ``EnvSpec`` of the environment normally set during :py:meth:`gymnasium.make_vec` +.. autoattribute:: gymnasium.vector.VectorEnv.metadata + + The metadata of the environment containing rendering modes, rendering fps, etc + .. autoattribute:: gymnasium.vector.VectorEnv.render_mode The render mode of the environment which should follow similar specifications to `Env.render_mode`. diff --git a/gymnasium/core.py b/gymnasium/core.py index f4fd37099..638759044 100644 --- a/gymnasium/core.py +++ b/gymnasium/core.py @@ -39,7 +39,7 @@ class Env(Generic[ObsType, ActType]): - :attr:`action_space` - The Space object corresponding to valid actions, all valid actions should be contained within the space. - :attr:`observation_space` - The Space object corresponding to valid observations, all valid observations should be contained within the space. - :attr:`spec` - An environment spec that contains the information used to initialize the environment from :meth:`gymnasium.make` - - :attr:`metadata` - The metadata of the environment, i.e. render modes, render fps + - :attr:`metadata` - The metadata of the environment, e.g., `{"render_modes": ["rgb_array", "human"], "render_fps": 30}`. For Jax or Torch, this can be indicated to users with `"jax"=True` or `"torch"=True`. - :attr:`np_random` - The random number generator for the environment. This is automatically assigned during ``super().reset(seed=seed)`` and when assessing :attr:`np_random`. diff --git a/gymnasium/envs/__init__.py b/gymnasium/envs/__init__.py index ce95a5064..1bc447d89 100644 --- a/gymnasium/envs/__init__.py +++ b/gymnasium/envs/__init__.py @@ -449,7 +449,7 @@ # --- For shimmy compatibility def _raise_shimmy_error(*args: Any, **kwargs: Any): raise ImportError( - "To use the gym compatibility environments, run `pip install shimmy[gym-v21]` or `pip install shimmy[gym-v26]`" + 'To use the gym compatibility environments, run `pip install "shimmy[gym-v21]"` or `pip install "shimmy[gym-v26]"`' ) diff --git a/gymnasium/envs/box2d/bipedal_walker.py b/gymnasium/envs/box2d/bipedal_walker.py index ae4dd7b0b..1047b473d 100644 --- a/gymnasium/envs/box2d/bipedal_walker.py +++ b/gymnasium/envs/box2d/bipedal_walker.py @@ -23,7 +23,7 @@ ) except ImportError as e: raise DependencyNotInstalled( - "Box2D is not installed, run `pip install gymnasium[box2d]`" + 'Box2D is not installed, you can install it by run `pip install swig` followed by `pip install "gymnasium[box2d]"`' ) from e @@ -628,7 +628,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[box2d]`" + 'pygame is not installed, run `pip install "gymnasium[box2d]"`' ) from e if self.screen is None and self.render_mode == "human": diff --git a/gymnasium/envs/box2d/car_dynamics.py b/gymnasium/envs/box2d/car_dynamics.py index aab3f7c58..4f04a251b 100644 --- a/gymnasium/envs/box2d/car_dynamics.py +++ b/gymnasium/envs/box2d/car_dynamics.py @@ -19,7 +19,7 @@ from Box2D.b2 import fixtureDef, polygonShape, revoluteJointDef except ImportError as e: raise DependencyNotInstalled( - "Box2D is not installed, run `pip install gymnasium[box2d]`" + 'Box2D is not installed, you can install it by run `pip install swig` followed by `pip install "gymnasium[box2d]"`' ) from e diff --git a/gymnasium/envs/box2d/car_racing.py b/gymnasium/envs/box2d/car_racing.py index 6563b872f..19368c907 100644 --- a/gymnasium/envs/box2d/car_racing.py +++ b/gymnasium/envs/box2d/car_racing.py @@ -17,7 +17,7 @@ from Box2D.b2 import contactListener, fixtureDef, polygonShape except ImportError as e: raise DependencyNotInstalled( - "Box2D is not installed, run `pip install gymnasium[box2d]`" + 'Box2D is not installed, you can install it by run `pip install swig` followed by `pip install "gymnasium[box2d]"`' ) from e try: @@ -27,7 +27,7 @@ from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[box2d]`" + 'pygame is not installed, run `pip install "gymnasium[box2d]"`' ) from e diff --git a/gymnasium/envs/box2d/lunar_lander.py b/gymnasium/envs/box2d/lunar_lander.py index 6f46627b5..4242105a9 100644 --- a/gymnasium/envs/box2d/lunar_lander.py +++ b/gymnasium/envs/box2d/lunar_lander.py @@ -24,7 +24,7 @@ ) except ImportError as e: raise DependencyNotInstalled( - "Box2D is not installed, run `pip install gymnasium[box2d]`" + 'Box2D is not installed, you can install it by run `pip install swig` followed by `pip install "gymnasium[box2d]"`' ) from e @@ -683,7 +683,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[box2d]`" + 'pygame is not installed, run `pip install "gymnasium[box2d]"`' ) from e if self.screen is None and self.render_mode == "human": diff --git a/gymnasium/envs/classic_control/acrobot.py b/gymnasium/envs/classic_control/acrobot.py index 1700b93ab..f9c2842aa 100644 --- a/gymnasium/envs/classic_control/acrobot.py +++ b/gymnasium/envs/classic_control/acrobot.py @@ -298,7 +298,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic-control]"`' ) from e if self.screen is None: diff --git a/gymnasium/envs/classic_control/cartpole.py b/gymnasium/envs/classic_control/cartpole.py index 1b7ff9204..ee29ee72a 100644 --- a/gymnasium/envs/classic_control/cartpole.py +++ b/gymnasium/envs/classic_control/cartpole.py @@ -265,7 +265,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic-control]"`' ) from e if self.screen is None: @@ -528,7 +528,7 @@ def render(self): from pygame import gfxdraw except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic_control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) if self.screens is None: diff --git a/gymnasium/envs/classic_control/continuous_mountain_car.py b/gymnasium/envs/classic_control/continuous_mountain_car.py index f27577fed..e8ecd9841 100644 --- a/gymnasium/envs/classic_control/continuous_mountain_car.py +++ b/gymnasium/envs/classic_control/continuous_mountain_car.py @@ -212,7 +212,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e if self.screen is None: diff --git a/gymnasium/envs/classic_control/mountain_car.py b/gymnasium/envs/classic_control/mountain_car.py index a6157377c..54b9b1950 100644 --- a/gymnasium/envs/classic_control/mountain_car.py +++ b/gymnasium/envs/classic_control/mountain_car.py @@ -187,7 +187,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e if self.screen is None: diff --git a/gymnasium/envs/classic_control/pendulum.py b/gymnasium/envs/classic_control/pendulum.py index 64866ecaa..8ec162539 100644 --- a/gymnasium/envs/classic_control/pendulum.py +++ b/gymnasium/envs/classic_control/pendulum.py @@ -187,7 +187,7 @@ def render(self): from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e if self.screen is None: diff --git a/gymnasium/envs/mujoco/mujoco_env.py b/gymnasium/envs/mujoco/mujoco_env.py index f16ef43e4..5f2fa6396 100644 --- a/gymnasium/envs/mujoco/mujoco_env.py +++ b/gymnasium/envs/mujoco/mujoco_env.py @@ -13,8 +13,7 @@ import mujoco except ImportError as e: raise error.DependencyNotInstalled( - "Could not import mujoco" - "(HINT: you need to install mujoco, run `pip install gymnasium[mujoco]`.)" + 'MuJoCo is not installed, run `pip install "gymnasium[mujoco]"`' ) from e diff --git a/gymnasium/envs/mujoco/pusher_v4.py b/gymnasium/envs/mujoco/pusher_v4.py index 72d571f29..cb4c483e3 100644 --- a/gymnasium/envs/mujoco/pusher_v4.py +++ b/gymnasium/envs/mujoco/pusher_v4.py @@ -1,3 +1,4 @@ +import mujoco import numpy as np from gymnasium import utils @@ -22,7 +23,12 @@ class PusherEnv(MujocoEnv, utils.EzPickle): } def __init__(self, **kwargs): + if mujoco.__version__ >= "3.0.0": + raise ImportError( + "`Pusher-v4` is only supported on `mujuco<3`, for more information https://github.com/Farama-Foundation/Gymnasium/issues/950" + ) utils.EzPickle.__init__(self, **kwargs) + observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) MujocoEnv.__init__( self, diff --git a/gymnasium/envs/phys2d/cartpole.py b/gymnasium/envs/phys2d/cartpole.py index 010a702b3..4acbc1a45 100644 --- a/gymnasium/envs/phys2d/cartpole.py +++ b/gymnasium/envs/phys2d/cartpole.py @@ -145,7 +145,7 @@ def render_image( from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e screen, clock = render_state @@ -217,7 +217,7 @@ def render_init( import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e pygame.init() @@ -232,7 +232,7 @@ def render_close(self, render_state: RenderStateType) -> None: import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e pygame.display.quit() pygame.quit() diff --git a/gymnasium/envs/phys2d/pendulum.py b/gymnasium/envs/phys2d/pendulum.py index 10862c951..1bac2d446 100644 --- a/gymnasium/envs/phys2d/pendulum.py +++ b/gymnasium/envs/phys2d/pendulum.py @@ -117,7 +117,7 @@ def render_image( from pygame import gfxdraw except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e screen, clock, last_u = render_state @@ -191,7 +191,7 @@ def render_init( import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e pygame.init() @@ -210,7 +210,7 @@ def render_close( import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e pygame.display.quit() pygame.quit() diff --git a/gymnasium/envs/tabular/blackjack.py b/gymnasium/envs/tabular/blackjack.py index 35cb4bb93..a41f9ecb4 100644 --- a/gymnasium/envs/tabular/blackjack.py +++ b/gymnasium/envs/tabular/blackjack.py @@ -353,7 +353,7 @@ def render_init( import pygame except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic_control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) rng = seeding.np_random(0)[0] @@ -377,7 +377,7 @@ def render_image( import pygame except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy_text]`" + 'pygame is not installed, run `pip install "gymnasium[toy_text]"`' ) screen, dealer_top_card_value_str, dealer_top_card_suit = render_state @@ -483,7 +483,7 @@ def render_close(self, render_state: RenderStateType) -> None: import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic_control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e pygame.display.quit() pygame.quit() diff --git a/gymnasium/envs/tabular/cliffwalking.py b/gymnasium/envs/tabular/cliffwalking.py index 5b8ad7c78..91359ec43 100644 --- a/gymnasium/envs/tabular/cliffwalking.py +++ b/gymnasium/envs/tabular/cliffwalking.py @@ -218,7 +218,7 @@ def render_init( import pygame except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic_control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) cell_size = (60, 60) @@ -302,7 +302,7 @@ def render_image( import pygame except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy_text]`" + 'pygame is not installed, run `pip install "gymnasium[toy_text]"`' ) ( window_surface, @@ -349,7 +349,7 @@ def render_close(self, render_state: RenderStateType) -> None: import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy-text]`" + 'pygame is not installed, run `pip install "gymnasium[toy-text]"`' ) from e pygame.display.quit() pygame.quit() diff --git a/gymnasium/envs/toy_text/blackjack.py b/gymnasium/envs/toy_text/blackjack.py index 7fed3533e..39816d94f 100644 --- a/gymnasium/envs/toy_text/blackjack.py +++ b/gymnasium/envs/toy_text/blackjack.py @@ -238,7 +238,7 @@ def render(self): import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy-text]`" + 'pygame is not installed, run `pip install "gymnasium[toy-text]"`' ) from e player_sum, dealer_card_value, usable_ace = self._get_obs() diff --git a/gymnasium/envs/toy_text/cliffwalking.py b/gymnasium/envs/toy_text/cliffwalking.py index 76655baba..813378f90 100644 --- a/gymnasium/envs/toy_text/cliffwalking.py +++ b/gymnasium/envs/toy_text/cliffwalking.py @@ -211,7 +211,7 @@ def _render_gui(self, mode): import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy-text]`" + 'pygame is not installed, run `pip install "gymnasium[toy-text]"`' ) from e if self.window_surface is None: pygame.init() diff --git a/gymnasium/envs/toy_text/frozen_lake.py b/gymnasium/envs/toy_text/frozen_lake.py index 5dd129a70..5f852865d 100644 --- a/gymnasium/envs/toy_text/frozen_lake.py +++ b/gymnasium/envs/toy_text/frozen_lake.py @@ -343,7 +343,7 @@ def _render_gui(self, mode): import pygame except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy-text]`" + 'pygame is not installed, run `pip install "gymnasium[toy-text]"`' ) from e if self.window_surface is None: diff --git a/gymnasium/envs/toy_text/taxi.py b/gymnasium/envs/toy_text/taxi.py index f354b2b8b..45678e7c7 100644 --- a/gymnasium/envs/toy_text/taxi.py +++ b/gymnasium/envs/toy_text/taxi.py @@ -328,7 +328,7 @@ def _render_gui(self, mode): import pygame # dependency to pygame only if rendering with human except ImportError as e: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[toy-text]`" + 'pygame is not installed, run `pip install "gymnasium[toy-text]"`' ) from e if self.window is None: diff --git a/gymnasium/utils/play.py b/gymnasium/utils/play.py index a4744ca29..53b882ffb 100644 --- a/gymnasium/utils/play.py +++ b/gymnasium/utils/play.py @@ -18,7 +18,7 @@ from pygame.event import Event except ImportError as e: raise gym.error.DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[classic-control]`" + 'pygame is not installed, run `pip install "gymnasium[classic_control]"`' ) from e try: @@ -27,7 +27,7 @@ matplotlib.use("TkAgg") import matplotlib.pyplot as plt except ImportError: - logger.warn("matplotlib is not installed, run `pip install gymnasium[other]`") + logger.warn('matplotlib is not installed, run `pip install "gymnasium[other]"`') matplotlib, plt = None, None @@ -358,7 +358,7 @@ def __init__( if plt is None: raise DependencyNotInstalled( - "matplotlib is not installed, run `pip install gymnasium[other]`" + 'matplotlib is not installed, run `pip install "gymnasium[other]"`' ) num_plots = len(self.plot_names) @@ -411,6 +411,6 @@ def callback( if plt is None: raise DependencyNotInstalled( - "matplotlib is not installed, run `pip install gymnasium[other]`" + 'matplotlib is not installed, run `pip install "gymnasium[other]"`' ) plt.pause(0.000001) diff --git a/gymnasium/utils/save_video.py b/gymnasium/utils/save_video.py index 341b31b35..c9366217b 100644 --- a/gymnasium/utils/save_video.py +++ b/gymnasium/utils/save_video.py @@ -12,7 +12,7 @@ from moviepy.video.io.ImageSequenceClip import ImageSequenceClip except ImportError as e: raise gym.error.DependencyNotInstalled( - "moviepy is not installed, run `pip install moviepy`" + 'moviepy is not installed, run `pip install "gymnasium[other]"`' ) from e diff --git a/gymnasium/vector/vector_env.py b/gymnasium/vector/vector_env.py index 7127dd9aa..271603a9a 100644 --- a/gymnasium/vector/vector_env.py +++ b/gymnasium/vector/vector_env.py @@ -92,6 +92,9 @@ class VectorEnv(Generic[ObsType, ActType, ArrayType]): :func:`make_vec` is the equivalent function to :func:`make` for vector environments. """ + # Set this in SOME subclasses + metadata: dict[str, Any] = {"render_modes": []} + spec: EnvSpec | None = None render_mode: str | None = None closed: bool = False @@ -446,6 +449,11 @@ def render_mode(self) -> tuple[RenderFrame, ...] | None: """Returns the `render_mode` from the base environment.""" return self.env.render_mode + @property + def metadata(self) -> dict[str, Any]: + """Returns the `metadata` from the base environment.""" + return self.env.metadata + @property def np_random(self) -> np.random.Generator: """Returns the environment's internal :attr:`_np_random` that if not set will initialise with a random seed. diff --git a/gymnasium/wrappers/atari_preprocessing.py b/gymnasium/wrappers/atari_preprocessing.py index ec9e43caa..4e4d89a00 100644 --- a/gymnasium/wrappers/atari_preprocessing.py +++ b/gymnasium/wrappers/atari_preprocessing.py @@ -90,7 +90,7 @@ def __init__( import cv2 # noqa: F401 except ImportError: raise gym.error.DependencyNotInstalled( - "opencv-python package not installed, run `pip install gymnasium[other]` to get dependencies for atari" + 'opencv-python package not installed, run `pip install "gymnasium[other]"` to get dependencies for atari' ) assert frame_skip > 0 diff --git a/gymnasium/wrappers/jax_to_numpy.py b/gymnasium/wrappers/jax_to_numpy.py index dfa682594..33c64e2f7 100644 --- a/gymnasium/wrappers/jax_to_numpy.py +++ b/gymnasium/wrappers/jax_to_numpy.py @@ -18,7 +18,7 @@ import jax.numpy as jnp except ImportError: raise DependencyNotInstalled( - "Jax is not installed therefore cannot call `numpy_to_jax`, run `pip install gymnasium[jax]`" + 'Jax is not installed therefore cannot call `numpy_to_jax`, run `pip install "gymnasium[jax]"`' ) __all__ = ["JaxToNumpy", "jax_to_numpy", "numpy_to_jax"] @@ -145,7 +145,7 @@ def __init__(self, env: gym.Env[ObsType, ActType]): """ if jnp is None: raise DependencyNotInstalled( - "jax is not installed, run `pip install gymnasium[jax]`" + 'Jax is not installed, run `pip install "gymnasium[jax]"`' ) gym.utils.RecordConstructorArgs.__init__(self) gym.Wrapper.__init__(self, env) diff --git a/gymnasium/wrappers/jax_to_torch.py b/gymnasium/wrappers/jax_to_torch.py index fbc5de33b..563160180 100644 --- a/gymnasium/wrappers/jax_to_torch.py +++ b/gymnasium/wrappers/jax_to_torch.py @@ -26,7 +26,7 @@ from jax import dlpack as jax_dlpack except ImportError: raise DependencyNotInstalled( - "Jax is not installed therefore cannot call `torch_to_jax`, run `pip install gymnasium[jax]`" + 'Jax is not installed therefore cannot call `torch_to_jax`, run `pip install "gymnasium[jax]"`' ) try: @@ -36,7 +36,7 @@ Device = Union[str, torch.device] except ImportError: raise DependencyNotInstalled( - "Torch is not installed therefore cannot call `torch_to_jax`, run `pip install torch`" + 'Torch is not installed therefore cannot call `torch_to_jax`, run `pip install "gymnasium[torch]"`' ) diff --git a/gymnasium/wrappers/numpy_to_torch.py b/gymnasium/wrappers/numpy_to_torch.py index 5d8139de0..794f1d9f2 100644 --- a/gymnasium/wrappers/numpy_to_torch.py +++ b/gymnasium/wrappers/numpy_to_torch.py @@ -19,7 +19,7 @@ Device = Union[str, torch.device] except ImportError: raise DependencyNotInstalled( - "Torch is not installed therefore cannot call `torch_to_numpy`, run `pip install torch`" + 'Torch is not installed therefore cannot call `torch_to_numpy`, run `pip install "gymnasium[torch]"`' ) diff --git a/gymnasium/wrappers/rendering.py b/gymnasium/wrappers/rendering.py index 591ce7861..8ad49e373 100644 --- a/gymnasium/wrappers/rendering.py +++ b/gymnasium/wrappers/rendering.py @@ -302,7 +302,7 @@ def __init__( import moviepy # noqa: F401 except ImportError as e: raise error.DependencyNotInstalled( - "MoviePy is not installed, run `pip install moviepy`" + 'MoviePy is not installed, run `pip install "gymnasium[other]"`' ) from e def _capture_frame(self): @@ -397,7 +397,7 @@ def stop_recording(self): from moviepy.video.io.ImageSequenceClip import ImageSequenceClip except ImportError as e: raise error.DependencyNotInstalled( - "MoviePy is not installed, run `pip install moviepy`" + 'MoviePy is not installed, run `pip install "gymnasium[other]"`' ) from e clip = ImageSequenceClip(self.recorded_frames, fps=self.frames_per_sec) @@ -510,7 +510,7 @@ def _render_frame(self): import pygame except ImportError: raise DependencyNotInstalled( - "pygame is not installed, run `pip install gymnasium[box2d]`" + 'pygame is not installed, run `pip install "gymnasium[box2d]"`' ) if self.env.render_mode == "rgb_array_list": last_rgb_array = self.env.render() diff --git a/gymnasium/wrappers/transform_observation.py b/gymnasium/wrappers/transform_observation.py index ec1f50256..b288c426c 100644 --- a/gymnasium/wrappers/transform_observation.py +++ b/gymnasium/wrappers/transform_observation.py @@ -377,7 +377,7 @@ def __init__(self, env: gym.Env[ObsType, ActType], shape: tuple[int, int]): import cv2 except ImportError as e: raise DependencyNotInstalled( - "opencv (cv2) is not installed, run `pip install gymnasium[other]`" + 'opencv (cv2) is not installed, run `pip install "gymnasium[other]"`' ) from e self.shape: Final[tuple[int, int]] = tuple(shape) diff --git a/gymnasium/wrappers/vector/jax_to_numpy.py b/gymnasium/wrappers/vector/jax_to_numpy.py index 6db86fd0f..62fc4e591 100644 --- a/gymnasium/wrappers/vector/jax_to_numpy.py +++ b/gymnasium/wrappers/vector/jax_to_numpy.py @@ -37,7 +37,7 @@ def __init__(self, env: VectorEnv): """ if jnp is None: raise DependencyNotInstalled( - "jax is not installed, run `pip install gymnasium[jax]`" + 'Jax is not installed, run `pip install "gymnasium[jax]"`' ) super().__init__(env) diff --git a/pyproject.toml b/pyproject.toml index d5886fed5..919994b57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,8 +34,7 @@ dynamic = ["version"] [project.optional-dependencies] # Update dependencies in `all` if any are added or removed -atari = ["shimmy[atari] >=0.1.0,<1.0"] -accept-rom-license = ["autorom[accept-rom-license] ~=0.4.2"] +atari = ["ale_py >= 0.8"] box2d = ["box2d-py ==2.3.5", "pygame >=2.1.3", "swig ==4.*"] classic-control = ["pygame >=2.1.3"] classic_control = ["pygame >=2.1.3"] # kept for backward compatibility @@ -45,13 +44,8 @@ mujoco = ["mujoco >=2.1.5", "imageio >=2.14.1"] toy-text = ["pygame >=2.1.3"] toy_text = ["pygame >=2.1.3"] # kept for backward compatibility jax = ["jax >=0.4.0", "jaxlib >=0.4.0", "flax >= 0.5.0"] -other = [ - "lz4 >=3.1.0", - "opencv-python >=3.0", - "matplotlib >=3.0", - "moviepy >=1.0.0", - "torch >=1.0.0", -] +torch = ["torch >=1.0.0"] +other = ["moviepy >=1.0.0", "matplotlib >=3.0", "opencv-python >=3.0"] all = [ # All dependencies above except accept-rom-license # NOTE: No need to manually remove the duplicates, setuptools automatically does that. @@ -65,7 +59,7 @@ all = [ "pygame >=2.1.3", # mujoco-py "mujoco-py >=2.1,<2.2", - "cython<3", + "cython <3", # mujoco "mujoco >=2.1.5", "imageio >=2.14.1", @@ -75,17 +69,17 @@ all = [ "jax >=0.4.0", "jaxlib >=0.4.0", "flax >= 0.5.0", + # torch + "torch >=1.0.0", # other - "lz4 >=3.1.0", "opencv-python >=3.0", "matplotlib >=3.0", "moviepy >=1.0.0", - "torch >=1.0.0", ] testing = [ "pytest ==7.1.3", - "scipy >= 1.7.3", - "dill>=0.3.7", + "scipy >=1.7.3", + "dill >=0.3.7", ] [project.urls] diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py index 02149b360..c7a5fb0c0 100644 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -331,15 +331,17 @@ def test_reward_sum(version: str): env_conf("HumanoidStandup", True, False, False, "superset"), env_conf("InvertedDoublePendulum", True, True, False, "superset"), env_conf("InvertedPendulum", False, True, False, "superset"), - env_conf("Pusher", False, True, False, "keys-superset"), + env_conf("Pusher", True, True, False, "keys-superset"), # pusher-v4 env_conf("Reacher", True, True, False, "keys-equivalence"), env_conf("Swimmer", False, False, False, "skip"), env_conf("Walker2d", True, True, True, "keys-superset"), ], ) -def test_identical_behaviour_v45(env_conf): +def test_identical_behaviour_v45(env_conf, NUM_STEPS: int = 100): """Verify that v4 -> v5 transition. Does not change the behaviour of the environments in any unexpected way.""" - NUM_STEPS = 100 + if env_conf.env_name == "Pusher" and mujoco.__version__ >= "3.0.0": + pytest.skip("Pusher-v4 is not compatible with mujoco >= 3") + env_v4 = gym.make(f"{env_conf.env_name}-v4") env_v5 = gym.make(f"{env_conf.env_name}-v5") @@ -584,17 +586,22 @@ def test_model_object_count(version: str): assert env.model.ngeom == 3 assert env.model.ntendon == 0 - env = gym.make(f"Pusher-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) - assert env.model.nq == 11 - assert env.model.nv == 11 - assert env.model.nu == 7 - assert env.model.nbody == 13 - if mujoco.__version__ >= "3.1.2": - assert env.model.nbvh == 8 - assert env.model.njnt == 11 - assert env.model.ngeom == 21 - assert env.model.ntendon == 0 + if not (version == "v4" and mujoco.__version__ >= "3.0.0"): + env = gym.make(f"Pusher-{version}").unwrapped + assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert env.model.nq == 11 + assert env.model.nv == 11 + assert env.model.nu == 7 + assert env.model.nbody == 13 + if mujoco.__version__ >= "3.1.4": + assert env.model.nbvh == 7 + elif mujoco.__version__ >= "3.1.2": + assert env.model.nbvh == 8 + else: + assert env.model.nbvh == 18 + assert env.model.njnt == 11 + assert env.model.ngeom == 21 + assert env.model.ntendon == 0 env = gym.make(f"Reacher-{version}").unwrapped assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) @@ -706,5 +713,8 @@ def test_reset_noise_scale(env_id): @pytest.mark.parametrize("version", ["v5", "v4"]) def test_reset_state(env_name: str, version: str): """Asserts that `reset()` properly resets the internal state.""" + if env_name == "Pusher" and version == "v4" and mujoco.__version__ >= "3.0.0": + pytest.skip("Skipping Pusher-v4 as not compatible with mujoco >= 3.0") + env = gym.make(f"{env_name}-{version}") check_mujoco_reset_state(env) diff --git a/tests/vector/test_vector_wrapper.py b/tests/vector/test_vector_wrapper.py index 2701ecbb7..16dd82932 100644 --- a/tests/vector/test_vector_wrapper.py +++ b/tests/vector/test_vector_wrapper.py @@ -54,3 +54,17 @@ def test_vector_env_wrapper_attributes(): assert np.allclose(wrapped.env.get_attr("gravity"), env.get_attr("gravity")) env.close() + + +def test_vector_env_metadata(): + """Test if `metadata` property for VectorWrapper correctly forwards to the vector env it is wrapping.""" + env = gym.make_vec("CartPole-v1", num_envs=3, vectorization_mode="sync") + wrapped = DummyVectorWrapper( + gym.make_vec("CartPole-v1", num_envs=3, vectorization_mode="sync") + ) + + assert env.metadata == wrapped.metadata + env.metadata = {"render_modes": ["rgb_array"]} + assert env.metadata != wrapped.metadata + + env.close()