mirror of
https://github.com/Farama-Foundation/Gymnasium.git
synced 2025-07-31 05:44:31 +00:00
Add support for python 3.6 (#2836)
* Add support for python 3.6 * Add support for python 3.6 * Added check for python 3.6 to not install mujoco as no version exists * Fixed the install groups for python 3.6 * Re-added python 3.6 support for gym * black * Added support for dataclasses through dataclasses module in setup that backports the module * Fixed install requirements * Re-added dummy env spec with dataclasses * Changed type for compatability for python 3.6 * Added a python 3.6 warning * Fixed python 3.6 typing issue * Removed __future__ import annotation for python 3.6 support * Fixed python 3.6 typing
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.7', '3.8', '3.9', '3.10']
|
||||
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
|
@@ -41,7 +41,7 @@ repos:
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
# TODO: remove `--keep-runtime-typing` option
|
||||
args: ["--py37-plus", "--keep-runtime-typing"]
|
||||
args: ["--py36-plus", "--keep-runtime-typing"]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pyright
|
||||
|
28
gym/core.py
28
gym/core.py
@@ -1,13 +1,17 @@
|
||||
"""Core API for Environment, Wrapper, ActionWrapper, RewardWrapper and ObservationWrapper."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Generic, Optional, SupportsFloat, TypeVar, Union
|
||||
import sys
|
||||
from typing import Generic, Optional, SupportsFloat, Tuple, TypeVar, Union
|
||||
|
||||
from gym import spaces
|
||||
from gym.logger import deprecation
|
||||
from gym.logger import deprecation, warn
|
||||
from gym.utils import seeding
|
||||
from gym.utils.seeding import RandomNumberGenerator
|
||||
|
||||
if sys.version_info == (3, 6):
|
||||
warn(
|
||||
"Gym minimally supports python 3.6 as the python foundation not longer supports the version, please update your version to 3.7+"
|
||||
)
|
||||
|
||||
ObsType = TypeVar("ObsType")
|
||||
ActType = TypeVar("ActType")
|
||||
|
||||
@@ -62,7 +66,7 @@ class Env(Generic[ObsType, ActType]):
|
||||
def np_random(self, value: RandomNumberGenerator):
|
||||
self._np_random = value
|
||||
|
||||
def step(self, action: ActType) -> tuple[ObsType, float, bool, dict]:
|
||||
def step(self, action: ActType) -> Tuple[ObsType, float, bool, dict]:
|
||||
"""Run one timestep of the environment's dynamics.
|
||||
|
||||
When end of episode is reached, you are responsible for calling :meth:`reset` to reset this environment's state.
|
||||
@@ -92,7 +96,7 @@ class Env(Generic[ObsType, ActType]):
|
||||
seed: Optional[int] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
) -> Union[ObsType, tuple[ObsType, dict]]:
|
||||
) -> Union[ObsType, Tuple[ObsType, dict]]:
|
||||
"""Resets the environment to an initial state and returns the initial observation.
|
||||
|
||||
This method can reset the environment's random number generator(s) if ``seed`` is an integer or
|
||||
@@ -201,7 +205,7 @@ class Env(Generic[ObsType, ActType]):
|
||||
return [seed]
|
||||
|
||||
@property
|
||||
def unwrapped(self) -> Env:
|
||||
def unwrapped(self) -> "Env":
|
||||
"""Returns the base non-wrapped environment.
|
||||
|
||||
Returns:
|
||||
@@ -248,7 +252,7 @@ class Wrapper(Env[ObsType, ActType]):
|
||||
|
||||
self._action_space: Optional[spaces.Space] = None
|
||||
self._observation_space: Optional[spaces.Space] = None
|
||||
self._reward_range: Optional[tuple[SupportsFloat, SupportsFloat]] = None
|
||||
self._reward_range: Optional[Tuple[SupportsFloat, SupportsFloat]] = None
|
||||
self._metadata: Optional[dict] = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
@@ -290,14 +294,14 @@ class Wrapper(Env[ObsType, ActType]):
|
||||
self._observation_space = space
|
||||
|
||||
@property
|
||||
def reward_range(self) -> tuple[SupportsFloat, SupportsFloat]:
|
||||
def reward_range(self) -> Tuple[SupportsFloat, SupportsFloat]:
|
||||
"""Return the reward range of the environment."""
|
||||
if self._reward_range is None:
|
||||
return self.env.reward_range
|
||||
return self._reward_range
|
||||
|
||||
@reward_range.setter
|
||||
def reward_range(self, value: tuple[SupportsFloat, SupportsFloat]):
|
||||
def reward_range(self, value: Tuple[SupportsFloat, SupportsFloat]):
|
||||
self._reward_range = value
|
||||
|
||||
@property
|
||||
@@ -311,11 +315,11 @@ class Wrapper(Env[ObsType, ActType]):
|
||||
def metadata(self, value):
|
||||
self._metadata = value
|
||||
|
||||
def step(self, action: ActType) -> tuple[ObsType, float, bool, dict]:
|
||||
def step(self, action: ActType) -> Tuple[ObsType, float, bool, dict]:
|
||||
"""Steps through the environment with action."""
|
||||
return self.env.step(action)
|
||||
|
||||
def reset(self, **kwargs) -> Union[ObsType, tuple[ObsType, dict]]:
|
||||
def reset(self, **kwargs) -> Union[ObsType, Tuple[ObsType, dict]]:
|
||||
"""Resets the environment with kwargs."""
|
||||
return self.env.reset(**kwargs)
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import copy
|
||||
import difflib
|
||||
@@ -10,12 +8,14 @@ import sys
|
||||
import warnings
|
||||
from dataclasses import dataclass, field
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
SupportsFloat,
|
||||
Tuple,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
@@ -34,15 +34,11 @@ else:
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
|
||||
class Literal(str):
|
||||
def __class_getitem__(cls, item):
|
||||
return Any
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
from gym import Env, error, logger
|
||||
|
||||
ENV_ID_RE: re.Pattern = re.compile(
|
||||
ENV_ID_RE = re.compile(
|
||||
r"^(?:(?P<namespace>[\w:-]+)\/)?(?:(?P<name>[\w:.-]+?))(?:-v(?P<version>\d+))?$"
|
||||
)
|
||||
|
||||
@@ -54,7 +50,7 @@ def load(name: str) -> type:
|
||||
return fn
|
||||
|
||||
|
||||
def parse_env_id(id: str) -> tuple[Optional[str], str, Optional[int]]:
|
||||
def parse_env_id(id: str) -> Tuple[Optional[str], str, Optional[int]]:
|
||||
"""Parse environment ID string format.
|
||||
|
||||
This format is true today, but it's *not* an official spec.
|
||||
@@ -241,7 +237,7 @@ def _check_version_exists(ns: Optional[str], name: str, version: Optional[int]):
|
||||
|
||||
|
||||
def find_highest_version(ns: Optional[str], name: str) -> Optional[int]:
|
||||
version: list[int] = [
|
||||
version: List[int] = [
|
||||
spec_.version
|
||||
for spec_ in registry.values()
|
||||
if spec_.namespace == ns and spec_.name == name and spec_.version is not None
|
||||
@@ -302,39 +298,39 @@ def load_env_plugins(entry_point: str = "gym.envs") -> None:
|
||||
|
||||
|
||||
@overload
|
||||
def make(id: Literal["CartPole-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["CartPole-v0", "CartPole-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["MountainCar-v0"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["MountainCar-v0"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["MountainCarContinuous-v0"], **kwargs) -> Env[np.ndarray, np.ndarray | Sequence[SupportsFloat]]: ...
|
||||
def make(id: Literal["MountainCarContinuous-v0"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, Sequence[SupportsFloat]]]: ...
|
||||
@overload
|
||||
def make(id: Literal["Pendulum-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | Sequence[SupportsFloat]]: ...
|
||||
def make(id: Literal["Pendulum-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, Sequence[SupportsFloat]]]: ...
|
||||
@overload
|
||||
def make(id: Literal["Acrobot-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["Acrobot-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
|
||||
# Box2d
|
||||
# ----------------------------------------
|
||||
|
||||
|
||||
@overload
|
||||
def make(id: Literal["LunarLander-v2", "LunarLanderContinuous-v2"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["LunarLander-v2", "LunarLanderContinuous-v2"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["BipedalWalker-v3", "BipedalWalkerHardcore-v3"], **kwargs) -> Env[np.ndarray, np.ndarray | Sequence[SupportsFloat]]: ...
|
||||
def make(id: Literal["BipedalWalker-v3", "BipedalWalkerHardcore-v3"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, Sequence[SupportsFloat]]]: ...
|
||||
@overload
|
||||
def make(id: Literal["CarRacing-v1", "CarRacingDomainRandomize-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | Sequence[SupportsFloat]]: ...
|
||||
def make(id: Literal["CarRacing-v1", "CarRacingDomainRandomize-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, Sequence[SupportsFloat]]]: ...
|
||||
|
||||
# Toy Text
|
||||
# ----------------------------------------
|
||||
|
||||
|
||||
@overload
|
||||
def make(id: Literal["Blackjack-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["Blackjack-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["FrozenLake-v1", "FrozenLake8x8-v1"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["FrozenLake-v1", "FrozenLake8x8-v1"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["CliffWalking-v0"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["CliffWalking-v0"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
@overload
|
||||
def make(id: Literal["Taxi-v3"], **kwargs) -> Env[np.ndarray, np.ndarray | int]: ...
|
||||
def make(id: Literal["Taxi-v3"], **kwargs) -> Env[np.ndarray, Union[np.ndarray, int]]: ...
|
||||
|
||||
# Mujoco
|
||||
# ----------------------------------------
|
||||
@@ -415,7 +411,7 @@ class EnvRegistry(dict):
|
||||
|
||||
|
||||
# Global registry of environments. Meant to be accessed through `register` and `make`
|
||||
registry: dict[str, EnvSpec] = EnvRegistry()
|
||||
registry: Dict[str, EnvSpec] = EnvRegistry()
|
||||
current_namespace: Optional[str] = None
|
||||
|
||||
|
||||
@@ -522,7 +518,7 @@ def register(id: str, **kwargs):
|
||||
|
||||
|
||||
def make(
|
||||
id: str | EnvSpec,
|
||||
id: Union[str, EnvSpec],
|
||||
max_episode_steps: Optional[int] = None,
|
||||
autoreset: bool = False,
|
||||
disable_env_checker: bool = False,
|
||||
|
@@ -1,9 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import closing
|
||||
from io import StringIO
|
||||
from os import path
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -31,7 +29,7 @@ MAPS = {
|
||||
}
|
||||
|
||||
|
||||
def generate_random_map(size: int = 8, p: float = 0.8) -> list[str]:
|
||||
def generate_random_map(size: int = 8, p: float = 0.8) -> List[str]:
|
||||
"""Generates a random valid map (one that has a path from start to goal)
|
||||
|
||||
Args:
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Implementation of a space that represents closed boxes in euclidean space."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Sequence, SupportsFloat, Union
|
||||
from typing import List, Optional, Sequence, SupportsFloat, Tuple, Type, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -52,8 +50,8 @@ class Box(Space[np.ndarray]):
|
||||
low: Union[SupportsFloat, np.ndarray],
|
||||
high: Union[SupportsFloat, np.ndarray],
|
||||
shape: Optional[Sequence[int]] = None,
|
||||
dtype: type = np.float32,
|
||||
seed: Optional[int | seeding.RandomNumberGenerator] = None,
|
||||
dtype: Type = np.float32,
|
||||
seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
|
||||
):
|
||||
r"""Constructor of :class:`Box`.
|
||||
|
||||
@@ -105,7 +103,7 @@ class Box(Space[np.ndarray]):
|
||||
assert isinstance(high, np.ndarray)
|
||||
assert high.shape == shape, "high.shape doesn't match provided shape"
|
||||
|
||||
self._shape: tuple[int, ...] = shape
|
||||
self._shape: Tuple[int, ...] = shape
|
||||
|
||||
low_precision = get_precision(low.dtype)
|
||||
high_precision = get_precision(high.dtype)
|
||||
@@ -121,7 +119,7 @@ class Box(Space[np.ndarray]):
|
||||
super().__init__(self.shape, self.dtype, seed)
|
||||
|
||||
@property
|
||||
def shape(self) -> tuple[int, ...]:
|
||||
def shape(self) -> Tuple[int, ...]:
|
||||
"""Has stricter type than gym.Space - never None."""
|
||||
return self._shape
|
||||
|
||||
@@ -210,7 +208,7 @@ class Box(Space[np.ndarray]):
|
||||
"""Convert a batch of samples from this space to a JSONable data type."""
|
||||
return np.array(sample_n).tolist()
|
||||
|
||||
def from_jsonable(self, sample_n: Sequence[SupportsFloat]) -> list[np.ndarray]:
|
||||
def from_jsonable(self, sample_n: Sequence[SupportsFloat]) -> List[np.ndarray]:
|
||||
"""Convert a JSONable data type to a batch of samples from this space."""
|
||||
return [np.asarray(sample) for sample in sample_n]
|
||||
|
||||
@@ -278,7 +276,7 @@ def get_precision(dtype) -> SupportsFloat:
|
||||
def _broadcast(
|
||||
value: Union[SupportsFloat, np.ndarray],
|
||||
dtype,
|
||||
shape: tuple[int, ...],
|
||||
shape: Tuple[int, ...],
|
||||
inf_sign: str,
|
||||
) -> np.ndarray:
|
||||
"""Handle infinite bounds and broadcast at the same time if needed."""
|
||||
|
@@ -1,10 +1,8 @@
|
||||
"""Implementation of a space that represents the cartesian product of other spaces as a dictionary."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Dict as TypingDict
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -53,8 +51,8 @@ class Dict(Space[TypingDict[str, Space]], Mapping):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spaces: Optional[dict[str, Space]] = None,
|
||||
seed: Optional[dict | int | seeding.RandomNumberGenerator] = None,
|
||||
spaces: Optional[TypingDict[str, Space]] = None,
|
||||
seed: Optional[Union[dict, int, seeding.RandomNumberGenerator]] = None,
|
||||
**spaces_kwargs: Space,
|
||||
):
|
||||
"""Constructor of :class:`Dict` space.
|
||||
@@ -101,7 +99,7 @@ class Dict(Space[TypingDict[str, Space]], Mapping):
|
||||
None, None, seed # type: ignore
|
||||
) # None for shape and dtype, since it'll require special handling
|
||||
|
||||
def seed(self, seed: Optional[dict | int] = None) -> list:
|
||||
def seed(self, seed: Optional[Union[dict, int]] = None) -> list:
|
||||
"""Seed the PRNG of this space and all subspaces."""
|
||||
seeds = []
|
||||
if isinstance(seed, dict):
|
||||
@@ -188,9 +186,9 @@ class Dict(Space[TypingDict[str, Space]], Mapping):
|
||||
for key, space in self.spaces.items()
|
||||
}
|
||||
|
||||
def from_jsonable(self, sample_n: dict[str, list]) -> list:
|
||||
def from_jsonable(self, sample_n: TypingDict[str, list]) -> list:
|
||||
"""Convert a JSONable data type to a batch of samples from this space."""
|
||||
dict_of_list: dict[str, list] = {}
|
||||
dict_of_list: TypingDict[str, list] = {}
|
||||
for key, space in self.spaces.items():
|
||||
dict_of_list[key] = space.from_jsonable(sample_n[key])
|
||||
ret = []
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Implementation of a space consisting of finitely many elements."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -23,7 +21,7 @@ class Discrete(Space[int]):
|
||||
def __init__(
|
||||
self,
|
||||
n: int,
|
||||
seed: Optional[int | seeding.RandomNumberGenerator] = None,
|
||||
seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
|
||||
start: int = 0,
|
||||
):
|
||||
r"""Constructor of :class:`Discrete` space.
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Implementation of a space that consists of binary np.ndarrays of a fixed shape."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Sequence, Union
|
||||
from typing import Optional, Sequence, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -29,7 +27,7 @@ class MultiBinary(Space[np.ndarray]):
|
||||
def __init__(
|
||||
self,
|
||||
n: Union[np.ndarray, Sequence[int], int],
|
||||
seed: Optional[int | seeding.RandomNumberGenerator] = None,
|
||||
seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
|
||||
):
|
||||
"""Constructor of :class:`MultiBinary` space.
|
||||
|
||||
@@ -49,7 +47,7 @@ class MultiBinary(Space[np.ndarray]):
|
||||
super().__init__(input_n, np.int8, seed)
|
||||
|
||||
@property
|
||||
def shape(self) -> tuple[int, ...]:
|
||||
def shape(self) -> Tuple[int, ...]:
|
||||
"""Has stricter type than gym.Space - never None."""
|
||||
return self._shape # type: ignore
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Implementation of a space that represents the cartesian product of `Discrete` spaces."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Optional, Sequence, Union
|
||||
from typing import Iterable, List, Optional, Sequence, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -31,9 +29,9 @@ class MultiDiscrete(Space[np.ndarray]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nvec: Union[np.ndarray, list[int]],
|
||||
nvec: Union[np.ndarray, List[int]],
|
||||
dtype=np.int64,
|
||||
seed: Optional[int | seeding.RandomNumberGenerator] = None,
|
||||
seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
|
||||
):
|
||||
"""Constructor of :class:`MultiDiscrete` space.
|
||||
|
||||
@@ -61,7 +59,7 @@ class MultiDiscrete(Space[np.ndarray]):
|
||||
super().__init__(self.nvec.shape, dtype, seed)
|
||||
|
||||
@property
|
||||
def shape(self) -> tuple[int, ...]:
|
||||
def shape(self) -> Tuple[int, ...]:
|
||||
"""Has stricter type than :class:`gym.Space` - never None."""
|
||||
return self._shape # type: ignore
|
||||
|
||||
|
@@ -1,7 +1,17 @@
|
||||
"""Implementation of the `Space` metaclass."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Generic, Iterable, Mapping, Optional, Sequence, TypeVar
|
||||
from typing import (
|
||||
Generic,
|
||||
Iterable,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -39,8 +49,8 @@ class Space(Generic[T_cov]):
|
||||
def __init__(
|
||||
self,
|
||||
shape: Optional[Sequence[int]] = None,
|
||||
dtype: Optional[type | str | np.dtype] = None,
|
||||
seed: Optional[int | seeding.RandomNumberGenerator] = None,
|
||||
dtype: Optional[Union[Type, str, np.dtype]] = None,
|
||||
seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None,
|
||||
):
|
||||
"""Constructor of :class:`Space`.
|
||||
|
||||
@@ -67,7 +77,7 @@ class Space(Generic[T_cov]):
|
||||
return self._np_random # type: ignore ## self.seed() call guarantees right type.
|
||||
|
||||
@property
|
||||
def shape(self) -> Optional[tuple[int, ...]]:
|
||||
def shape(self) -> Optional[Tuple[int, ...]]:
|
||||
"""Return the shape of the space as an immutable property."""
|
||||
return self._shape
|
||||
|
||||
@@ -88,7 +98,7 @@ class Space(Generic[T_cov]):
|
||||
"""Return boolean specifying if x is a valid member of this space."""
|
||||
return self.contains(x)
|
||||
|
||||
def __setstate__(self, state: Iterable | Mapping):
|
||||
def __setstate__(self, state: Union[Iterable, Mapping]):
|
||||
"""Used when loading a pickled space.
|
||||
|
||||
This method was implemented explicitly to allow for loading of legacy states.
|
||||
@@ -119,7 +129,7 @@ class Space(Generic[T_cov]):
|
||||
# By default, assume identity is JSONable
|
||||
return list(sample_n)
|
||||
|
||||
def from_jsonable(self, sample_n: list) -> list[T_cov]:
|
||||
def from_jsonable(self, sample_n: list) -> List[T_cov]:
|
||||
"""Convert a JSONable data type to a batch of samples from this space."""
|
||||
# By default, assume identity is JSONable
|
||||
return sample_n
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Implementation of a space that represents the cartesian product of other spaces."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Optional, Sequence
|
||||
from typing import Iterable, List, Optional, Sequence, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -25,7 +23,7 @@ class Tuple(Space[tuple], Sequence):
|
||||
def __init__(
|
||||
self,
|
||||
spaces: Iterable[Space],
|
||||
seed: Optional[int | list[int] | seeding.RandomNumberGenerator] = None,
|
||||
seed: Optional[Union[int, List[int], seeding.RandomNumberGenerator]] = None,
|
||||
):
|
||||
r"""Constructor of :class:`Tuple` space.
|
||||
|
||||
@@ -43,7 +41,7 @@ class Tuple(Space[tuple], Sequence):
|
||||
), "Elements of the tuple must be instances of gym.Space"
|
||||
super().__init__(None, None, seed) # type: ignore
|
||||
|
||||
def seed(self, seed: Optional[int | list[int]] = None) -> list:
|
||||
def seed(self, seed: Optional[Union[int, List[int]]] = None) -> list:
|
||||
"""Seed the PRNG of this space and all subspaces."""
|
||||
seeds = []
|
||||
|
||||
|
@@ -3,8 +3,6 @@
|
||||
These functions mostly take care of flattening and unflattening elements of spaces
|
||||
to facilitate their usage in learning code.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import operator as op
|
||||
from collections import OrderedDict
|
||||
from functools import reduce, singledispatch
|
||||
@@ -142,7 +140,9 @@ def unflatten(space: Space[T], x: np.ndarray) -> T:
|
||||
|
||||
@unflatten.register(Box)
|
||||
@unflatten.register(MultiBinary)
|
||||
def _unflatten_box_multibinary(space: Box | MultiBinary, x: np.ndarray) -> np.ndarray:
|
||||
def _unflatten_box_multibinary(
|
||||
space: Union[Box, MultiBinary], x: np.ndarray
|
||||
) -> np.ndarray:
|
||||
return np.asarray(x, dtype=space.dtype).reshape(space.shape)
|
||||
|
||||
|
||||
|
@@ -1,8 +1,6 @@
|
||||
"""Utilities of visualising an environment."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import deque
|
||||
from typing import Callable, Dict, Optional, Tuple, Union
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import pygame
|
||||
@@ -35,7 +33,7 @@ class PlayableGame:
|
||||
def __init__(
|
||||
self,
|
||||
env: Env,
|
||||
keys_to_action: Optional[dict[tuple[int], int]] = None,
|
||||
keys_to_action: Optional[Dict[Tuple[int], int]] = None,
|
||||
zoom: Optional[float] = None,
|
||||
):
|
||||
"""Wraps an environment with a dictionary of keyboard buttons to action and if to zoom in on the environment.
|
||||
@@ -53,7 +51,7 @@ class PlayableGame:
|
||||
self.running = True
|
||||
|
||||
def _get_relevant_keys(
|
||||
self, keys_to_action: Optional[dict[tuple[int], int]] = None
|
||||
self, keys_to_action: Optional[Dict[Tuple[int], int]] = None
|
||||
) -> set:
|
||||
if keys_to_action is None:
|
||||
if hasattr(self.env, "get_keys_to_action"):
|
||||
@@ -68,7 +66,7 @@ class PlayableGame:
|
||||
relevant_keys = set(sum((list(k) for k in keys_to_action.keys()), []))
|
||||
return relevant_keys
|
||||
|
||||
def _get_video_size(self, zoom: Optional[float] = None) -> tuple[int, int]:
|
||||
def _get_video_size(self, zoom: Optional[float] = None) -> Tuple[int, int]:
|
||||
# TODO: this needs to be updated when the render API change goes through
|
||||
rendered = self.env.render(mode="rgb_array")
|
||||
video_size = [rendered.shape[1], rendered.shape[0]]
|
||||
@@ -103,7 +101,7 @@ class PlayableGame:
|
||||
|
||||
|
||||
def display_arr(
|
||||
screen: Surface, arr: np.ndarray, video_size: tuple[int, int], transpose: bool
|
||||
screen: Surface, arr: np.ndarray, video_size: Tuple[int, int], transpose: bool
|
||||
):
|
||||
"""Displays a numpy array on screen.
|
||||
|
||||
@@ -273,7 +271,7 @@ class PlayPlot:
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, callback: callable, horizon_timesteps: int, plot_names: list[str]
|
||||
self, callback: callable, horizon_timesteps: int, plot_names: List[str]
|
||||
):
|
||||
"""Constructor of :class:`PlayPlot`.
|
||||
|
||||
|
@@ -1,10 +1,8 @@
|
||||
"""Set of random number generator functions: seeding, generator, hashing seeds."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import struct
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -12,7 +10,7 @@ from gym import error
|
||||
from gym.logger import deprecation
|
||||
|
||||
|
||||
def np_random(seed: Optional[int] = None) -> tuple[RandomNumberGenerator, Any]:
|
||||
def np_random(seed: Optional[int] = None) -> Tuple["RandomNumberGenerator", Any]:
|
||||
"""Generates a random number generator from the seed and returns the Generator and seed.
|
||||
|
||||
Args:
|
||||
@@ -216,7 +214,7 @@ def _bigint_from_bytes(bt: bytes) -> int:
|
||||
return accum
|
||||
|
||||
|
||||
def _int_list_from_bigint(bigint: int) -> list[int]:
|
||||
def _int_list_from_bigint(bigint: int) -> List[int]:
|
||||
deprecation(
|
||||
"Function `_int_list_from_bigint` is marked as deprecated and will be removed in the future. "
|
||||
)
|
||||
@@ -226,7 +224,7 @@ def _int_list_from_bigint(bigint: int) -> list[int]:
|
||||
elif bigint == 0:
|
||||
return [0]
|
||||
|
||||
ints: list[int] = []
|
||||
ints: List[int] = []
|
||||
while bigint > 0:
|
||||
bigint, mod = divmod(bigint, 2**32)
|
||||
ints.append(mod)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Module for vector environments."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Optional, Union
|
||||
from typing import Iterable, List, Optional, Union
|
||||
|
||||
from gym.vector.async_vector_env import AsyncVectorEnv
|
||||
from gym.vector.sync_vector_env import SyncVectorEnv
|
||||
@@ -14,7 +12,7 @@ def make(
|
||||
id: str,
|
||||
num_envs: int = 1,
|
||||
asynchronous: bool = True,
|
||||
wrappers: Optional[Union[callable, list[callable]]] = None,
|
||||
wrappers: Optional[Union[callable, List[callable]]] = None,
|
||||
**kwargs,
|
||||
) -> VectorEnv:
|
||||
"""Create a vectorized environment from multiple copies of an environment, from its id.
|
||||
|
@@ -1,12 +1,10 @@
|
||||
"""An async vector environment."""
|
||||
from __future__ import annotations
|
||||
|
||||
import multiprocessing as mp
|
||||
import sys
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from enum import Enum
|
||||
from typing import Optional, Sequence, Union
|
||||
from typing import List, Optional, Sequence, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -199,7 +197,7 @@ class AsyncVectorEnv(VectorEnv):
|
||||
|
||||
def reset_async(
|
||||
self,
|
||||
seed: Optional[Union[int, list[int]]] = None,
|
||||
seed: Optional[Union[int, List[int]]] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
):
|
||||
@@ -250,7 +248,7 @@ class AsyncVectorEnv(VectorEnv):
|
||||
seed: Optional[int] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
) -> Union[ObsType, tuple[ObsType, list[dict]]]:
|
||||
) -> Union[ObsType, Tuple[ObsType, List[dict]]]:
|
||||
"""Waits for the calls triggered by :meth:`reset_async` to finish and returns the results.
|
||||
|
||||
Args:
|
||||
@@ -333,7 +331,7 @@ class AsyncVectorEnv(VectorEnv):
|
||||
|
||||
def step_wait(
|
||||
self, timeout: Optional[Union[int, float]] = None
|
||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray, list[dict]]:
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, List[dict]]:
|
||||
"""Wait for the calls to :obj:`step` in each sub-environment to finish.
|
||||
|
||||
Args:
|
||||
|
@@ -1,8 +1,6 @@
|
||||
"""A synchronous vector environment."""
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any, Iterator, Optional, Sequence, Union
|
||||
from typing import Any, Iterator, List, Optional, Sequence, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -89,7 +87,7 @@ class SyncVectorEnv(VectorEnv):
|
||||
|
||||
def reset_wait(
|
||||
self,
|
||||
seed: Optional[Union[int, list[int]]] = None,
|
||||
seed: Optional[Union[int, List[int]]] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
):
|
||||
|
@@ -1,7 +1,5 @@
|
||||
"""Base class for vectorized environments."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -50,7 +48,7 @@ class VectorEnv(gym.Env):
|
||||
|
||||
def reset_async(
|
||||
self,
|
||||
seed: Optional[Union[int, list[int]]] = None,
|
||||
seed: Optional[Union[int, List[int]]] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
):
|
||||
@@ -68,7 +66,7 @@ class VectorEnv(gym.Env):
|
||||
|
||||
def reset_wait(
|
||||
self,
|
||||
seed: Optional[Union[int, list[int]]] = None,
|
||||
seed: Optional[Union[int, List[int]]] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
):
|
||||
@@ -91,7 +89,7 @@ class VectorEnv(gym.Env):
|
||||
def reset(
|
||||
self,
|
||||
*,
|
||||
seed: Optional[Union[int, list[int]]] = None,
|
||||
seed: Optional[Union[int, List[int]]] = None,
|
||||
return_info: bool = False,
|
||||
options: Optional[dict] = None,
|
||||
):
|
||||
@@ -144,10 +142,10 @@ class VectorEnv(gym.Env):
|
||||
def call_async(self, name, *args, **kwargs):
|
||||
"""Calls a method name for each parallel environment asynchronously."""
|
||||
|
||||
def call_wait(self, **kwargs) -> list[Any]:
|
||||
def call_wait(self, **kwargs) -> List[Any]:
|
||||
"""After calling a method in :meth:`call_async`, this function collects the results."""
|
||||
|
||||
def call(self, name: str, *args, **kwargs) -> list[Any]:
|
||||
def call(self, name: str, *args, **kwargs) -> List[Any]:
|
||||
"""Call a method, or get a property, from each parallel environment.
|
||||
|
||||
Args:
|
||||
|
@@ -1,6 +1,4 @@
|
||||
"""A wrapper for video recording environments by rolling it out, frame by frame."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
@@ -9,7 +7,7 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from io import StringIO
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -362,7 +360,7 @@ class ImageEncoder:
|
||||
def __init__(
|
||||
self,
|
||||
output_path: str,
|
||||
frame_shape: tuple[int, int, int],
|
||||
frame_shape: Tuple[int, int, int],
|
||||
frames_per_sec: int,
|
||||
output_frames_per_sec: int,
|
||||
):
|
||||
|
@@ -1,10 +1,8 @@
|
||||
"""Wrapper for augmenting observations by pixel values."""
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import copy
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -52,8 +50,8 @@ class PixelObservationWrapper(gym.ObservationWrapper):
|
||||
self,
|
||||
env: gym.Env,
|
||||
pixels_only: bool = True,
|
||||
render_kwargs: Optional[dict[str, dict[str, Any]]] = None,
|
||||
pixel_keys: tuple[str, ...] = ("pixels",),
|
||||
render_kwargs: Optional[Dict[str, Dict[str, Any]]] = None,
|
||||
pixel_keys: Tuple[str, ...] = ("pixels",),
|
||||
):
|
||||
"""Initializes a new pixel Wrapper.
|
||||
|
||||
|
@@ -14,6 +14,7 @@ ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/.mujoco/mujoco210/bin
|
||||
COPY . /usr/local/gym/
|
||||
WORKDIR /usr/local/gym/
|
||||
|
||||
RUN pip install .[noatari] && pip install -r test_requirements.txt
|
||||
RUN if [ python:$PYTHON_VERSION = "python:3.6.15" ] ; then pip install .[box2d,classic_control,toy_text,other] ; else pip install .[noatari] ; fi
|
||||
RUN pip install -r test_requirements.txt
|
||||
|
||||
ENTRYPOINT ["/usr/local/gym/bin/docker_entrypoint"]
|
||||
|
@@ -25,7 +25,7 @@ strict = [
|
||||
]
|
||||
|
||||
typeCheckingMode = "basic"
|
||||
pythonVersion = "3.7"
|
||||
pythonVersion = "3.6"
|
||||
typeshedPath = "typeshed"
|
||||
enableTypeIgnoreComments = true
|
||||
|
||||
|
6
setup.py
6
setup.py
@@ -55,8 +55,9 @@ setup(
|
||||
install_requires=[
|
||||
"numpy>=1.18.0",
|
||||
"cloudpickle>=1.2.0",
|
||||
"importlib_metadata>=4.10.0; python_version < '3.10'",
|
||||
"importlib_metadata>=4.8.0; python_version < '3.10'",
|
||||
"gym_notices>=0.0.4",
|
||||
"dataclasses==0.8; python_version == '3.6'",
|
||||
],
|
||||
extras_require=extras,
|
||||
package_data={
|
||||
@@ -69,9 +70,10 @@ setup(
|
||||
]
|
||||
},
|
||||
tests_require=["pytest", "mock"],
|
||||
python_requires=">=3.7",
|
||||
python_requires=">=3.6",
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
|
@@ -1,3 +1,2 @@
|
||||
lz4~=3.1
|
||||
pytest~=6.2
|
||||
pytest-forked~=1.3
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from gym import envs, logger
|
||||
|
||||
SKIP_MUJOCO_V3_WARNING_MESSAGE = (
|
||||
"Cannot run mujoco test because mujoco-py is not installed"
|
||||
"Cannot run mujoco test because `mujoco-py` is not installed"
|
||||
)
|
||||
SKIP_MUJOCO_V4_WARNING_MESSAGE = (
|
||||
"Cannot run mujoco test because `mujoco` is not installed"
|
||||
)
|
||||
|
||||
skip_mujoco_v3 = False
|
||||
@@ -10,13 +13,19 @@ try:
|
||||
except ImportError:
|
||||
skip_mujoco_v3 = True
|
||||
|
||||
skip_mujoco_v4 = False
|
||||
try:
|
||||
import mujoco # noqa:F401
|
||||
except ImportError:
|
||||
skip_mujoco_v4 = True
|
||||
|
||||
|
||||
def should_skip_env_spec_for_tests(spec):
|
||||
# We skip tests for envs that require dependencies or are otherwise
|
||||
# troublesome to run frequently
|
||||
ep = spec.entry_point
|
||||
# Skip mujoco tests for pull request CI
|
||||
if skip_mujoco_v3 and ep.startswith("gym.envs.mujoco"):
|
||||
if (skip_mujoco_v3 or skip_mujoco_v4) and ep.startswith("gym.envs.mujoco"):
|
||||
return True
|
||||
try:
|
||||
import gym.envs.atari # noqa:F401
|
||||
|
@@ -3,11 +3,11 @@ from typing import List
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from gym import envs
|
||||
import gym
|
||||
from gym import Env
|
||||
from gym.envs.registration import EnvSpec
|
||||
from gym.spaces.box import Box
|
||||
from gym.spaces.discrete import Discrete
|
||||
from gym.spaces.space import Space
|
||||
from tests.envs.spec_list import (
|
||||
SKIP_MUJOCO_V3_WARNING_MESSAGE,
|
||||
skip_mujoco_v3,
|
||||
@@ -17,17 +17,20 @@ from tests.envs.spec_list import (
|
||||
ENVIRONMENT_IDS = ("HalfCheetah-v2",)
|
||||
|
||||
|
||||
def make_envs_by_action_space_type(spec_list: List[EnvSpec], action_space: Space):
|
||||
def filters_envs_action_space_type(
|
||||
env_spec_list: List[EnvSpec], action_space: type
|
||||
) -> List[Env]:
|
||||
"""Make environments of specific action_space type.
|
||||
This function returns a filtered list of environment from the
|
||||
spec_list that matches the action_space type.
|
||||
|
||||
This function returns a filtered list of environment from the spec_list that matches the action_space type.
|
||||
|
||||
Args:
|
||||
spec_list (list): list of registered environments' specification
|
||||
env_spec_list (list): list of registered environments' specification
|
||||
action_space (gym.spaces.Space): action_space type
|
||||
"""
|
||||
filtered_envs = []
|
||||
for spec in spec_list:
|
||||
env = envs.make(spec.id)
|
||||
for spec in env_spec_list:
|
||||
env = gym.make(spec.id)
|
||||
if isinstance(env.action_space, action_space):
|
||||
filtered_envs.append(env)
|
||||
return filtered_envs
|
||||
@@ -36,7 +39,7 @@ def make_envs_by_action_space_type(spec_list: List[EnvSpec], action_space: Space
|
||||
@pytest.mark.skipif(skip_mujoco_v3, reason=SKIP_MUJOCO_V3_WARNING_MESSAGE)
|
||||
@pytest.mark.parametrize("environment_id", ENVIRONMENT_IDS)
|
||||
def test_serialize_deserialize(environment_id):
|
||||
env = envs.make(environment_id)
|
||||
env = gym.make(environment_id)
|
||||
env.reset()
|
||||
|
||||
with pytest.raises(ValueError, match="Action dimension mismatch"):
|
||||
@@ -46,7 +49,7 @@ def test_serialize_deserialize(environment_id):
|
||||
env.step(0.1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("env", make_envs_by_action_space_type(spec_list, Discrete))
|
||||
@pytest.mark.parametrize("env", filters_envs_action_space_type(spec_list, Discrete))
|
||||
def test_discrete_actions_out_of_bound(env):
|
||||
"""Test out of bound actions in Discrete action_space.
|
||||
In discrete action_space environments, `out-of-bound`
|
||||
@@ -65,7 +68,7 @@ def test_discrete_actions_out_of_bound(env):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("env", "seed"),
|
||||
[(env, 42) for env in make_envs_by_action_space_type(spec_list, Box)],
|
||||
[(env, 42) for env in filters_envs_action_space_type(spec_list, Box)],
|
||||
)
|
||||
def test_box_actions_out_of_bound(env, seed):
|
||||
"""Test out of bound actions in Box action_space.
|
||||
@@ -80,7 +83,7 @@ def test_box_actions_out_of_bound(env, seed):
|
||||
|
||||
env.reset(seed=seed)
|
||||
|
||||
oob_env = envs.make(env.spec.id)
|
||||
oob_env = gym.make(env.spec.id)
|
||||
oob_env.reset(seed=seed)
|
||||
|
||||
dtype = env.action_space.dtype
|
||||
|
Reference in New Issue
Block a user