The typing
module was introduced in Python 3.5 (Preliminary typing.py, anticipating provisional acceptance of PEP 484. · python/cpython@46dbb7d · GitHub). Initially, almost all these generic subscriptable things (List
, Sequence
, Callable
) were classes (with special metaclass). The only exceptions were Union
and Optional
. Subscripting all of them returned an instance of the same class, so it was not easy to distinguish generic things from non-generic. There was a lot of hacking here.
In Python 3.7 it was re-designed (Implement PEP 560: Core support for typing module and generic types · Issue #76407 · python/cpython · GitHub) – now most of subscriptable things (except Generic
) no longer classes, they were instances of _GenericAlias
or _SpecialForm
. Subscribing them returned an instance of _GenericAlias
. Still a lot of hacking.
In Python 3.9 I refactored the whole hierarhy in more strictly typed way (Refactor typing._GenericAlias · Issue #84577 · python/cpython · GitHub), so List
and List[int]
became instances of different types. Subscribing any of subscrptable things now returns and instance of _GenericAlias
or its more specialized subclasses (like _UnionGenericAlias
). types.GenericAlias
also was introduced in Python 3.9 (Implement PEP 585 (Type Hinting Generics In Standard Collections) · Issue #83662 · python/cpython · GitHub). It was similar to typing._GenericAlias
and named alike. typling.List[int]
returns typing._GenericAlias
, list[int]
returns types.GenericAlias
.
In Python 3.10, types.UnionType
together with type.__or__()
was introduced (PEP 604 -- Allow writing union types as X | Y · Issue #85600 · python/cpython · GitHub). It was initially named types.Union
, but was renamed in Rename types.Union to types.UnionType · Issue #88895 · python/cpython · GitHub to avoid confusion with typing.Union
. It was similar to typing._UnionGenericAlias
. typling.Union[int, str]
returns typing._UnionGenericAlias
, int | str
returns types.UnionType
.
It was planned to merge types.GenericAlias
with typing._GenericAlias
and types.UnionType
with typing._UnionGenericAlias
in distant future (after implementing the missing pieces in types.GenericAlias
and types.UnionType
).
types.UnionType
has been merged during the 3.14 developing cycle, not with typing._UnionGenericAlias
, but with typing.Union
, which was not even a class. This breaks long planes for types.GenericAlias
and types.UnionType
and violates some principles.
typing.Union
never was a class, from the beginning. Even when other generic types were classes. And there were reasons to make other generic types non-classes.types.UnionType
should not be subscriptable. It is not generic class, and classes should only be subscriptable to make a specialized type from generic class. The result of subscripting a class is alwaystypes.GenericAlias
, nottypes.UnionType
.
I suggest two options:
- Either completely revert Merge `typing.Union` and `types.UnionType` · Issue #105499 · python/cpython · GitHub. This is preferable, because
types.UnionType
should be merged withtyping._UnionGenericAlias
simultaneously or later thantypes.GenericAlias
withtyping._GenericAlias
. - Or revert it partially, by merging
types.UnionType
with the right classtyping._UnionGenericAlias
instead oftyping.Union
(gh-137065: Unmerge types.UnionType and typing.Union by serhiy-storchaka · Pull Request #137069 · python/cpython · GitHub).
I am sorry that I only found out about this change three weeks ago and we need to make a decision so late. But if we do not make it, I suspect that we will need to spent next few years undoing this change. It will be much more painfully.