python / typing_extensions

Backported and experimental type hints for Python
Other
445 stars 109 forks source link

Starred unpack does not equal `Unpack` in Python 3.11 #485

Open Daraan opened 1 month ago

Daraan commented 1 month ago

Equivalence between typing and typing_extensions is something I do not expect, however I think the following example is an exception as the non-equivalence comes as a surprise:

# python 3.11
import typing
from typing_extensions import Unpack, TypeVarTuple, get_type_hints
Ts = TypeVarTuple("Ts")

def foo(*x: *Ts): ...

print(get_type_hints(foo)['x'] == Unpack[Ts])  # <--- False
print(get_type_hints(foo)['x'] == typing.Unpack[Ts])  # <--- True

# or minimal example:
print(next(iter(Ts)) == Unpack[Ts])  # False

Why does this happen?

The 3.11+ backport of TypeVarTuple returns a patched instance tvt = typing.TypeVarTuple, which in turn will unpack to typing.Unpack[Ts] when __iter__ is used. (Unpack is backported until 3.11)

https://github.com/python/typing_extensions/blob/17d3a37635bad3902c4e913a48d969cbebfb08c3/src/typing_extensions.py#L2473

Possible Fixes

1) Likely bad idea: overwrite tvt.__iter__ to return typing_extensions.Unpack; might cause same problem at other places. 2) Add __eq__ to typing_extensions.Unpack to equal with typing.Unpack. BUT, this will only be valid for the left-hand-side.

   print(Unpack[Ts] == get_type_hints(foo)['x'])  # <--- True
   print(get_type_hints(foo)['x'] == Unpack[Ts])  # <--- False

Fix for the right-hand-side: a) typing_extensions._UnpackAlias must inherit from typing._UnpackGenericAlias for right-hand side priority. b) add typing._UnpackGenericAlias.__eq__ via monkey-patch

Sideeffects: All Unpack and *-unpacks will be equivalent

3) do not fix, but add a limitation/warning to the documentation that in 3.11

   typing_extensions.Unpack[Ts] != *Ts == typing.Unpack[Ts]  # pseudocode

I already have the necessary code for 2.a, but I am not sure what your stance is on this. Should equality be introduced, and if so is subclassing acceptable?

JelleZijlstra commented 1 month ago

I think I'd prefer not to introduce any changes here. I'd prefer to discourage people from relying on equality of derived typing objects; instead introduce introspection APIs (get_origin() etc.) to decompose the objects to see if they are structurally equal.