diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 7ded82063..a8a2963ff 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -5,38 +5,54 @@ permissions: contents: read # to fetch code (actions/checkout) jobs: - build-all: +# build-all: +# runs-on: ubuntu-latest +# strategy: +# fail-fast: true +# matrix: +# python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] +# numpy-version: ['>=1.21,<2.0', '>=2.0'] +# exclude: +# - python-version: '3.8' # numpy>=2.0 requires Python>=3.9 +# numpy-version: '>=2.0' +# steps: +# - uses: actions/checkout@v4 +# - run: | +# docker build -f bin/all-py.Dockerfile \ +# --build-arg PYTHON_VERSION="${{ matrix.python-version }}" \ +# --build-arg NUMPY_VERSION="${{ matrix.numpy-version }}" \ +# --tag gymnasium-all-docker . +# - name: Run tests +# run: docker run gymnasium-all-docker pytest tests/* +# - name: Run doctests +# if: ${{ matrix.python-version != '3.8' }} +# run: docker run gymnasium-all-docker pytest --doctest-modules gymnasium/ +# +# build-necessary: +# runs-on: +# ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - run: | +# docker build -f bin/necessary-py.Dockerfile \ +# --build-arg PYTHON_VERSION='3.10' \ +# --tag gymnasium-necessary-docker . +# - name: Run tests +# run: | +# docker run gymnasium-necessary-docker pytest tests/test_core.py tests/envs/test_envs.py tests/spaces +# + build-type-testing: runs-on: ubuntu-latest strategy: fail-fast: true matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] numpy-version: ['>=1.21,<2.0', '>=2.0'] - exclude: - - python-version: '3.8' # numpy>=2.0 requires Python>=3.9 - numpy-version: '>=2.0' steps: - uses: actions/checkout@v4 - run: | docker build -f bin/all-py.Dockerfile \ - --build-arg PYTHON_VERSION="${{ matrix.python-version }}" \ + --build-arg PYTHON_VERSION="3.9" \ --build-arg NUMPY_VERSION="${{ matrix.numpy-version }}" \ --tag gymnasium-all-docker . - name: Run tests - run: docker run gymnasium-all-docker pytest tests/* - - name: Run doctests - if: ${{ matrix.python-version != '3.8' }} - run: docker run gymnasium-all-docker pytest --doctest-modules gymnasium/ - - build-necessary: - runs-on: - ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: | - docker build -f bin/necessary-py.Dockerfile \ - --build-arg PYTHON_VERSION='3.10' \ - --tag gymnasium-necessary-docker . - - name: Run tests - run: | - docker run gymnasium-necessary-docker pytest tests/test_core.py tests/envs/test_envs.py tests/spaces + run: docker run gymnasium-all-docker pytest --beartype-packages='gymnasium' tests/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f42cc582..1b9a3f648 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-symlinks - id: destroyed-symlinks @@ -35,16 +35,16 @@ repos: - --show-source - --statistics - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.18.0 hooks: - id: pyupgrade - args: ["--py38-plus"] + args: ["--py39-plus"] - repo: https://github.com/PyCQA/isort rev: 5.13.2 hooks: - id: isort - repo: https://github.com/python/black - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black - repo: https://github.com/pycqa/pydocstyle diff --git a/gymnasium/core.py b/gymnasium/core.py index 8225e4905..d386cd3be 100644 --- a/gymnasium/core.py +++ b/gymnasium/core.py @@ -313,7 +313,7 @@ def __init__(self, env: Env[ObsType, ActType]): self._observation_space: spaces.Space[WrapperObsType] | None = None self._metadata: dict[str, Any] | None = None - self._cached_spec: EnvSpec | None = None + self._cached_spec: gymnasium.envs.registration.EnvSpec | None = None def step( self, action: WrapperActType diff --git a/gymnasium/envs/box2d/bipedal_walker.py b/gymnasium/envs/box2d/bipedal_walker.py index 94dda8bfc..6ab9b7641 100644 --- a/gymnasium/envs/box2d/bipedal_walker.py +++ b/gymnasium/envs/box2d/bipedal_walker.py @@ -1,7 +1,7 @@ __credits__ = ["Andrea PIERRÉ"] import math -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional import numpy as np @@ -179,7 +179,7 @@ def __init__(self, render_mode: Optional[str] = None, hardcore: bool = False): self.isopen = True self.world = Box2D.b2World() - self.terrain: List[Box2D.b2Body] = [] + self.terrain: list[Box2D.b2Body] = [] self.hull: Optional[Box2D.b2Body] = None self.prev_shaping = None @@ -458,8 +458,8 @@ def reset( (self.np_random.uniform(-INITIAL_RANDOM, INITIAL_RANDOM), 0), True ) - self.legs: List[Box2D.b2Body] = [] - self.joints: List[Box2D.b2RevoluteJoint] = [] + self.legs: list[Box2D.b2Body] = [] + self.joints: list[Box2D.b2RevoluteJoint] = [] for i in [-1, +1]: leg = self.world.CreateDynamicBody( position=(init_x, init_y - LEG_H / 2 - LEG_DOWN), diff --git a/gymnasium/envs/classic_control/cartpole.py b/gymnasium/envs/classic_control/cartpole.py index c3e3e7781..2baed3390 100644 --- a/gymnasium/envs/classic_control/cartpole.py +++ b/gymnasium/envs/classic_control/cartpole.py @@ -5,7 +5,7 @@ """ import math -from typing import Optional, Tuple, Union +from typing import Optional, Union import numpy as np @@ -418,7 +418,7 @@ def __init__( def step( self, action: np.ndarray - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, dict]: + ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, dict]: assert self.action_space.contains( action ), f"{action!r} ({type(action)}) invalid" diff --git a/gymnasium/envs/classic_control/utils.py b/gymnasium/envs/classic_control/utils.py index 930588e1d..7cd09f466 100644 --- a/gymnasium/envs/classic_control/utils.py +++ b/gymnasium/envs/classic_control/utils.py @@ -2,7 +2,7 @@ Utility functions used for classic control environments. """ -from typing import Optional, SupportsFloat, Tuple +from typing import Optional, SupportsFloat def verify_number_and_cast(x: SupportsFloat) -> float: @@ -16,7 +16,7 @@ def verify_number_and_cast(x: SupportsFloat) -> float: def maybe_parse_reset_bounds( options: Optional[dict], default_low: float, default_high: float -) -> Tuple[float, float]: +) -> tuple[float, float]: """ This function can be called during a reset() to customize the sampling ranges for setting the initial state distributions. diff --git a/gymnasium/envs/mujoco/ant_v5.py b/gymnasium/envs/mujoco/ant_v5.py index 3a9ffaacd..69a42b047 100644 --- a/gymnasium/envs/mujoco/ant_v5.py +++ b/gymnasium/envs/mujoco/ant_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple, Union +from typing import Union import numpy as np @@ -231,15 +231,15 @@ def __init__( self, xml_file: str = "ant.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, forward_reward_weight: float = 1, ctrl_cost_weight: float = 0.5, contact_cost_weight: float = 5e-4, healthy_reward: float = 1.0, main_body: Union[int, str] = 1, terminate_when_unhealthy: bool = True, - healthy_z_range: Tuple[float, float] = (0.2, 1.0), - contact_force_range: Tuple[float, float] = (-1.0, 1.0), + healthy_z_range: tuple[float, float] = (0.2, 1.0), + contact_force_range: tuple[float, float] = (-1.0, 1.0), reset_noise_scale: float = 0.1, exclude_current_positions_from_observation: bool = True, include_cfrc_ext_in_observation: bool = True, diff --git a/gymnasium/envs/mujoco/half_cheetah_v5.py b/gymnasium/envs/mujoco/half_cheetah_v5.py index 2de031c19..ffc74caad 100644 --- a/gymnasium/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium/envs/mujoco/half_cheetah_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -156,7 +156,7 @@ def __init__( self, xml_file: str = "half_cheetah.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, forward_reward_weight: float = 1.0, ctrl_cost_weight: float = 0.1, reset_noise_scale: float = 0.1, diff --git a/gymnasium/envs/mujoco/hopper_v5.py b/gymnasium/envs/mujoco/hopper_v5.py index 54a4adf23..f3438cab1 100644 --- a/gymnasium/envs/mujoco/hopper_v5.py +++ b/gymnasium/envs/mujoco/hopper_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple, Union +from typing import Union import numpy as np @@ -166,14 +166,14 @@ def __init__( self, xml_file: str = "hopper.xml", frame_skip: int = 4, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, forward_reward_weight: float = 1.0, ctrl_cost_weight: float = 1e-3, healthy_reward: float = 1.0, terminate_when_unhealthy: bool = True, - healthy_state_range: Tuple[float, float] = (-100.0, 100.0), - healthy_z_range: Tuple[float, float] = (0.7, float("inf")), - healthy_angle_range: Tuple[float, float] = (-0.2, 0.2), + healthy_state_range: tuple[float, float] = (-100.0, 100.0), + healthy_z_range: tuple[float, float] = (0.7, float("inf")), + healthy_angle_range: tuple[float, float] = (-0.2, 0.2), reset_noise_scale: float = 5e-3, exclude_current_positions_from_observation: bool = True, **kwargs, diff --git a/gymnasium/envs/mujoco/humanoid_v5.py b/gymnasium/envs/mujoco/humanoid_v5.py index b0b96cc4d..e77c1166b 100644 --- a/gymnasium/envs/mujoco/humanoid_v5.py +++ b/gymnasium/envs/mujoco/humanoid_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple, Union +from typing import Union import numpy as np @@ -309,14 +309,14 @@ def __init__( self, xml_file: str = "humanoid.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, forward_reward_weight: float = 1.25, ctrl_cost_weight: float = 0.1, contact_cost_weight: float = 5e-7, - contact_cost_range: Tuple[float, float] = (-np.inf, 10.0), + contact_cost_range: tuple[float, float] = (-np.inf, 10.0), healthy_reward: float = 5.0, terminate_when_unhealthy: bool = True, - healthy_z_range: Tuple[float, float] = (1.0, 2.0), + healthy_z_range: tuple[float, float] = (1.0, 2.0), reset_noise_scale: float = 1e-2, exclude_current_positions_from_observation: bool = True, include_cinert_in_observation: bool = True, diff --git a/gymnasium/envs/mujoco/humanoidstandup_v5.py b/gymnasium/envs/mujoco/humanoidstandup_v5.py index b35426272..3c3f31455 100644 --- a/gymnasium/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium/envs/mujoco/humanoidstandup_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple, Union +from typing import Union import numpy as np @@ -289,11 +289,11 @@ def __init__( self, xml_file: str = "humanoidstandup.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, uph_cost_weight: float = 1, ctrl_cost_weight: float = 0.1, impact_cost_weight: float = 0.5e-6, - impact_cost_range: Tuple[float, float] = (-np.inf, 10.0), + impact_cost_range: tuple[float, float] = (-np.inf, 10.0), reset_noise_scale: float = 1e-2, exclude_current_positions_from_observation: bool = True, include_cinert_in_observation: bool = True, diff --git a/gymnasium/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium/envs/mujoco/inverted_double_pendulum_v5.py index 07b44dae2..3f7ef525f 100644 --- a/gymnasium/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium/envs/mujoco/inverted_double_pendulum_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -148,7 +148,7 @@ def __init__( self, xml_file: str = "inverted_double_pendulum.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = {}, + default_camera_config: dict[str, Union[float, int]] = {}, healthy_reward: float = 10.0, reset_noise_scale: float = 0.1, **kwargs, diff --git a/gymnasium/envs/mujoco/inverted_pendulum_v5.py b/gymnasium/envs/mujoco/inverted_pendulum_v5.py index 8e2433dc4..33ad147f1 100644 --- a/gymnasium/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium/envs/mujoco/inverted_pendulum_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -123,7 +123,7 @@ def __init__( self, xml_file: str = "inverted_pendulum.xml", frame_skip: int = 2, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, reset_noise_scale: float = 0.01, **kwargs, ): diff --git a/gymnasium/envs/mujoco/mujoco_env.py b/gymnasium/envs/mujoco/mujoco_env.py index 2f3e53050..fdec4474f 100644 --- a/gymnasium/envs/mujoco/mujoco_env.py +++ b/gymnasium/envs/mujoco/mujoco_env.py @@ -1,5 +1,5 @@ from os import path -from typing import Dict, Optional, Tuple, Union +from typing import Optional, Union import numpy as np from numpy.typing import NDArray @@ -47,9 +47,9 @@ def __init__( height: int = DEFAULT_SIZE, camera_id: Optional[int] = None, camera_name: Optional[str] = None, - default_camera_config: Optional[Dict[str, Union[float, int]]] = None, + default_camera_config: Optional[dict[str, Union[float, int]]] = None, max_geom: int = 1000, - visual_options: Dict[int, bool] = {}, + visual_options: dict[int, bool] = {}, ): """Base abstract class for mujoco based environments. @@ -121,7 +121,7 @@ def _set_action_space(self): def _initialize_simulation( self, - ) -> Tuple["mujoco.MjModel", "mujoco.MjData"]: + ) -> tuple["mujoco.MjModel", "mujoco.MjData"]: """ Initialize MuJoCo simulation data structures `mjModel` and `mjData`. """ @@ -215,7 +215,7 @@ def state_vector(self) -> NDArray[np.float64]: # ---------------------------- def step( self, action: NDArray[np.float32] - ) -> Tuple[NDArray[np.float64], np.float64, bool, bool, Dict[str, np.float64]]: + ) -> tuple[NDArray[np.float64], np.float64, bool, bool, dict[str, np.float64]]: raise NotImplementedError def reset_model(self) -> NDArray[np.float64]: @@ -225,7 +225,7 @@ def reset_model(self) -> NDArray[np.float64]: """ raise NotImplementedError - def _get_reset_info(self) -> Dict[str, float]: + def _get_reset_info(self) -> dict[str, float]: """Function that generates the `info` that is returned during a `reset()`.""" return {} diff --git a/gymnasium/envs/mujoco/mujoco_py_env.py b/gymnasium/envs/mujoco/mujoco_py_env.py index b026f6d27..a2dd01ddb 100644 --- a/gymnasium/envs/mujoco/mujoco_py_env.py +++ b/gymnasium/envs/mujoco/mujoco_py_env.py @@ -1,5 +1,5 @@ from os import path -from typing import Any, Dict, Optional, Tuple, Union +from typing import Any, Optional, Union import numpy as np from numpy.typing import NDArray @@ -110,7 +110,7 @@ def _set_action_space(self): # ---------------------------- def step( self, action: NDArray[np.float32] - ) -> Tuple[NDArray[np.float64], np.float64, bool, bool, Dict[str, np.float64]]: + ) -> tuple[NDArray[np.float64], np.float64, bool, bool, dict[str, np.float64]]: raise NotImplementedError def reset_model(self) -> NDArray[np.float64]: @@ -120,7 +120,7 @@ def reset_model(self) -> NDArray[np.float64]: """ raise NotImplementedError - def _initialize_simulation(self) -> Tuple[Any, Any]: + def _initialize_simulation(self) -> tuple[Any, Any]: """ Initialize MuJoCo simulation data structures mjModel and mjData. """ @@ -145,7 +145,7 @@ def render(self) -> Union[NDArray[np.float64], None]: raise NotImplementedError # ----------------------------- - def _get_reset_info(self) -> Dict[str, float]: + def _get_reset_info(self) -> dict[str, float]: """Function that generates the `info` that is returned during a `reset()`.""" return {} diff --git a/gymnasium/envs/mujoco/mujoco_rendering.py b/gymnasium/envs/mujoco/mujoco_rendering.py index e1ffe3abb..6c1d8a497 100644 --- a/gymnasium/envs/mujoco/mujoco_rendering.py +++ b/gymnasium/envs/mujoco/mujoco_rendering.py @@ -1,6 +1,6 @@ import os import time -from typing import Dict, Optional +from typing import Optional import glfw import imageio @@ -41,7 +41,7 @@ def __init__( width: int, height: int, max_geom: int = 1000, - visual_options: Dict[int, bool] = {}, + visual_options: dict[int, bool] = {}, ): """Render context superclass for offscreen and window rendering.""" self.model = model @@ -147,7 +147,7 @@ def __init__( width: int, height: int, max_geom: int = 1000, - visual_options: Dict[int, bool] = {}, + visual_options: dict[int, bool] = {}, ): # We must make GLContext before MjrContext self._get_opengl_backend(width, height) @@ -299,7 +299,7 @@ def __init__( width: Optional[int] = None, height: Optional[int] = None, max_geom: int = 1000, - visual_options: Dict[int, bool] = {}, + visual_options: dict[int, bool] = {}, ): glfw.init() @@ -646,7 +646,7 @@ def __init__( max_geom: int = 1000, camera_id: Optional[int] = None, camera_name: Optional[str] = None, - visual_options: Dict[int, bool] = {}, + visual_options: dict[int, bool] = {}, ): """A wrapper for clipping continuous actions within the valid bound. diff --git a/gymnasium/envs/mujoco/pusher_v5.py b/gymnasium/envs/mujoco/pusher_v5.py index 7e55da5cc..2a17ad8a5 100644 --- a/gymnasium/envs/mujoco/pusher_v5.py +++ b/gymnasium/envs/mujoco/pusher_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -171,7 +171,7 @@ def __init__( self, xml_file: str = "pusher_v5.xml", frame_skip: int = 5, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, reward_near_weight: float = 0.5, reward_dist_weight: float = 1, reward_control_weight: float = 0.1, diff --git a/gymnasium/envs/mujoco/reacher_v5.py b/gymnasium/envs/mujoco/reacher_v5.py index 0acf55536..80bd0144e 100644 --- a/gymnasium/envs/mujoco/reacher_v5.py +++ b/gymnasium/envs/mujoco/reacher_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -148,7 +148,7 @@ def __init__( self, xml_file: str = "reacher.xml", frame_skip: int = 2, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, reward_dist_weight: float = 1, reward_control_weight: float = 1, **kwargs, diff --git a/gymnasium/envs/mujoco/swimmer_v5.py b/gymnasium/envs/mujoco/swimmer_v5.py index 05395a35c..cc38f3484 100644 --- a/gymnasium/envs/mujoco/swimmer_v5.py +++ b/gymnasium/envs/mujoco/swimmer_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] -from typing import Dict, Union +from typing import Union import numpy as np @@ -155,7 +155,7 @@ def __init__( self, xml_file: str = "swimmer.xml", frame_skip: int = 4, - default_camera_config: Dict[str, Union[float, int]] = {}, + default_camera_config: dict[str, Union[float, int]] = {}, forward_reward_weight: float = 1.0, ctrl_cost_weight: float = 1e-4, reset_noise_scale: float = 0.1, diff --git a/gymnasium/envs/mujoco/walker2d_v5.py b/gymnasium/envs/mujoco/walker2d_v5.py index c9b22517f..dad0b7637 100644 --- a/gymnasium/envs/mujoco/walker2d_v5.py +++ b/gymnasium/envs/mujoco/walker2d_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple, Union +from typing import Union import numpy as np @@ -175,13 +175,13 @@ def __init__( self, xml_file: str = "walker2d_v5.xml", frame_skip: int = 4, - default_camera_config: Dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, + default_camera_config: dict[str, Union[float, int]] = DEFAULT_CAMERA_CONFIG, forward_reward_weight: float = 1.0, ctrl_cost_weight: float = 1e-3, healthy_reward: float = 1.0, terminate_when_unhealthy: bool = True, - healthy_z_range: Tuple[float, float] = (0.8, 2.0), - healthy_angle_range: Tuple[float, float] = (-1.0, 1.0), + healthy_z_range: tuple[float, float] = (0.8, 2.0), + healthy_angle_range: tuple[float, float] = (-1.0, 1.0), reset_noise_scale: float = 5e-3, exclude_current_positions_from_observation: bool = True, **kwargs, diff --git a/gymnasium/envs/phys2d/cartpole.py b/gymnasium/envs/phys2d/cartpole.py index 99c3b5f44..ece9e62f6 100644 --- a/gymnasium/envs/phys2d/cartpole.py +++ b/gymnasium/envs/phys2d/cartpole.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Tuple +from typing import Any import jax import jax.numpy as jnp @@ -17,7 +17,7 @@ from gymnasium.utils import EzPickle -RenderStateType = Tuple["pygame.Surface", "pygame.time.Clock"] # type: ignore # noqa: F821 +RenderStateType = tuple["pygame.Surface", "pygame.time.Clock"] # type: ignore # noqa: F821 @struct.dataclass diff --git a/gymnasium/envs/phys2d/pendulum.py b/gymnasium/envs/phys2d/pendulum.py index 2e2538263..36eb8586a 100644 --- a/gymnasium/envs/phys2d/pendulum.py +++ b/gymnasium/envs/phys2d/pendulum.py @@ -3,7 +3,7 @@ from __future__ import annotations from os import path -from typing import Any, Optional, Tuple +from typing import Any, Optional import jax import jax.numpy as jnp @@ -18,7 +18,7 @@ from gymnasium.utils import EzPickle -RenderStateType = Tuple["pygame.Surface", "pygame.time.Clock", Optional[float]] # type: ignore # noqa: F821 +RenderStateType = tuple["pygame.Surface", "pygame.time.Clock", Optional[float]] # type: ignore # noqa: F821 @struct.dataclass diff --git a/gymnasium/envs/registration.py b/gymnasium/envs/registration.py index de00ac1c4..398aece11 100644 --- a/gymnasium/envs/registration.py +++ b/gymnasium/envs/registration.py @@ -1,7 +1,6 @@ """Functions for registering environments within gymnasium using public functions ``make``, ``register`` and ``spec``.""" -from __future__ import annotations - +import collections.abc import contextlib import copy import dataclasses @@ -12,10 +11,11 @@ import re import sys from collections import defaultdict +from collections.abc import Callable, Iterable from dataclasses import dataclass, field from enum import Enum from types import ModuleType -from typing import Any, Callable, Iterable, Sequence +from typing import Any, Optional, runtime_checkable import gymnasium as gym from gymnasium import Env, Wrapper, error, logger @@ -50,16 +50,18 @@ ] +@runtime_checkable class EnvCreator(Protocol): """Function type expected for an environment.""" def __call__(self, **kwargs: Any) -> Env: ... +@runtime_checkable class VectorEnvCreator(Protocol): """Function type expected for an environment.""" - def __call__(self, **kwargs: Any) -> gym.vector.VectorEnv: ... + def __call__(self, **kwargs: Any) -> "gym.vector.VectorEnv": ... @dataclass @@ -99,7 +101,7 @@ class EnvSpec: entry_point: EnvCreator | str | None = field(default=None) # Environment attributes - reward_threshold: float | None = field(default=None) + reward_threshold: float | int | None = field(default=None) nondeterministic: bool = field(default=False) # Wrappers @@ -165,7 +167,7 @@ def _check_can_jsonify(env_spec: dict[str, Any]): ) @staticmethod - def from_json(json_env_spec: str) -> EnvSpec: + def from_json(json_env_spec: str) -> "gym.envs.registration.EnvSpec": """Converts a JSON string into a specification stack. Args: @@ -260,7 +262,7 @@ class VectorizeMode(Enum): # Global registry of environments. Meant to be accessed through `register` and `make` registry: dict[str, EnvSpec] = {} -current_namespace: str | None = None +current_namespace: Optional[str] = None def parse_env_id(env_id: str) -> tuple[str | None, str, int | None]: @@ -569,7 +571,7 @@ def namespace(ns: str): def register( id: str, entry_point: EnvCreator | str | None = None, - reward_threshold: float | None = None, + reward_threshold: float | int | None = None, nondeterministic: bool = False, max_episode_steps: int | None = None, order_enforce: bool = True, @@ -835,9 +837,9 @@ def make_vec( num_envs: int = 1, vectorization_mode: VectorizeMode | str | None = None, vector_kwargs: dict[str, Any] | None = None, - wrappers: Sequence[Callable[[Env], Wrapper]] | None = None, + wrappers: collections.abc.Sequence[Callable[[Env], Wrapper]] | None = None, **kwargs, -) -> gym.vector.VectorEnv: +) -> "gym.vector.VectorEnv": """Create a vector environment according to the given ID. To find all available environments use :func:`gymnasium.pprint_registry` or ``gymnasium.registry.keys()`` for all valid ids. diff --git a/gymnasium/envs/tabular/blackjack.py b/gymnasium/envs/tabular/blackjack.py index 714bfea07..ca8929283 100644 --- a/gymnasium/envs/tabular/blackjack.py +++ b/gymnasium/envs/tabular/blackjack.py @@ -2,7 +2,7 @@ import math import os -from typing import NamedTuple, Optional, Tuple, Union +from typing import NamedTuple, Optional, Union import jax import jax.numpy as jnp @@ -19,7 +19,7 @@ from gymnasium.wrappers import HumanRendering -RenderStateType = Tuple["pygame.Surface", str, int] # type: ignore # noqa: F821 +RenderStateType = tuple["pygame.Surface", str, int] # type: ignore # noqa: F821 deck = jnp.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]) @@ -371,7 +371,7 @@ def render_image( state: StateType, render_state: RenderStateType, params: BlackJackParams = BlackJackParams, - ) -> Tuple[RenderStateType, np.ndarray]: + ) -> tuple[RenderStateType, np.ndarray]: """Renders an image from a state.""" try: import pygame diff --git a/gymnasium/envs/toy_text/cliffwalking.py b/gymnasium/envs/toy_text/cliffwalking.py index fddf5bcaa..3a64468f9 100644 --- a/gymnasium/envs/toy_text/cliffwalking.py +++ b/gymnasium/envs/toy_text/cliffwalking.py @@ -1,7 +1,7 @@ from contextlib import closing from io import StringIO from os import path -from typing import Any, List, Optional, Tuple, Union +from typing import Any, Optional, Union import numpy as np @@ -159,8 +159,8 @@ def _limit_coordinates(self, coord: np.ndarray) -> np.ndarray: return coord def _calculate_transition_prob( - self, current: Union[List[int], np.ndarray], move: int - ) -> List[Tuple[float, Any, int, bool]]: + self, current: Union[list[int], np.ndarray], move: int + ) -> list[tuple[float, Any, int, bool]]: """Determine the outcome for an action. Transition Prob is always 1.0. Args: diff --git a/gymnasium/envs/toy_text/frozen_lake.py b/gymnasium/envs/toy_text/frozen_lake.py index 9b9c1bfa4..2e820c63c 100644 --- a/gymnasium/envs/toy_text/frozen_lake.py +++ b/gymnasium/envs/toy_text/frozen_lake.py @@ -1,7 +1,7 @@ from contextlib import closing from io import StringIO from os import path -from typing import List, Optional +from typing import Optional import numpy as np @@ -33,7 +33,7 @@ # DFS to check that it's a valid path. -def is_valid(board: List[List[str]], max_size: int) -> bool: +def is_valid(board: list[list[str]], max_size: int) -> bool: frontier, discovered = [], set() frontier.append((0, 0)) while frontier: @@ -55,7 +55,7 @@ def is_valid(board: List[List[str]], max_size: int) -> bool: def generate_random_map( size: int = 8, p: float = 0.8, seed: Optional[int] = None -) -> List[str]: +) -> list[str]: """Generates a random valid map (one that has a path from start to goal) Args: diff --git a/gymnasium/experimental/functional.py b/gymnasium/experimental/functional.py index bdcbb40d7..51fa23156 100644 --- a/gymnasium/experimental/functional.py +++ b/gymnasium/experimental/functional.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Callable, Generic, TypeVar +from collections.abc import Callable +from typing import Any, Generic, TypeVar import numpy as np diff --git a/gymnasium/logger.py b/gymnasium/logger.py index 5c999aaed..d321a6407 100644 --- a/gymnasium/logger.py +++ b/gymnasium/logger.py @@ -1,7 +1,7 @@ """Set of functions for logging messages.""" import warnings -from typing import Optional, Type +from typing import Optional from gymnasium.utils import colorize @@ -19,7 +19,7 @@ def warn( msg: str, *args: object, - category: Optional[Type[Warning]] = None, + category: Optional[type[Warning]] = None, stacklevel: int = 1, ): """Raises a warning to the user if the min_level <= WARN. diff --git a/gymnasium/spaces/box.py b/gymnasium/spaces/box.py index fb9a99645..ae2839a88 100644 --- a/gymnasium/spaces/box.py +++ b/gymnasium/spaces/box.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Iterable, Mapping, Sequence, SupportsFloat +from collections.abc import Iterable, Mapping, Sequence +from typing import Any, SupportsFloat, no_type_check import numpy as np from numpy.typing import NDArray @@ -53,6 +54,7 @@ class Box(Space[NDArray[Any]]): Box([-1. -2.], [2. 4.], (2,), float32) """ + @no_type_check def __init__( self, low: SupportsFloat | NDArray[Any], diff --git a/gymnasium/spaces/dict.py b/gymnasium/spaces/dict.py index 49ff4c907..2e1f0c40c 100644 --- a/gymnasium/spaces/dict.py +++ b/gymnasium/spaces/dict.py @@ -3,15 +3,15 @@ from __future__ import annotations import collections.abc -import typing -from typing import Any, KeysView, Sequence +from collections.abc import KeysView, Sequence +from typing import Any import numpy as np from gymnasium.spaces.space import Space -class Dict(Space[typing.Dict[str, Any]], typing.Mapping[str, Space[Any]]): +class Dict(Space[dict[str, Any]], collections.abc.Mapping[str, Space[Any]]): """A dictionary of :class:`Space` instances. Elements of this space are (ordered) dictionaries of elements from the constituent spaces. diff --git a/gymnasium/spaces/discrete.py b/gymnasium/spaces/discrete.py index 9a4575252..87c60fe36 100644 --- a/gymnasium/spaces/discrete.py +++ b/gymnasium/spaces/discrete.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Iterable, Mapping, Sequence +from collections.abc import Iterable, Mapping, Sequence +from typing import Any import numpy as np @@ -26,9 +27,9 @@ class Discrete(Space[np.int64]): def __init__( self, - n: int | np.integer[Any], + n: int | np.integer, seed: int | np.random.Generator | None = None, - start: int | np.integer[Any] = 0, + start: int | np.integer = 0, ): r"""Constructor of :class:`Discrete` space. diff --git a/gymnasium/spaces/graph.py b/gymnasium/spaces/graph.py index 7f7c517eb..6dde68087 100644 --- a/gymnasium/spaces/graph.py +++ b/gymnasium/spaces/graph.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, NamedTuple, Sequence +from collections.abc import Sequence +from typing import Any, NamedTuple import numpy as np from numpy.typing import NDArray @@ -22,9 +23,9 @@ class GraphInstance(NamedTuple): * edge_links (Optional[np.ndarray]): an (m x 2) sized array of ints representing the indices of the two nodes that each edge connects. """ - nodes: NDArray[Any] - edges: NDArray[Any] | None - edge_links: NDArray[Any] | None + nodes: NDArray + edges: NDArray | None + edge_links: NDArray | None class Graph(Space[GraphInstance]): diff --git a/gymnasium/spaces/multi_binary.py b/gymnasium/spaces/multi_binary.py index 1da027fac..a1be6ffa4 100644 --- a/gymnasium/spaces/multi_binary.py +++ b/gymnasium/spaces/multi_binary.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any import numpy as np from numpy.typing import NDArray @@ -29,7 +30,7 @@ class MultiBinary(Space[NDArray[np.int8]]): def __init__( self, - n: NDArray[np.integer[Any]] | Sequence[int] | int, + n: NDArray[np.integer] | Sequence[int] | int, seed: int | np.random.Generator | None = None, ): """Constructor of :class:`MultiBinary` space. diff --git a/gymnasium/spaces/multi_discrete.py b/gymnasium/spaces/multi_discrete.py index 86ba399c8..02b7b86fb 100644 --- a/gymnasium/spaces/multi_discrete.py +++ b/gymnasium/spaces/multi_discrete.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Iterable, Mapping, Sequence +from collections.abc import Iterable, Mapping, Sequence +from typing import Any import numpy as np from numpy.typing import NDArray @@ -42,10 +43,10 @@ class MultiDiscrete(Space[NDArray[np.integer]]): def __init__( self, - nvec: NDArray[np.integer[Any]] | list[int], - dtype: str | type[np.integer[Any]] = np.int64, + nvec: NDArray[np.integer] | list[int] | list[list[int]], + dtype: str | type[np.integer] = np.int64, seed: int | np.random.Generator | None = None, - start: NDArray[np.integer[Any]] | list[int] | None = None, + start: NDArray[np.integer] | list[int] | list[list[int]] | None = None, ): """Constructor of :class:`MultiDiscrete` space. @@ -97,7 +98,7 @@ def is_np_flattenable(self): def sample( self, mask: tuple[MaskNDArray, ...] | None = None - ) -> NDArray[np.integer[Any]]: + ) -> NDArray[np.integer]: """Generates a single random sample this space. Args: @@ -112,8 +113,8 @@ def sample( def _apply_mask( sub_mask: MaskNDArray | tuple[MaskNDArray, ...], - sub_nvec: MaskNDArray | np.integer[Any], - sub_start: MaskNDArray | np.integer[Any], + sub_nvec: MaskNDArray | np.integer, + sub_start: MaskNDArray | np.integer, ) -> int | list[Any]: if isinstance(sub_nvec, np.ndarray): assert isinstance( @@ -177,14 +178,12 @@ def contains(self, x: Any) -> bool: ) def to_jsonable( - self, sample_n: Sequence[NDArray[np.integer[Any]]] + self, sample_n: Sequence[NDArray[np.integer]] ) -> list[Sequence[int]]: """Convert a batch of samples from this space to a JSONable data type.""" return [sample.tolist() for sample in sample_n] - def from_jsonable( - self, sample_n: list[Sequence[int]] - ) -> list[NDArray[np.integer[Any]]]: + def from_jsonable(self, sample_n: list[Sequence[int]]) -> list[NDArray[np.integer]]: """Convert a JSONable data type to a batch of samples from this space.""" return [np.array(sample, dtype=np.int64) for sample in sample_n] diff --git a/gymnasium/spaces/oneof.py b/gymnasium/spaces/oneof.py index 50e463be4..ef4dd4be1 100644 --- a/gymnasium/spaces/oneof.py +++ b/gymnasium/spaces/oneof.py @@ -2,8 +2,9 @@ from __future__ import annotations -import typing -from typing import Any, Iterable +import collections.abc +from collections.abc import Iterable +from typing import Any import numpy as np @@ -33,7 +34,7 @@ class OneOf(Space[Any]): def __init__( self, spaces: Iterable[Space[Any]], - seed: int | typing.Sequence[int] | np.random.Generator | None = None, + seed: int | collections.abc.Sequence[int] | np.random.Generator | None = None, ): r"""Constructor of :class:`OneOf` space. @@ -142,7 +143,7 @@ def __repr__(self) -> str: return "OneOf(" + ", ".join([str(s) for s in self.spaces]) + ")" def to_jsonable( - self, sample_n: typing.Sequence[tuple[int, Any]] + self, sample_n: collections.abc.Sequence[tuple[int, Any]] ) -> list[list[Any]]: """Convert a batch of samples from this space to a JSONable data type.""" return [ diff --git a/gymnasium/spaces/sequence.py b/gymnasium/spaces/sequence.py index 8d5560c0a..8e07c1f6c 100644 --- a/gymnasium/spaces/sequence.py +++ b/gymnasium/spaces/sequence.py @@ -2,7 +2,7 @@ from __future__ import annotations -import typing +import collections.abc from typing import Any, Union import numpy as np @@ -12,7 +12,7 @@ from gymnasium.spaces.space import Space -class Sequence(Space[Union[typing.Tuple[Any, ...], Any]]): +class Sequence(Space[Union[tuple[Any, ...], Any]]): r"""This space represent sets of finite-length sequences. This space represents the set of tuples of the form :math:`(a_0, \dots, a_n)` where the :math:`a_i` belong @@ -186,7 +186,7 @@ def __repr__(self) -> str: return f"Sequence({self.feature_space}, stack={self.stack})" def to_jsonable( - self, sample_n: typing.Sequence[tuple[Any, ...] | Any] + self, sample_n: collections.abc.Sequence[tuple[Any, ...] | Any] ) -> list[list[Any]]: """Convert a batch of samples from this space to a JSONable data type.""" if self.stack: diff --git a/gymnasium/spaces/space.py b/gymnasium/spaces/space.py index 4c6b4919e..91a5e9e7a 100644 --- a/gymnasium/spaces/space.py +++ b/gymnasium/spaces/space.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Generic, Iterable, Mapping, Sequence, TypeVar +from collections.abc import Iterable, Mapping, Sequence +from typing import Any, Generic, TypeVar import numpy as np import numpy.typing as npt diff --git a/gymnasium/spaces/tuple.py b/gymnasium/spaces/tuple.py index 05a1f652a..09c4ff74c 100644 --- a/gymnasium/spaces/tuple.py +++ b/gymnasium/spaces/tuple.py @@ -2,15 +2,16 @@ from __future__ import annotations -import typing -from typing import Any, Iterable +import collections.abc +from collections.abc import Iterable +from typing import Any import numpy as np from gymnasium.spaces.space import Space -class Tuple(Space[typing.Tuple[Any, ...]], typing.Sequence[Any]): +class Tuple(Space[tuple[Any, ...]], collections.abc.Sequence[Any]): """A tuple (more precisely: the cartesian product) of :class:`Space` instances. Elements of this space are tuples of elements of the constituent spaces. @@ -25,7 +26,7 @@ class Tuple(Space[typing.Tuple[Any, ...]], typing.Sequence[Any]): def __init__( self, spaces: Iterable[Space[Any]], - seed: int | typing.Sequence[int] | np.random.Generator | None = None, + seed: int | collections.abc.Sequence[int] | np.random.Generator | None = None, ): r"""Constructor of :class:`Tuple` space. @@ -47,7 +48,9 @@ def is_np_flattenable(self): """Checks whether this space can be flattened to a :class:`spaces.Box`.""" return all(space.is_np_flattenable for space in self.spaces) - def seed(self, seed: int | typing.Sequence[int] | None = None) -> tuple[int, ...]: + def seed( + self, seed: int | collections.abc.Sequence[int] | None = None + ) -> tuple[int, ...]: """Seed the PRNG of this space and all subspaces. Depending on the type of seed, the subspaces will be seeded differently @@ -130,7 +133,7 @@ def __repr__(self) -> str: return "Tuple(" + ", ".join([str(s) for s in self.spaces]) + ")" def to_jsonable( - self, sample_n: typing.Sequence[tuple[Any, ...]] + self, sample_n: collections.abc.Sequence[tuple[Any, ...]] ) -> list[list[Any]]: """Convert a batch of samples from this space to a JSONable data type.""" # serialize as list-repr of tuple of vectors diff --git a/gymnasium/spaces/utils.py b/gymnasium/spaces/utils.py index 3ce554707..06d2877ba 100644 --- a/gymnasium/spaces/utils.py +++ b/gymnasium/spaces/utils.py @@ -7,7 +7,6 @@ from __future__ import annotations import operator as op -import typing from functools import reduce, singledispatch from typing import Any, TypeVar, Union, cast @@ -111,9 +110,7 @@ def _flatdim_oneof(space: OneOf) -> int: T = TypeVar("T") -FlatType = Union[ - NDArray[Any], typing.Dict[str, Any], typing.Tuple[Any, ...], GraphInstance -] +FlatType = Union[NDArray[Any], dict[str, Any], tuple[Any, ...], GraphInstance] @singledispatch @@ -319,8 +316,8 @@ def _unflatten_discrete(space: Discrete, x: NDArray[np.int64]) -> np.int64: @unflatten.register(MultiDiscrete) def _unflatten_multidiscrete( - space: MultiDiscrete, x: NDArray[np.integer[Any]] -) -> NDArray[np.integer[Any]]: + space: MultiDiscrete, x: NDArray[np.integer] +) -> NDArray[np.integer]: offsets = np.zeros((space.nvec.size + 1,), dtype=space.dtype) offsets[1:] = np.cumsum(space.nvec.flatten()) nonzero = np.nonzero(x) diff --git a/gymnasium/utils/passive_env_checker.py b/gymnasium/utils/passive_env_checker.py index 0b08fc516..0a4d2ce32 100644 --- a/gymnasium/utils/passive_env_checker.py +++ b/gymnasium/utils/passive_env_checker.py @@ -1,8 +1,8 @@ """A set of functions for passively checking environment implementations.""" import inspect +from collections.abc import Callable from functools import partial -from typing import Callable import numpy as np diff --git a/gymnasium/utils/performance.py b/gymnasium/utils/performance.py index 54d4da7b8..dd7ea7849 100644 --- a/gymnasium/utils/performance.py +++ b/gymnasium/utils/performance.py @@ -1,7 +1,7 @@ """A collection of runtime performance bencharks, useful for debugging performance related issues.""" import time -from typing import Callable +from collections.abc import Callable import gymnasium diff --git a/gymnasium/utils/play.py b/gymnasium/utils/play.py index 9f863c0e4..930291b6a 100644 --- a/gymnasium/utils/play.py +++ b/gymnasium/utils/play.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import deque -from typing import Callable, List +from collections.abc import Callable import numpy as np @@ -85,7 +85,7 @@ def _get_relevant_keys( def _get_video_size(self, zoom: float | None = None) -> tuple[int, int]: rendered = self.env.render() - if isinstance(rendered, List): + if isinstance(rendered, list): rendered = rendered[-1] assert rendered is not None and isinstance(rendered, np.ndarray) video_size = (rendered.shape[1], rendered.shape[0]) @@ -293,7 +293,7 @@ def play( callback(prev_obs, obs, action, rew, terminated, truncated, info) if obs is not None: rendered = env.render() - if isinstance(rendered, List): + if isinstance(rendered, list): rendered = rendered[-1] assert rendered is not None and isinstance(rendered, np.ndarray) display_arr( diff --git a/gymnasium/utils/save_video.py b/gymnasium/utils/save_video.py index 9185af180..2c97ca20a 100644 --- a/gymnasium/utils/save_video.py +++ b/gymnasium/utils/save_video.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Callable +from collections.abc import Callable import gymnasium as gym from gymnasium import logger diff --git a/gymnasium/utils/step_api_compatibility.py b/gymnasium/utils/step_api_compatibility.py index cacb727f6..539b51383 100644 --- a/gymnasium/utils/step_api_compatibility.py +++ b/gymnasium/utils/step_api_compatibility.py @@ -2,21 +2,21 @@ from __future__ import annotations -from typing import SupportsFloat, Tuple, Union +from typing import SupportsFloat, Union import numpy as np from gymnasium.core import ObsType -DoneStepType = Tuple[ +DoneStepType = tuple[ Union[ObsType, np.ndarray], Union[SupportsFloat, np.ndarray], Union[bool, np.ndarray], Union[dict, list], ] -TerminatedTruncatedStepType = Tuple[ +TerminatedTruncatedStepType = tuple[ Union[ObsType, np.ndarray], Union[SupportsFloat, np.ndarray], Union[bool, np.ndarray], diff --git a/gymnasium/vector/async_vector_env.py b/gymnasium/vector/async_vector_env.py index 3d7e24b78..7e91eb150 100644 --- a/gymnasium/vector/async_vector_env.py +++ b/gymnasium/vector/async_vector_env.py @@ -2,15 +2,18 @@ from __future__ import annotations +import collections.abc import multiprocessing import sys import time import traceback +from collections.abc import Callable from copy import deepcopy from enum import Enum from multiprocessing import Queue from multiprocessing.connection import Connection -from typing import Any, Callable, Sequence +from multiprocessing.sharedctypes import SynchronizedArray +from typing import Any import numpy as np @@ -89,7 +92,7 @@ class AsyncVectorEnv(VectorEnv): def __init__( self, - env_fns: Sequence[Callable[[], Env]], + env_fns: collections.abc.Sequence[Callable[[], Env]], shared_memory: bool = True, copy: bool = True, context: str | None = None, @@ -683,10 +686,10 @@ def __del__(self): def _async_worker( index: int, - env_fn: callable, + env_fn: Callable, pipe: Connection, parent_pipe: Connection, - shared_memory: multiprocessing.Array | dict[str, Any] | tuple[Any, ...], + shared_memory: SynchronizedArray | dict[str, Any] | tuple[Any, ...], error_queue: Queue, ): env = env_fn() diff --git a/gymnasium/vector/sync_vector_env.py b/gymnasium/vector/sync_vector_env.py index b92a26889..8ef9f8d5e 100644 --- a/gymnasium/vector/sync_vector_env.py +++ b/gymnasium/vector/sync_vector_env.py @@ -2,8 +2,10 @@ from __future__ import annotations +import collections.abc +from collections.abc import Callable, Iterator from copy import deepcopy -from typing import Any, Callable, Iterator, Sequence +from typing import Any import numpy as np @@ -62,7 +64,9 @@ class SyncVectorEnv(VectorEnv): def __init__( self, - env_fns: Iterator[Callable[[], Env]] | Sequence[Callable[[], Env]], + env_fns: ( + Iterator[Callable[[], Env]] | collections.abc.Sequence[Callable[[], Env]] + ), copy: bool = True, observation_mode: str | Space = "same", ): diff --git a/gymnasium/vector/utils/shared_memory.py b/gymnasium/vector/utils/shared_memory.py index 914f6c962..c3da4237a 100644 --- a/gymnasium/vector/utils/shared_memory.py +++ b/gymnasium/vector/utils/shared_memory.py @@ -5,6 +5,7 @@ import multiprocessing as mp from ctypes import c_bool from functools import singledispatch +from multiprocessing.sharedctypes import SynchronizedArray from typing import Any import numpy as np @@ -32,7 +33,7 @@ @singledispatch def create_shared_memory( space: Space[Any], n: int = 1, ctx=mp -) -> dict[str, Any] | tuple[Any, ...] | mp.Array: +) -> dict[str, Any] | tuple[Any, ...] | SynchronizedArray: """Create a shared memory object, to be shared across processes. This eventually contains the observations from the vectorized environment. @@ -109,7 +110,9 @@ def _create_dynamic_shared_memory(space: Graph | Sequence, n: int = 1, ctx=mp): @singledispatch def read_from_shared_memory( - space: Space, shared_memory: dict | tuple | mp.Array, n: int = 1 + space: Space, + shared_memory: dict[str, Any] | tuple[Any, ...] | SynchronizedArray, + n: int = 1, ) -> dict[str, Any] | tuple[Any, ...] | np.ndarray: """Read the batch of observations from shared memory as a numpy array. @@ -209,7 +212,7 @@ def write_to_shared_memory( space: Space, index: int, value: np.ndarray, - shared_memory: dict[str, Any] | tuple[Any, ...] | mp.Array, + shared_memory: dict[str, Any] | tuple[Any, ...] | SynchronizedArray, ): """Write the observation of a single environment into shared memory. diff --git a/gymnasium/vector/utils/space_utils.py b/gymnasium/vector/utils/space_utils.py index 05c5be55d..3434cf66b 100644 --- a/gymnasium/vector/utils/space_utils.py +++ b/gymnasium/vector/utils/space_utils.py @@ -9,10 +9,11 @@ from __future__ import annotations -import typing +import collections.abc +from collections.abc import Callable, Iterable, Iterator from copy import deepcopy from functools import singledispatch -from typing import Any, Iterable, Iterator +from typing import Any import numpy as np @@ -148,7 +149,7 @@ def _batch_space_custom(space: Graph | Text | Sequence | OneOf, n: int = 1): @singledispatch -def batch_differing_spaces(spaces: typing.Sequence[Space]) -> Space: +def batch_differing_spaces(spaces: collections.abc.Sequence[Space]) -> Space: """Batch a Sequence of spaces where subspaces to contain minor differences. Args: @@ -428,7 +429,7 @@ def _concatenate_custom(space: Space, items: Iterable, out: None) -> tuple[Any, @singledispatch def create_empty_array( - space: Space, n: int = 1, fn: callable = np.zeros + space: Space, n: int = 1, fn: Callable = np.zeros ) -> tuple[Any, ...] | dict[str, Any] | np.ndarray: """Create an empty (possibly nested and normally numpy-based) array, used in conjunction with ``concatenate(..., out=array)``. diff --git a/gymnasium/wrappers/jax_to_numpy.py b/gymnasium/wrappers/jax_to_numpy.py index 8c632b84a..582c35c5f 100644 --- a/gymnasium/wrappers/jax_to_numpy.py +++ b/gymnasium/wrappers/jax_to_numpy.py @@ -5,7 +5,8 @@ import functools import numbers from collections import abc -from typing import Any, Iterable, Mapping, SupportsFloat +from collections.abc import Iterable, Mapping +from typing import Any, SupportsFloat import numpy as np diff --git a/gymnasium/wrappers/jax_to_torch.py b/gymnasium/wrappers/jax_to_torch.py index fb13d37eb..43d90a9f4 100644 --- a/gymnasium/wrappers/jax_to_torch.py +++ b/gymnasium/wrappers/jax_to_torch.py @@ -12,7 +12,8 @@ import functools import numbers from collections import abc -from typing import Any, Iterable, Mapping, SupportsFloat, Union +from collections.abc import Iterable, Mapping +from typing import Any, SupportsFloat, Union import gymnasium as gym from gymnasium.core import RenderFrame, WrapperActType, WrapperObsType diff --git a/gymnasium/wrappers/numpy_to_torch.py b/gymnasium/wrappers/numpy_to_torch.py index 8275790ee..8f6d5dae7 100644 --- a/gymnasium/wrappers/numpy_to_torch.py +++ b/gymnasium/wrappers/numpy_to_torch.py @@ -5,7 +5,8 @@ import functools import numbers from collections import abc -from typing import Any, Iterable, Mapping, SupportsFloat, Union +from collections.abc import Iterable, Mapping +from typing import Any, SupportsFloat, Union import numpy as np diff --git a/gymnasium/wrappers/rendering.py b/gymnasium/wrappers/rendering.py index 7291df06d..28f02c59a 100644 --- a/gymnasium/wrappers/rendering.py +++ b/gymnasium/wrappers/rendering.py @@ -8,8 +8,9 @@ from __future__ import annotations import os +from collections.abc import Callable from copy import deepcopy -from typing import Any, Callable, List, SupportsFloat +from typing import Any, SupportsFloat import numpy as np @@ -310,7 +311,7 @@ def _capture_frame(self): assert self.recording, "Cannot capture a frame, recording wasn't started." frame = self.env.render() - if isinstance(frame, List): + if isinstance(frame, list): if len(frame) == 0: # render was called return self.render_history += frame @@ -363,7 +364,7 @@ def step( def render(self) -> RenderFrame | list[RenderFrame]: """Compute the render frames as specified by render_mode attribute during initialization of the environment.""" render_out = super().render() - if self.recording and isinstance(render_out, List): + if self.recording and isinstance(render_out, list): self.recorded_frames += render_out if len(self.render_history) > 0: diff --git a/gymnasium/wrappers/transform_action.py b/gymnasium/wrappers/transform_action.py index a069ab04f..69039e9a3 100644 --- a/gymnasium/wrappers/transform_action.py +++ b/gymnasium/wrappers/transform_action.py @@ -7,7 +7,7 @@ from __future__ import annotations -from typing import Callable +from collections.abc import Callable import numpy as np diff --git a/gymnasium/wrappers/transform_observation.py b/gymnasium/wrappers/transform_observation.py index 824a401c3..f85d674d0 100644 --- a/gymnasium/wrappers/transform_observation.py +++ b/gymnasium/wrappers/transform_observation.py @@ -13,7 +13,9 @@ from __future__ import annotations -from typing import Any, Callable, Final, Sequence +import collections.abc +from collections.abc import Callable +from typing import Any, Final import numpy as np @@ -124,7 +126,9 @@ class FilterObservation( """ def __init__( - self, env: gym.Env[ObsType, ActType], filter_keys: Sequence[str | int] + self, + env: gym.Env[ObsType, ActType], + filter_keys: collections.abc.Sequence[str | int], ): """Constructor for the filter observation wrapper. @@ -132,7 +136,7 @@ def __init__( env: The environment to wrap filter_keys: The set of subspaces to be *included*, use a list of strings for ``Dict`` and integers for ``Tuple`` spaces """ - if not isinstance(filter_keys, Sequence): + if not isinstance(filter_keys, collections.abc.Sequence): raise TypeError( f"Expects `filter_keys` to be a Sequence, actual type: {type(filter_keys)}" ) @@ -211,7 +215,7 @@ def __init__( f"FilterObservation wrapper is only usable with `Dict` and `Tuple` observations, actual type: {type(env.observation_space)}" ) - self.filter_keys: Final[Sequence[str | int]] = filter_keys + self.filter_keys: Final[collections.abc.Sequence[str | int]] = filter_keys class FlattenObservation( diff --git a/gymnasium/wrappers/transform_reward.py b/gymnasium/wrappers/transform_reward.py index b17308c25..673a0c03c 100644 --- a/gymnasium/wrappers/transform_reward.py +++ b/gymnasium/wrappers/transform_reward.py @@ -6,7 +6,8 @@ from __future__ import annotations -from typing import Callable, SupportsFloat +from collections.abc import Callable +from typing import SupportsFloat import numpy as np diff --git a/gymnasium/wrappers/utils.py b/gymnasium/wrappers/utils.py index 5fc896c8f..ea6a0ebe1 100644 --- a/gymnasium/wrappers/utils.py +++ b/gymnasium/wrappers/utils.py @@ -2,8 +2,8 @@ from __future__ import annotations +from collections.abc import Callable from functools import singledispatch -from typing import Callable import numpy as np diff --git a/gymnasium/wrappers/vector/rendering.py b/gymnasium/wrappers/vector/rendering.py index 81418e385..5d9f5e46e 100644 --- a/gymnasium/wrappers/vector/rendering.py +++ b/gymnasium/wrappers/vector/rendering.py @@ -13,6 +13,11 @@ from gymnasium.vector.vector_env import ArrayType +__all__ = [ + "HumanRendering", +] + + class HumanRendering(VectorWrapper): """Adds support for Human-based Rendering for Vector-based environments.""" diff --git a/gymnasium/wrappers/vector/vectorize_action.py b/gymnasium/wrappers/vector/vectorize_action.py index f0f0b8e57..7a940f11b 100644 --- a/gymnasium/wrappers/vector/vectorize_action.py +++ b/gymnasium/wrappers/vector/vectorize_action.py @@ -2,8 +2,9 @@ from __future__ import annotations +from collections.abc import Callable from copy import deepcopy -from typing import Any, Callable +from typing import Any import numpy as np @@ -14,6 +15,9 @@ from gymnasium.wrappers import transform_action +__all__ = ["TransformAction", "VectorizeTransformAction", "ClipAction", "RescaleAction"] + + class TransformAction(VectorActionWrapper): """Transforms an action via a function provided to the wrapper. diff --git a/gymnasium/wrappers/vector/vectorize_observation.py b/gymnasium/wrappers/vector/vectorize_observation.py index 88bd539ad..13ded4d04 100644 --- a/gymnasium/wrappers/vector/vectorize_observation.py +++ b/gymnasium/wrappers/vector/vectorize_observation.py @@ -2,8 +2,10 @@ from __future__ import annotations +import collections.abc +from collections.abc import Callable from copy import deepcopy -from typing import Any, Callable, Sequence +from typing import Any import numpy as np @@ -14,6 +16,19 @@ from gymnasium.wrappers import transform_observation +__all__ = [ + "TransformObservation", + "VectorizeTransformObservation", + "FilterObservation", + "FlattenObservation", + "GrayscaleObservation", + "ResizeObservation", + "RescaleObservation", + "RescaleObservation", + "DtypeObservation", +] + + class TransformObservation(VectorObservationWrapper): """Transforms an observation via a function provided to the wrapper. @@ -196,12 +211,14 @@ class FilterObservation(VectorizeTransformObservation): dtype=float32)} """ - def __init__(self, env: VectorEnv, filter_keys: Sequence[str | int]): + def __init__( + self, env: VectorEnv, filter_keys: collections.abc.Sequence[str | int] + ): """Constructor for the filter observation wrapper. Args: env: The vector environment to wrap - filter_keys: The subspaces to be included, use a list of strings or integers for ``Dict`` and ``Tuple`` spaces respectivesly + filter_keys: The subspaces to be included, use a list of strings or integers for ``Dict`` and ``Tuple`` spaces respectively """ super().__init__( env, transform_observation.FilterObservation, filter_keys=filter_keys diff --git a/gymnasium/wrappers/vector/vectorize_reward.py b/gymnasium/wrappers/vector/vectorize_reward.py index 5f80192c4..f42f6fb28 100644 --- a/gymnasium/wrappers/vector/vectorize_reward.py +++ b/gymnasium/wrappers/vector/vectorize_reward.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Callable +from collections.abc import Callable +from typing import Any import numpy as np @@ -12,6 +13,13 @@ from gymnasium.wrappers import transform_reward +__all__ = [ + "TransformReward", + "VectorizeTransformReward", + "ClipReward", +] + + class TransformReward(VectorRewardWrapper): """A reward wrapper that allows a custom function to modify the step reward. diff --git a/pyproject.toml b/pyproject.toml index 0b8046372..e89673799 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" name = "gymnasium" description = "A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)." readme = "README.md" -requires-python = ">= 3.8" +requires-python = ">= 3.9" authors = [{ name = "Farama Foundation", email = "contact@farama.org" }] license = { text = "MIT License" } keywords = ["Reinforcement Learning", "game", "RL", "AI", "gymnasium"] @@ -16,7 +16,6 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -78,9 +77,10 @@ all = [ "moviepy >=1.0.0", ] testing = [ - "pytest ==7.1.3", + "pytest >=7.1.3", "scipy >=1.7.3", "dill >=0.3.7", + "pytest-beartype", ] [project.urls] @@ -124,7 +124,7 @@ exclude = ["tests/**", "**/node_modules", "**/__pycache__"] strict = [] typeCheckingMode = "basic" -pythonVersion = "3.8" +pythonVersion = "3.9" pythonPlatform = "All" typeshedPath = "typeshed" enableTypeIgnoreComments = true diff --git a/tests/envs/functional/test_core.py b/tests/envs/functional/test_core.py index 3b7349e04..a9d8b312c 100644 --- a/tests/envs/functional/test_core.py +++ b/tests/envs/functional/test_core.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any, Optional import numpy as np @@ -6,7 +6,7 @@ class BasicTestEnv(FuncEnv): - def __init__(self, options: Optional[Dict[str, Any]] = None): + def __init__(self, options: Optional[dict[str, Any]] = None): super().__init__(options) def initial(self, rng: Any) -> np.ndarray: diff --git a/tests/envs/utils.py b/tests/envs/utils.py index 415e9fd5a..14c28a982 100644 --- a/tests/envs/utils.py +++ b/tests/envs/utils.py @@ -1,6 +1,6 @@ """Finds all the specs that we can test with""" -from typing import List, Optional +from typing import Optional import gymnasium as gym from gymnasium import logger @@ -30,23 +30,23 @@ def try_make_env(env_spec: EnvSpec) -> Optional[gym.Env]: # Tries to make all environment to test with -all_testing_initialised_envs: List[Optional[gym.Env]] = [ +all_testing_initialised_envs: list[Optional[gym.Env]] = [ try_make_env(env_spec) for env_spec in gym.envs.registry.values() ] -all_testing_initialised_envs: List[gym.Env] = [ +all_testing_initialised_envs: list[gym.Env] = [ env for env in all_testing_initialised_envs if env is not None ] # All testing, mujoco and gymnasium environment specs -all_testing_env_specs: List[EnvSpec] = [ +all_testing_env_specs: list[EnvSpec] = [ env.spec for env in all_testing_initialised_envs ] -mujoco_testing_env_specs: List[EnvSpec] = [ +mujoco_testing_env_specs: list[EnvSpec] = [ env_spec for env_spec in all_testing_env_specs if "gymnasium.envs.mujoco" in env_spec.entry_point ] -gym_testing_env_specs: List[EnvSpec] = [ +gym_testing_env_specs: list[EnvSpec] = [ env_spec for env_spec in all_testing_env_specs if any( diff --git a/tests/spaces/test_spaces.py b/tests/spaces/test_spaces.py index 6df5963dc..6386abaf1 100644 --- a/tests/spaces/test_spaces.py +++ b/tests/spaces/test_spaces.py @@ -3,7 +3,8 @@ import json # note: ujson fails this test due to float equality import pickle import tempfile -from typing import Callable, List, Union +from collections.abc import Callable +from typing import Union import numpy as np import pytest @@ -402,7 +403,7 @@ def test_space_sample_mask(space: Space, mask, n_trials: int = 100): # Due to the multi-axis capability of MultiDiscrete, these functions need to be recursive and that the expected / observed numpy are of non-regular shapes def _generate_frequency( _dim: Union[np.ndarray, int], _mask, func: Callable - ) -> List: + ) -> list: if isinstance(_dim, np.ndarray): return [ _generate_frequency(sub_dim, sub_mask, func) diff --git a/tests/spaces/utils.py b/tests/spaces/utils.py index 4f1b4990a..b1849a4f8 100644 --- a/tests/spaces/utils.py +++ b/tests/spaces/utils.py @@ -1,5 +1,3 @@ -from typing import List - import numpy as np from gymnasium.spaces import ( @@ -116,7 +114,7 @@ ] TESTING_COMPOSITE_SPACES_IDS = [f"{space}" for space in TESTING_COMPOSITE_SPACES] -TESTING_SPACES: List[Space] = TESTING_FUNDAMENTAL_SPACES + TESTING_COMPOSITE_SPACES +TESTING_SPACES: list[Space] = TESTING_FUNDAMENTAL_SPACES + TESTING_COMPOSITE_SPACES TESTING_SPACES_IDS = TESTING_FUNDAMENTAL_SPACES_IDS + TESTING_COMPOSITE_SPACES_IDS diff --git a/tests/utils/test_env_checker.py b/tests/utils/test_env_checker.py index e365c4fa7..fe31569ec 100644 --- a/tests/utils/test_env_checker.py +++ b/tests/utils/test_env_checker.py @@ -2,7 +2,8 @@ import re import warnings -from typing import Callable, Tuple, Union +from collections.abc import Callable +from typing import Union import numpy as np import pytest @@ -137,7 +138,7 @@ def test_check_reset_seed_determinism(test, func: Callable, message: str): def _deprecated_return_info( self, return_info: bool = False -) -> Union[Tuple[ObsType, dict], ObsType]: +) -> Union[tuple[ObsType, dict], ObsType]: """function to simulate the signature and behavior of a `reset` function with the deprecated `return_info` optional argument""" if return_info: return self.observation_space.sample(), {} diff --git a/tests/utils/test_passive_env_checker.py b/tests/utils/test_passive_env_checker.py index 38ba95b48..24c2b3144 100644 --- a/tests/utils/test_passive_env_checker.py +++ b/tests/utils/test_passive_env_checker.py @@ -1,6 +1,7 @@ import re import warnings -from typing import Callable, Dict, Union +from collections.abc import Callable +from typing import Union import numpy as np import pytest @@ -267,7 +268,7 @@ def _reset_result(self, seed=None, options=None): ], ], ) -def test_passive_env_reset_checker(test, func: Callable, message: str, kwargs: Dict): +def test_passive_env_reset_checker(test, func: Callable, message: str, kwargs: dict): """Tests the passive env reset check""" if test is UserWarning: with pytest.warns( diff --git a/tests/utils/test_play.py b/tests/utils/test_play.py index 39a10c249..9ccdb666e 100644 --- a/tests/utils/test_play.py +++ b/tests/utils/test_play.py @@ -1,6 +1,6 @@ +from collections.abc import Callable from functools import partial from itertools import product -from typing import Callable import numpy as np import pygame diff --git a/tests/vector/utils/test_space_utils.py b/tests/vector/utils/test_space_utils.py index c03df1269..064f593fe 100644 --- a/tests/vector/utils/test_space_utils.py +++ b/tests/vector/utils/test_space_utils.py @@ -2,7 +2,7 @@ import copy import re -from typing import Iterable +from collections.abc import Iterable import numpy as np import pytest diff --git a/tests/wrappers/test_record_video.py b/tests/wrappers/test_record_video.py index b7bdcb1f2..6dab9ca24 100644 --- a/tests/wrappers/test_record_video.py +++ b/tests/wrappers/test_record_video.py @@ -2,7 +2,6 @@ import os import shutil -from typing import List import numpy as np import pytest @@ -151,13 +150,13 @@ def test_with_rgb_array_list(n_steps: int = 10): env.step(env.action_space.sample()) render_out = env.render() - assert isinstance(render_out, List) + assert isinstance(render_out, list) assert len(render_out) == n_steps + 1 assert all(isinstance(render, np.ndarray) for render in render_out) assert all(render.ndim == 3 for render in render_out) render_out = env.render() - assert isinstance(render_out, List) + assert isinstance(render_out, list) assert len(render_out) == 0 env.close() @@ -173,13 +172,13 @@ def test_with_rgb_array_list(n_steps: int = 10): env.step(env.action_space.sample()) render_out = env.render() - assert isinstance(render_out, List) + assert isinstance(render_out, list) assert len(render_out) == n_steps + 1 assert all(isinstance(render, np.ndarray) for render in render_out) assert all(render.ndim == 3 for render in render_out) render_out = env.render() - assert isinstance(render_out, List) + assert isinstance(render_out, list) assert len(render_out) == 0 env.close()