Skip to content

Allow generic type hinting for Tuple space#1537

Open
Coolgolf1 wants to merge 2 commits intoFarama-Foundation:mainfrom
Coolgolf1:main
Open

Allow generic type hinting for Tuple space#1537
Coolgolf1 wants to merge 2 commits intoFarama-Foundation:mainfrom
Coolgolf1:main

Conversation

@Coolgolf1
Copy link
Copy Markdown

@Coolgolf1 Coolgolf1 commented Feb 27, 2026

Description

This PR enables developers to specify the "sub-types" of a Tuple space using standard type hinting. Example: observation_space: spaces.Tuple[spaces.MultiDiscrete, spaces.Box].

Fixes #1476

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist:

  • I have run the pre-commit checks with pre-commit run --all-files (see CONTRIBUTING.md instructions to set it up)
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@pseudo-rnd-thoughts
Copy link
Copy Markdown
Member

pseudo-rnd-thoughts commented Mar 9, 2026

This is a cool PR, thanks @Coolgolf1
The limitation that I see is that Gymnasium's type hinting isn't well developed which can be seen in pyproject.toml as most of our type hinting is actually disabled currently.

Therefore, I would prefer for us to solve the general type hinting before merging this PR.
Hope that makes sense

@Coolgolf1
Copy link
Copy Markdown
Author

Thanks @pseudo-rnd-thoughts, that makes sense!

If you're open to improving the type hinting, I'd be happy to help and contribute.

I'm new to open source, and I'd love to participate or help in any way I can.

Copy link
Copy Markdown
Contributor

@jorenham jorenham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there,

I took a look and left some suggestions. Hope you don't mind :)

Comment thread gymnasium/spaces/tuple.py

class Tuple(Space[tuple[Any, ...]], typing.Sequence[Any]):

_TSpaces = TypeVarTuple("_TSpaces")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: The (informal) convention for type variable names is to add T as suffix, so _SpacesT.

Comment thread gymnasium/spaces/tuple.py
Comment on lines +13 to +18
try:
# Python 3.11+
from typing import TypeVarTuple, Unpack
except ImportError: # pragma: no cover
# For older Python versions supported by Gymnasium
from typing_extensions import TypeVarTuple, Unpack
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static type-checkers typically (pun intended) don't understand try: blocks like this one. They do, however, understand if sys.version_info >= (3, 11): guards.

Comment thread gymnasium/spaces/tuple.py
Comment on lines +40 to +44
# Help type-checkers understand that Tuple[Box, Discrete].spaces is (Box, Discrete)
if TYPE_CHECKING:
spaces: tuple[Unpack[_TSpaces]]
else:
spaces: tuple[Space[Any], ...]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed, because Unpack is imported, so it should be available at runtime.

Comment thread gymnasium/spaces/tuple.py
Comment on lines +52 to +59

@overload
def __init__(
self,
spaces: Iterable[Space[Any]],
seed: int | typing.Sequence[int] | np.random.Generator | None = None,
): ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: The convention is to have no newlines between overloads of the same function.

Suggested change
@overload
def __init__(
self,
spaces: Iterable[Space[Any]],
seed: int | typing.Sequence[int] | np.random.Generator | None = None,
): ...
@overload
def __init__(
self,
spaces: Iterable[Space[Any]],
seed: int | typing.Sequence[int] | np.random.Generator | None = None,
): ...

Comment thread gymnasium/spaces/tuple.py
Comment on lines +181 to +182
# Promote list and ndarray to tuple for contains check
x = tuple(x)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change seems irrelevant?

Comment on lines +15 to +19
try:
from typing import assert_type # type: ignore[attr-defined]
except ImportError: # pragma: no cover
# type: ignore[import-not-found]
from typing_extensions import assert_type
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a TYPE_CHECKING block you can always import typing_extensions; even if it's not installed. So there's no need for this try: block (or any of the type ignores for that matter).

Comment on lines +69 to +70
if TYPE_CHECKING:
assert_type(obs_space.spaces, tuple[MultiDiscrete, Box])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like you said, this is a no-op at runtime. So typing.assert_type would be more appropriate here (i.e. without the TYPE_CHECKING guard)

Comment on lines +124 to +125
space = gym.spaces.Tuple(
(gym.spaces.Box(0, 1), gym.spaces.Box(-1, 0, (2,))))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change seems unnecessary

Comment on lines +132 to +133
space = gym.spaces.Tuple(
(gym.spaces.Box(0, 1), gym.spaces.Box(-1, 0, (1,))))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change seems unnecessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Proposal] Make the Tuple space a generic class

3 participants