Skip to content

Commit 7ddce8c

Browse files
Add deprecation warnings for features removed in Gymnasium v1.0 (#535)
1 parent f46c988 commit 7ddce8c

11 files changed

+106
-21
lines changed

Diff for: gymnasium/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
registry,
1717
pprint_registry,
1818
make_vec,
19+
register_envs,
1920
)
2021

2122
# necessary for `envs.__init__` which registers all gymnasium environments and loads plugins
@@ -39,6 +40,7 @@
3940
"register",
4041
"registry",
4142
"pprint_registry",
43+
"register_envs",
4244
# module folders
4345
"envs",
4446
"experimental",

Diff for: gymnasium/core.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import numpy as np
88

9-
from gymnasium import spaces
9+
from gymnasium import logger, spaces
1010
from gymnasium.utils import RecordConstructorArgs, seeding
1111

1212

@@ -234,6 +234,10 @@ def __exit__(self, *args: Any):
234234
# propagate exception
235235
return False
236236

237+
def get_wrapper_attr(self, name: str) -> Any:
238+
"""Gets the attribute `name` from the environment."""
239+
return getattr(self, name)
240+
237241

238242
WrapperObsType = TypeVar("WrapperObsType")
239243
WrapperActType = TypeVar("WrapperActType")
@@ -273,15 +277,48 @@ def __init__(self, env: Env[ObsType, ActType]):
273277
self._cached_spec: EnvSpec | None = None
274278

275279
def __getattr__(self, name: str) -> Any:
276-
"""Returns an attribute with ``name``, unless ``name`` starts with an underscore."""
280+
"""Returns an attribute with ``name``, unless ``name`` starts with an underscore.
281+
282+
Args:
283+
name: The variable name
284+
285+
Returns:
286+
The value of the variable in the wrapper stack
287+
288+
Warnings:
289+
This feature is deprecated and removed in v1.0 and replaced with `env.get_attr(name})`
290+
"""
277291
if name == "_np_random":
278292
raise AttributeError(
279293
"Can't access `_np_random` of a wrapper, use `self.unwrapped._np_random` or `self.np_random`."
280294
)
281295
elif name.startswith("_"):
282296
raise AttributeError(f"accessing private attribute '{name}' is prohibited")
297+
logger.warn(
298+
f"env.{name} to get variables from other wrappers is deprecated and will be removed in v1.0, "
299+
f"to get this variable you can do `env.unwrapped.{name}` for environment variables or `env.get_attr('{name}')` that will search the reminding wrappers."
300+
)
283301
return getattr(self.env, name)
284302

303+
def get_wrapper_attr(self, name: str) -> Any:
304+
"""Gets an attribute from the wrapper and lower environments if `name` doesn't exist in this object.
305+
306+
Args:
307+
name: The variable name to get
308+
309+
Returns:
310+
The variable with name in wrapper or lower environments
311+
"""
312+
if hasattr(self, name):
313+
return getattr(self, name)
314+
else:
315+
try:
316+
return self.env.get_wrapper_attr(name)
317+
except AttributeError as e:
318+
raise AttributeError(
319+
f"wrapper {self.class_name()} has no attribute {name!r}"
320+
) from e
321+
285322
@property
286323
def spec(self) -> EnvSpec | None:
287324
"""Returns the :attr:`Env` :attr:`spec` attribute with the `WrapperSpec` if the wrapper inherits from `EzPickle`."""
@@ -361,6 +398,7 @@ def reward_range(self) -> tuple[SupportsFloat, SupportsFloat]:
361398
"""Return the :attr:`Env` :attr:`reward_range` unless overwritten then the wrapper :attr:`reward_range` is used."""
362399
if self._reward_range is None:
363400
return self.env.reward_range
401+
logger.warn("The `reward_range` is deprecated and will be removed in v1.0")
364402
return self._reward_range
365403

366404
@reward_range.setter

Diff for: gymnasium/envs/registration.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import traceback
1414
from collections import defaultdict
1515
from dataclasses import dataclass, field
16+
from types import ModuleType
1617
from typing import Any, Callable, Iterable, Sequence
1718

1819
import gymnasium as gym
@@ -43,6 +44,7 @@
4344
"make_vec",
4445
"spec",
4546
"pprint_registry",
47+
"register_envs",
4648
]
4749

4850

@@ -523,7 +525,9 @@ def _find_spec(env_id: str) -> EnvSpec:
523525

524526
if env_spec is None:
525527
_check_version_exists(ns, name, version)
526-
raise error.Error(f"No registered env with id: {env_name}")
528+
raise error.Error(
529+
f"No registered env with id: {env_name}. Did you register it, or import the package that registers it? Use `gymnasium.pprint_registry()` to see all of the registered environments."
530+
)
527531

528532
return env_spec
529533

@@ -592,6 +596,11 @@ def load_plugin_envs(entry_point: str = "gymnasium.envs"):
592596
logger.warn(f"plugin: {plugin.value} raised {traceback.format_exc()}")
593597

594598

599+
def register_envs(env_module: ModuleType):
600+
"""A No-op function such that it can appear to IDEs that a module is used."""
601+
pass
602+
603+
595604
@contextlib.contextmanager
596605
def namespace(ns: str):
597606
"""Context manager for modifying the current namespace."""
@@ -658,9 +667,13 @@ def register(
658667
ns_id = current_namespace
659668
else:
660669
ns_id = ns
661-
662670
full_env_id = get_env_id(ns_id, name, version)
663671

672+
if autoreset is True:
673+
logger.warn(
674+
"`gymnasium.register(..., autoreset=True)` is deprecated and will be removed in v1.0. If users wish to use it then add the auto reset wrapper in the `addition_wrappers` argument."
675+
)
676+
664677
new_spec = EnvSpec(
665678
id=full_env_id,
666679
entry_point=entry_point,
@@ -836,6 +849,9 @@ def make(
836849
if apply_api_compatibility is True or (
837850
apply_api_compatibility is None and env_spec.apply_api_compatibility is True
838851
):
852+
logger.warn(
853+
"`gymnasium.make(..., apply_api_compatibility=True)` and `env_spec.apply_api_compatibility` is deprecated and will be removed in v1.0"
854+
)
839855
env = gym.wrappers.EnvCompatibility(env, render_mode)
840856

841857
# Run the environment checker as the lowest level wrapper
@@ -858,6 +874,10 @@ def make(
858874
if autoreset is True or (autoreset is None and env_spec.autoreset is True):
859875
env = gym.wrappers.AutoResetWrapper(env)
860876

877+
logger.warn(
878+
"`gymnasium.make(..., autoreset=True)` is deprecated and will be removed in v1.0"
879+
)
880+
861881
for wrapper_spec in env_spec.additional_wrappers[num_prior_wrappers:]:
862882
if wrapper_spec.kwargs is None:
863883
raise ValueError(

Diff for: gymnasium/experimental/wrappers/stateful_observation.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,8 @@ def __init__(
185185
self.flatten: Final[bool] = flatten
186186
self.normalize_time: Final[bool] = normalize_time
187187

188-
if hasattr(env, "_max_episode_steps"):
189-
self.max_timesteps = getattr(env, "_max_episode_steps")
190-
elif env.spec is not None and env.spec.max_episode_steps is not None:
188+
# We don't need to keep if a TimeLimit wrapper exists as `spec` will do that work for us now
189+
if env.spec is not None and env.spec.max_episode_steps is not None:
191190
self.max_timesteps = env.spec.max_episode_steps
192191
else:
193192
raise ValueError(

Diff for: gymnasium/vector/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def make(
5050
[-0.03774345, -0.02418869, -0.00942293, 0.0469184 ]],
5151
dtype=float32), {})
5252
"""
53+
gym.logger.warn(
54+
"`gymnasium.vector.make(...)` is deprecated and will be replaced by `gymnasium.make_vec(...)` in v1.0"
55+
)
5356

5457
def create_env(env_num: int) -> Callable[[], Env]:
5558
"""Creates an environment that can enable or disable the environment checker."""

Diff for: gymnasium/wrappers/compatibility.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ def __init__(self, old_env: LegacyEnv, render_mode: Optional[str] = None):
5858
render_mode (str): the render mode to use when rendering the environment, passed automatically to env.render
5959
"""
6060
logger.deprecation(
61-
"The `gymnasium.make(..., apply_api_compatibility=...)` parameter is deprecated and will be removed in v0.29. "
62-
"Instead use `gym.make('GymV21Environment-v0', env_name=...)` or `from shimmy import GymV21CompatibilityV0`"
61+
"The `gymnasium.make(..., apply_api_compatibility=...)` parameter is deprecated and will be removed in v1.0. "
62+
"Instead use `gymnasium.make('GymV21Environment-v0', env_name=...)` or `from shimmy import GymV21CompatibilityV0`"
6363
)
6464

6565
self.env = old_env

Diff for: gymnasium/wrappers/normalize.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,13 @@ def __init__(self, env: gym.Env, epsilon: float = 1e-8):
6363
gym.utils.RecordConstructorArgs.__init__(self, epsilon=epsilon)
6464
gym.Wrapper.__init__(self, env)
6565

66-
self.num_envs = getattr(env, "num_envs", 1)
67-
self.is_vector_env = getattr(env, "is_vector_env", False)
66+
try:
67+
self.num_envs = self.get_wrapper_attr("num_envs")
68+
self.is_vector_env = self.get_wrapper_attr("is_vector_env")
69+
except AttributeError:
70+
self.num_envs = 1
71+
self.is_vector_env = False
72+
6873
if self.is_vector_env:
6974
self.obs_rms = RunningMeanStd(shape=self.single_observation_space.shape)
7075
else:
@@ -121,8 +126,13 @@ def __init__(
121126
gym.utils.RecordConstructorArgs.__init__(self, gamma=gamma, epsilon=epsilon)
122127
gym.Wrapper.__init__(self, env)
123128

124-
self.num_envs = getattr(env, "num_envs", 1)
125-
self.is_vector_env = getattr(env, "is_vector_env", False)
129+
try:
130+
self.num_envs = self.get_wrapper_attr("num_envs")
131+
self.is_vector_env = self.get_wrapper_attr("is_vector_env")
132+
except AttributeError:
133+
self.num_envs = 1
134+
self.is_vector_env = False
135+
126136
self.return_rms = RunningMeanStd(shape=())
127137
self.returns = np.zeros(self.num_envs)
128138
self.gamma = gamma

Diff for: gymnasium/wrappers/record_episode_statistics.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,19 @@ def __init__(self, env: gym.Env, deque_size: int = 100):
5959
gym.utils.RecordConstructorArgs.__init__(self, deque_size=deque_size)
6060
gym.Wrapper.__init__(self, env)
6161

62-
self.num_envs = getattr(env, "num_envs", 1)
62+
try:
63+
self.num_envs = self.get_wrapper_attr("num_envs")
64+
self.is_vector_env = self.get_wrapper_attr("is_vector_env")
65+
except AttributeError:
66+
self.num_envs = 1
67+
self.is_vector_env = False
68+
6369
self.episode_count = 0
6470
self.episode_start_times: np.ndarray = None
6571
self.episode_returns: Optional[np.ndarray] = None
6672
self.episode_lengths: Optional[np.ndarray] = None
6773
self.return_queue = deque(maxlen=deque_size)
6874
self.length_queue = deque(maxlen=deque_size)
69-
self.is_vector_env = getattr(env, "is_vector_env", False)
7075

7176
def reset(self, **kwargs):
7277
"""Resets the environment using kwargs and resets the episode returns and lengths."""

Diff for: gymnasium/wrappers/record_video.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,13 @@ def __init__(
105105
self.terminated = False
106106
self.truncated = False
107107
self.recorded_frames = 0
108-
self.is_vector_env = getattr(env, "is_vector_env", False)
109108
self.episode_id = 0
110109

110+
try:
111+
self.is_vector_env = self.get_wrapper_attr("is_vector_env")
112+
except AttributeError:
113+
self.is_vector_env = False
114+
111115
def reset(self, **kwargs):
112116
"""Reset the environment using kwargs and then starts recording if video enabled."""
113117
observations = super().reset(**kwargs)

Diff for: gymnasium/wrappers/time_aware_observation.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ def __init__(self, env: gym.Env):
3737
low = np.append(self.observation_space.low, 0.0)
3838
high = np.append(self.observation_space.high, np.inf)
3939
self.observation_space = Box(low, high, dtype=np.float32)
40-
self.is_vector_env = getattr(env, "is_vector_env", False)
40+
41+
try:
42+
self.is_vector_env = self.get_wrapper_attr("is_vector_env")
43+
except AttributeError:
44+
self.is_vector_env = False
4145

4246
def observation(self, observation):
4347
"""Adds to the observation with the current time step.

Diff for: gymnasium/wrappers/vector_list_info.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ def __init__(self, env):
4848
Args:
4949
env (Env): The environment to apply the wrapper
5050
"""
51-
assert getattr(
52-
env, "is_vector_env", False
53-
), "This wrapper can only be used in vectorized environments."
54-
5551
gym.utils.RecordConstructorArgs.__init__(self)
5652
gym.Wrapper.__init__(self, env)
53+
try:
54+
self.get_wrapper_attr("is_vector_env")
55+
except AttributeError:
56+
assert False, "This wrapper can only be used in vectorized environments."
5757

5858
def step(self, action):
5959
"""Steps through the environment, convert dict info to list."""

0 commit comments

Comments
 (0)