From 02a52bda285610a77d4ec117c9cbc52e03e90b2f Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Tue, 24 May 2022 22:37:32 +0100 Subject: [PATCH 01/14] Add support for python 3.6 --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ff4b817be6a..9466c4b8afd 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ 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", ], extras_require=extras, @@ -69,9 +69,10 @@ ] }, 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", From e99c220dc947f522f261c9541e304ee97a1cd424 Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Tue, 24 May 2022 22:43:23 +0100 Subject: [PATCH 02/14] Add support for python 3.6 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c376ad0c26..b670b2c764c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: | From 46b3a1ca9511060d8668e6149f4367c60c646798 Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Tue, 24 May 2022 23:20:17 +0100 Subject: [PATCH 03/14] Added check for python 3.6 to not install mujoco as no version exists --- py.Dockerfile | 4 +++- test_requirements.txt | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/py.Dockerfile b/py.Dockerfile index ce20b8430b9..c74b4e95699 100644 --- a/py.Dockerfile +++ b/py.Dockerfile @@ -14,6 +14,8 @@ 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 == 3.6 ]] ; \ + then pip install .[nomujoco] && pip install -r test_requirements.txt ; \ + else pip install .[noatari] && pip install -r test_requirements.txt ; fi ENTRYPOINT ["/usr/local/gym/bin/docker_entrypoint"] diff --git a/test_requirements.txt b/test_requirements.txt index 7f59ce32d86..9c7e6956d41 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,3 +1,2 @@ lz4~=3.1 pytest~=6.2 -pytest-forked~=1.3 From 7869668e45953743f56c2774bd60199b0ae64de4 Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Tue, 24 May 2022 23:32:02 +0100 Subject: [PATCH 04/14] Fixed the install groups for python 3.6 --- py.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py.Dockerfile b/py.Dockerfile index c74b4e95699..54a491c1e0f 100644 --- a/py.Dockerfile +++ b/py.Dockerfile @@ -15,7 +15,7 @@ COPY . /usr/local/gym/ WORKDIR /usr/local/gym/ RUN if [[ python:$PYTHON_VERSION == 3.6 ]] ; \ - then pip install .[nomujoco] && pip install -r test_requirements.txt ; \ + then pip install .[box2d,classic_control,toy_text,other] && pip install -r test_requirements.txt ; \ else pip install .[noatari] && pip install -r test_requirements.txt ; fi ENTRYPOINT ["/usr/local/gym/bin/docker_entrypoint"] From dad339b20a34f1592074ea59ff032563a6934628 Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Wed, 25 May 2022 09:55:08 +0100 Subject: [PATCH 05/14] Re-added python 3.6 support for gym --- .pre-commit-config.yaml | 2 +- gym/core.py | 28 +++----- gym/envs/registration.py | 79 +++++++++++------------ gym/spaces/box.py | 10 ++- gym/spaces/dict.py | 14 ++-- gym/spaces/discrete.py | 6 +- gym/spaces/multi_binary.py | 8 +-- gym/spaces/multi_discrete.py | 10 ++- gym/spaces/space.py | 24 +++++-- gym/spaces/tuple.py | 8 +-- gym/spaces/utils.py | 6 +- gym/utils/play.py | 14 ++-- gym/utils/seeding.py | 10 ++- gym/vector/__init__.py | 6 +- gym/vector/async_vector_env.py | 10 ++- gym/vector/sync_vector_env.py | 6 +- gym/vector/vector_env.py | 12 ++-- gym/wrappers/monitoring/video_recorder.py | 6 +- gym/wrappers/pixel_observation.py | 8 +-- py.Dockerfile | 5 +- pyproject.toml | 2 +- tests/envs/spec_list.py | 13 +++- tests/envs/test_action_dim_check.py | 25 +++---- tests/utils/test_play.py | 5 +- 24 files changed, 148 insertions(+), 169 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89a02ac19f8..1359be7a9d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/gym/core.py b/gym/core.py index f68dab16607..c9e21036175 100644 --- a/gym/core.py +++ b/gym/core.py @@ -1,8 +1,5 @@ """Core API for Environment, Wrapper, ActionWrapper, RewardWrapper and ObservationWrapper.""" -from __future__ import annotations - -from abc import abstractmethod -from typing import Generic, Optional, SupportsFloat, TypeVar, Union +from typing import Generic, Optional, SupportsFloat, Tuple, TypeVar, Union from gym import spaces from gym.logger import deprecation @@ -63,8 +60,7 @@ def np_random(self) -> RandomNumberGenerator: def np_random(self, value: RandomNumberGenerator): self._np_random = value - @abstractmethod - 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. @@ -88,14 +84,13 @@ def step(self, action: ActType) -> tuple[ObsType, float, bool, dict]: """ raise NotImplementedError - @abstractmethod def reset( self, *, 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 @@ -129,7 +124,6 @@ def reset( if seed is not None: self._np_random, seed = seeding.np_random(seed) - @abstractmethod def render(self, mode="human"): """Renders the environment. @@ -204,7 +198,7 @@ def seed(self, seed=None): return [seed] @property - def unwrapped(self) -> Env: + def unwrapped(self) -> "Env": """Returns the base non-wrapped environment. Returns: @@ -251,7 +245,7 @@ def __init__(self, env: Env): 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): @@ -293,14 +287,14 @@ def observation_space(self, space: spaces.Space): 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 @@ -314,11 +308,11 @@ def metadata(self) -> dict: 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) @@ -389,7 +383,6 @@ def step(self, action): observation, reward, done, info = self.env.step(action) return self.observation(observation), reward, done, info - @abstractmethod def observation(self, observation): """Returns a modified observation.""" raise NotImplementedError @@ -424,7 +417,6 @@ def step(self, action): observation, reward, done, info = self.env.step(action) return observation, self.reward(reward), done, info - @abstractmethod def reward(self, reward): """Returns a modified ``reward``.""" raise NotImplementedError @@ -466,12 +458,10 @@ def step(self, action): """Runs the environment :meth:`env.step` using the modified ``action`` from :meth:`self.action`.""" return self.env.step(self.action(action)) - @abstractmethod def action(self, action): """Returns a modified action before :meth:`env.step` is called.""" raise NotImplementedError - @abstractmethod def reverse_action(self, action): """Returns a reversed ``action``.""" raise NotImplementedError diff --git a/gym/envs/registration.py b/gym/envs/registration.py index c6a9b062221..4a1a1e50c34 100644 --- a/gym/envs/registration.py +++ b/gym/envs/registration.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import contextlib import copy import difflib @@ -8,11 +6,11 @@ import re import sys import warnings -from dataclasses import dataclass, field from typing import ( - Any, Callable, + Dict, Iterable, + List, Optional, Sequence, SupportsFloat, @@ -36,15 +34,12 @@ 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[\w:-]+)\/)?(?:(?P[\w:.-]+?))(?:-v(?P\d+))?$" ) @@ -90,23 +85,27 @@ def get_env_id(ns: Optional[str], name: str, version: Optional[int]): return full_name -@dataclass class EnvSpec: - id: str - entry_point: Optional[Union[Callable, str]] = field(default=None) - reward_threshold: Optional[float] = field(default=None) - nondeterministic: bool = field(default=False) - max_episode_steps: Optional[int] = field(default=None) - order_enforce: bool = field(default=True) - autoreset: bool = field(default=False) - kwargs: dict = field(default_factory=dict) - - namespace: Optional[str] = field(init=False) - name: str = field(init=False) - version: Optional[int] = field(init=False) - - def __post_init__(self): - # Initialize namespace, name, version + def __init__( + self, + id: str, + entry_point: Optional[Union[Callable, str]] = None, + reward_threshold: Optional[float] = None, + nondeterministic: bool = False, + max_episode_steps: Optional[int] = None, + order_enforce: bool = True, + autoreset: bool = False, + kwargs: dict = None, + ): + self.id = id + self.entry_point = entry_point + self.reward_threshold = reward_threshold + self.nondeterministic = nondeterministic + self.max_episode_steps = max_episode_steps + self.order_enforce = order_enforce + self.autoreset = autoreset + self.kwargs = {} if kwargs is None else kwargs + self.namespace, self.name, self.version = parse_env_id(self.id) def make(self, **kwargs) -> Env: @@ -215,7 +214,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 @@ -276,39 +275,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 # ---------------------------------------- @@ -388,7 +387,7 @@ def env_specs(self): # 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 @@ -492,7 +491,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, diff --git a/gym/spaces/box.py b/gym/spaces/box.py index 53169bfa118..971cd3b1a58 100644 --- a/gym/spaces/box.py +++ b/gym/spaces/box.py @@ -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, Tuple, Type, Union +from typing import List, Optional, Sequence, SupportsFloat, Tuple, Type, Union import numpy as np @@ -47,7 +45,7 @@ def __init__( high: Union[SupportsFloat, np.ndarray], shape: Optional[Sequence[int]] = None, dtype: Type = np.float32, - seed: Optional[int | seeding.RandomNumberGenerator] = None, + seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None, ): r"""Constructor of :class:`Box`. @@ -195,7 +193,7 @@ def to_jsonable(self, sample_n): """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] @@ -253,7 +251,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.""" diff --git a/gym/spaces/dict.py b/gym/spaces/dict.py index 40cb69f683d..ad5c2ff8b40 100644 --- a/gym/spaces/dict.py +++ b/gym/spaces/dict.py @@ -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 @@ def __init__( 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): @@ -189,9 +187,9 @@ def to_jsonable(self, sample_n: list) -> dict: 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 = [] diff --git a/gym/spaces/discrete.py b/gym/spaces/discrete.py index ff645be5503..de87ed9fd32 100644 --- a/gym/spaces/discrete.py +++ b/gym/spaces/discrete.py @@ -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. diff --git a/gym/spaces/multi_binary.py b/gym/spaces/multi_binary.py index 36abef52b53..9b04f6b77f2 100644 --- a/gym/spaces/multi_binary.py +++ b/gym/spaces/multi_binary.py @@ -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 @@ def __init__( 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 diff --git a/gym/spaces/multi_discrete.py b/gym/spaces/multi_discrete.py index 8ebca413a08..cb43420157b 100644 --- a/gym/spaces/multi_discrete.py +++ b/gym/spaces/multi_discrete.py @@ -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 @@ def __init__( 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 diff --git a/gym/spaces/space.py b/gym/spaces/space.py index 57602c29a76..f8a7727dce5 100644 --- a/gym/spaces/space.py +++ b/gym/spaces/space.py @@ -1,7 +1,17 @@ """Implementation of the `Space` metaclass.""" -from __future__ import annotations -from typing import Generic, Iterable, Mapping, Optional, Sequence, Type, TypeVar +from typing import ( + Generic, + Iterable, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, +) import numpy as np @@ -37,8 +47,8 @@ class Space(Generic[T_cov]): def __init__( self, shape: Optional[Sequence[int]] = None, - dtype: Optional[Type | str] = None, - seed: Optional[int | seeding.RandomNumberGenerator] = None, + dtype: Optional[Union[Type, str]] = None, + seed: Optional[Union[int, seeding.RandomNumberGenerator]] = None, ): """Constructor of :class:`Space`. @@ -65,7 +75,7 @@ def np_random(self) -> seeding.RandomNumberGenerator: 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 @@ -86,7 +96,7 @@ def __contains__(self, x) -> bool: """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. @@ -114,7 +124,7 @@ def to_jsonable(self, sample_n: Sequence[T_cov]) -> list: # 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 diff --git a/gym/spaces/tuple.py b/gym/spaces/tuple.py index 7adbd36cb7e..6715575aec4 100644 --- a/gym/spaces/tuple.py +++ b/gym/spaces/tuple.py @@ -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 @@ def __init__( ), "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 = [] diff --git a/gym/spaces/utils.py b/gym/spaces/utils.py index f7a238b3aed..ffd1f1f5574 100644 --- a/gym/spaces/utils.py +++ b/gym/spaces/utils.py @@ -2,8 +2,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 @@ -125,7 +123,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) diff --git a/gym/utils/play.py b/gym/utils/play.py index d58fc15f21f..e54cd660e2b 100644 --- a/gym/utils/play.py +++ b/gym/utils/play.py @@ -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 @@ def __init__( 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 @@ def _get_relevant_keys( 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]] @@ -102,7 +100,7 @@ def process_event(self, event: Event): 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. @@ -271,7 +269,7 @@ def compute_metrics(obs_t, obs_tp, action, reward, done, info): """ 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`. diff --git a/gym/utils/seeding.py b/gym/utils/seeding.py index a6ee786d4aa..d88c8f876e2 100644 --- a/gym/utils/seeding.py +++ b/gym/utils/seeding.py @@ -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.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: @@ -210,7 +208,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. " ) @@ -220,7 +218,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) diff --git a/gym/vector/__init__.py b/gym/vector/__init__.py index 45b0be061c2..5aa5634bd1e 100644 --- a/gym/vector/__init__.py +++ b/gym/vector/__init__.py @@ -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. diff --git a/gym/vector/async_vector_env.py b/gym/vector/async_vector_env.py index 998c0123f6b..fe751330fb2 100644 --- a/gym/vector/async_vector_env.py +++ b/gym/vector/async_vector_env.py @@ -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 @@ -185,7 +183,7 @@ def seed(self, seed=None): 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, ): @@ -236,7 +234,7 @@ def reset_wait( 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: @@ -319,7 +317,7 @@ def step_async(self, actions: np.ndarray): 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: diff --git a/gym/vector/sync_vector_env.py b/gym/vector/sync_vector_env.py index 5e9b44a4662..9e9a851e0c6 100644 --- a/gym/vector/sync_vector_env.py +++ b/gym/vector/sync_vector_env.py @@ -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 @@ -86,7 +84,7 @@ def seed(self, seed: Optional[Union[int, Sequence[int]]] = None): 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, ): diff --git a/gym/vector/vector_env.py b/gym/vector/vector_env.py index ad2da0ab02e..2b17218d1fb 100644 --- a/gym/vector/vector_env.py +++ b/gym/vector/vector_env.py @@ -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 @@ def __init__( 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, ): @@ -62,7 +60,7 @@ def reset_async( 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, ): @@ -75,7 +73,7 @@ def reset_wait( 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, ): @@ -126,7 +124,7 @@ def call_wait(self, **kwargs): """After calling a method in :meth:`call_async`, this function collects the results.""" raise NotImplementedError() - 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: diff --git a/gym/wrappers/monitoring/video_recorder.py b/gym/wrappers/monitoring/video_recorder.py index 44c34530ca8..1be8f9e0ce1 100644 --- a/gym/wrappers/monitoring/video_recorder.py +++ b/gym/wrappers/monitoring/video_recorder.py @@ -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 subprocess import tempfile from io import StringIO -from typing import Optional, Union +from typing import Optional, Tuple, Union import numpy as np @@ -355,7 +353,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, ): diff --git a/gym/wrappers/pixel_observation.py b/gym/wrappers/pixel_observation.py index a6053a3bfdb..b38cc7fe4a2 100644 --- a/gym/wrappers/pixel_observation.py +++ b/gym/wrappers/pixel_observation.py @@ -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 @@ def __init__( 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. diff --git a/py.Dockerfile b/py.Dockerfile index 54a491c1e0f..882fd34e0d7 100644 --- a/py.Dockerfile +++ b/py.Dockerfile @@ -14,8 +14,7 @@ ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/.mujoco/mujoco210/bin COPY . /usr/local/gym/ WORKDIR /usr/local/gym/ -RUN if [[ python:$PYTHON_VERSION == 3.6 ]] ; \ - then pip install .[box2d,classic_control,toy_text,other] && pip install -r test_requirements.txt ; \ - else pip install .[noatari] && pip install -r test_requirements.txt ; fi +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"] diff --git a/pyproject.toml b/pyproject.toml index 1522621bf32..b886e94b071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ strict = [ ] typeCheckingMode = "basic" -pythonVersion = "3.7" +pythonVersion = "3.6" typeshedPath = "typeshed" enableTypeIgnoreComments = true diff --git a/tests/envs/spec_list.py b/tests/envs/spec_list.py index f7a1a2777be..b96a57ead49 100644 --- a/tests/envs/spec_list.py +++ b/tests/envs/spec_list.py @@ -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 @@ 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 diff --git a/tests/envs/test_action_dim_check.py b/tests/envs/test_action_dim_check.py index 8289673c47e..20a3079ed2f 100644 --- a/tests/envs/test_action_dim_check.py +++ b/tests/envs/test_action_dim_check.py @@ -3,11 +3,11 @@ 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,18 @@ 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 +37,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 +47,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 +66,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 +81,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 diff --git a/tests/utils/test_play.py b/tests/utils/test_play.py index 02acce160de..0aa45c939f5 100644 --- a/tests/utils/test_play.py +++ b/tests/utils/test_play.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass from typing import Callable import numpy as np @@ -15,9 +14,9 @@ IRRELEVANT_KEY = 1 -@dataclass class DummyEnvSpec: - id: str + def __init__(self, id: str): + self.id = id class DummyPlayEnv(gym.Env): From 6d5f702b2d4f8d8e5dbb56fbd01564a1cef6d35e Mon Sep 17 00:00:00 2001 From: pseudo-rnd-thoughts Date: Wed, 25 May 2022 10:11:41 +0100 Subject: [PATCH 06/14] black --- tests/envs/test_action_dim_check.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/envs/test_action_dim_check.py b/tests/envs/test_action_dim_check.py index 20a3079ed2f..6b5032258e1 100644 --- a/tests/envs/test_action_dim_check.py +++ b/tests/envs/test_action_dim_check.py @@ -17,7 +17,9 @@ ENVIRONMENT_IDS = ("HalfCheetah-v2",) -def filters_envs_action_space_type(env_spec_list: List[EnvSpec], action_space: type) -> List[Env]: +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. From 6260f977d19207ac8167c4fb9a05aec0104ee512 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 13:00:45 +0100 Subject: [PATCH 07/14] Added support for dataclasses through dataclasses module in setup that backports the module --- gym/envs/registration.py | 37 +++++++++++++++++-------------------- setup.py | 1 + 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/gym/envs/registration.py b/gym/envs/registration.py index 4a1a1e50c34..c40ad16a5f6 100644 --- a/gym/envs/registration.py +++ b/gym/envs/registration.py @@ -6,6 +6,7 @@ import re import sys import warnings +from dataclasses import dataclass, field from typing import ( Callable, Dict, @@ -85,27 +86,23 @@ def get_env_id(ns: Optional[str], name: str, version: Optional[int]): return full_name +@dataclass class EnvSpec: - def __init__( - self, - id: str, - entry_point: Optional[Union[Callable, str]] = None, - reward_threshold: Optional[float] = None, - nondeterministic: bool = False, - max_episode_steps: Optional[int] = None, - order_enforce: bool = True, - autoreset: bool = False, - kwargs: dict = None, - ): - self.id = id - self.entry_point = entry_point - self.reward_threshold = reward_threshold - self.nondeterministic = nondeterministic - self.max_episode_steps = max_episode_steps - self.order_enforce = order_enforce - self.autoreset = autoreset - self.kwargs = {} if kwargs is None else kwargs - + id: str + entry_point: Optional[Union[Callable, str]] = field(default=None) + reward_threshold: Optional[float] = field(default=None) + nondeterministic: bool = field(default=False) + max_episode_steps: Optional[int] = field(default=None) + order_enforce: bool = field(default=True) + autoreset: bool = field(default=False) + kwargs: dict = field(default_factory=dict) + + namespace: Optional[str] = field(init=False) + name: str = field(init=False) + version: Optional[int] = field(init=False) + + def __post_init__(self): + # Initialize namespace, name, version self.namespace, self.name, self.version = parse_env_id(self.id) def make(self, **kwargs) -> Env: diff --git a/setup.py b/setup.py index 9f4abdcf1ba..b3ac1bad79f 100644 --- a/setup.py +++ b/setup.py @@ -57,6 +57,7 @@ "cloudpickle>=1.2.0", "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={ From 1941db8c3650cccbe3eead651f75c0f356e43bc5 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 13:30:20 +0100 Subject: [PATCH 08/14] Fixed install requirements --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b3ac1bad79f..ea1592b8bd0 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ "cloudpickle>=1.2.0", "importlib_metadata>=4.8.0; python_version < '3.10'", "gym_notices>=0.0.4", - "dataclasses==0.8; python_version = '3.6'", + "dataclasses==0.8; python_version == '3.6'", ], extras_require=extras, package_data={ From 51123240a03cd6e96e16e3765bbba2224bc30d7d Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 14:43:44 +0100 Subject: [PATCH 09/14] Re-added dummy env spec with dataclasses --- tests/utils/test_play.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/utils/test_play.py b/tests/utils/test_play.py index 0aa45c939f5..02acce160de 100644 --- a/tests/utils/test_play.py +++ b/tests/utils/test_play.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from typing import Callable import numpy as np @@ -14,9 +15,9 @@ IRRELEVANT_KEY = 1 +@dataclass class DummyEnvSpec: - def __init__(self, id: str): - self.id = id + id: str class DummyPlayEnv(gym.Env): From 66ec6e90eaf8acfb8c8fbe606fe8e10b3f3fa517 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 15:00:28 +0100 Subject: [PATCH 10/14] Changed type for compatability for python 3.6 --- gym/envs/registration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym/envs/registration.py b/gym/envs/registration.py index 7dfd35950dd..506960898d3 100644 --- a/gym/envs/registration.py +++ b/gym/envs/registration.py @@ -15,6 +15,7 @@ Optional, Sequence, SupportsFloat, + Tuple, Union, overload, ) @@ -35,7 +36,6 @@ else: from typing_extensions import Literal - from gym import Env, error, logger ENV_ID_RE = re.compile( @@ -50,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. From 437316396d59424947eb79d9a55e7490d91d3980 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 15:05:42 +0100 Subject: [PATCH 11/14] Added a python 3.6 warning --- gym/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gym/core.py b/gym/core.py index 1a51fd506f9..1356aebf8fe 100644 --- a/gym/core.py +++ b/gym/core.py @@ -1,11 +1,17 @@ """Core API for Environment, Wrapper, ActionWrapper, RewardWrapper and ObservationWrapper.""" +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") From b8279b2c6677d628a3f0f4c9c40fa5d325db9170 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 15:09:35 +0100 Subject: [PATCH 12/14] Fixed python 3.6 typing issue --- gym/vector/vector_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym/vector/vector_env.py b/gym/vector/vector_env.py index 7ea804106d0..2cb9ee8042d 100644 --- a/gym/vector/vector_env.py +++ b/gym/vector/vector_env.py @@ -142,7 +142,7 @@ def step(self, actions): 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]: From 1517a4a67473eb4ac37aaa0f8ff60d87be9c6c08 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 15:13:50 +0100 Subject: [PATCH 13/14] Removed __future__ import annotation for python 3.6 support --- gym/envs/toy_text/frozen_lake.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gym/envs/toy_text/frozen_lake.py b/gym/envs/toy_text/frozen_lake.py index 512ff781dec..4ccf9da5182 100644 --- a/gym/envs/toy_text/frozen_lake.py +++ b/gym/envs/toy_text/frozen_lake.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from contextlib import closing from io import StringIO from os import path From 430e9357c28af73b18c4aaad4ac8407d2d32ed20 Mon Sep 17 00:00:00 2001 From: StringTheory Date: Wed, 25 May 2022 15:23:40 +0100 Subject: [PATCH 14/14] Fixed python 3.6 typing --- gym/envs/toy_text/frozen_lake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym/envs/toy_text/frozen_lake.py b/gym/envs/toy_text/frozen_lake.py index 4ccf9da5182..1c87dfa5e3c 100644 --- a/gym/envs/toy_text/frozen_lake.py +++ b/gym/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 Optional +from typing import List, Optional import numpy as np @@ -29,7 +29,7 @@ } -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: