Skip to content

Commit dc6d66f

Browse files
JelleZijlstraAlexWaygoodFidget-Spinnercarljm
authored
gh-105499: Merge typing.Union and types.UnionType (#105511)
Co-authored-by: Alex Waygood <[email protected]> Co-authored-by: Ken Jin <[email protected]> Co-authored-by: Carl Meyer <[email protected]>
1 parent e091520 commit dc6d66f

File tree

20 files changed

+542
-307
lines changed

20 files changed

+542
-307
lines changed

Doc/deprecations/pending-removal-in-future.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ although there is currently no date scheduled for their removal.
127127

128128
* :class:`typing.Text` (:gh:`92332`).
129129

130+
* The internal class ``typing._UnionGenericAlias`` is no longer used to implement
131+
:class:`typing.Union`. To preserve compatibility with users using this private
132+
class, a compatibility shim will be provided until at least Python 3.17. (Contributed by
133+
Jelle Zijlstra in :gh:`105499`.)
134+
130135
* :class:`unittest.IsolatedAsyncioTestCase`: it is deprecated to return a value
131136
that is not ``None`` from a test case.
132137

Doc/library/functools.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ The :mod:`functools` module defines the following functions:
518518
... for i, elem in enumerate(arg):
519519
... print(i, elem)
520520

521-
:data:`types.UnionType` and :data:`typing.Union` can also be used::
521+
:class:`typing.Union` can also be used::
522522

523523
>>> @fun.register
524524
... def _(arg: int | float, verbose=False):
@@ -654,8 +654,8 @@ The :mod:`functools` module defines the following functions:
654654
The :func:`register` attribute now supports using type annotations.
655655

656656
.. versionchanged:: 3.11
657-
The :func:`register` attribute now supports :data:`types.UnionType`
658-
and :data:`typing.Union` as type annotations.
657+
The :func:`register` attribute now supports
658+
:class:`typing.Union` as a type annotation.
659659

660660

661661
.. class:: singledispatchmethod(func)

Doc/library/stdtypes.rst

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5364,7 +5364,7 @@ Union Type
53645364
A union object holds the value of the ``|`` (bitwise or) operation on
53655365
multiple :ref:`type objects <bltin-type-objects>`. These types are intended
53665366
primarily for :term:`type annotations <annotation>`. The union type expression
5367-
enables cleaner type hinting syntax compared to :data:`typing.Union`.
5367+
enables cleaner type hinting syntax compared to subscripting :class:`typing.Union`.
53685368

53695369
.. describe:: X | Y | ...
53705370

@@ -5400,9 +5400,10 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`.
54005400

54015401
int | str == str | int
54025402

5403-
* It is compatible with :data:`typing.Union`::
5403+
* It creates instances of :class:`typing.Union`::
54045404

54055405
int | str == typing.Union[int, str]
5406+
type(int | str) is typing.Union
54065407

54075408
* Optional types can be spelled as a union with ``None``::
54085409

@@ -5428,16 +5429,15 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`.
54285429
TypeError: isinstance() argument 2 cannot be a parameterized generic
54295430

54305431
The user-exposed type for the union object can be accessed from
5431-
:data:`types.UnionType` and used for :func:`isinstance` checks. An object cannot be
5432-
instantiated from the type::
5432+
:class:`typing.Union` and used for :func:`isinstance` checks::
54335433

5434-
>>> import types
5435-
>>> isinstance(int | str, types.UnionType)
5434+
>>> import typing
5435+
>>> isinstance(int | str, typing.Union)
54365436
True
5437-
>>> types.UnionType()
5437+
>>> typing.Union()
54385438
Traceback (most recent call last):
54395439
File "<stdin>", line 1, in <module>
5440-
TypeError: cannot create 'types.UnionType' instances
5440+
TypeError: cannot create 'typing.Union' instances
54415441

54425442
.. note::
54435443
The :meth:`!__or__` method for type objects was added to support the syntax
@@ -5464,6 +5464,11 @@ instantiated from the type::
54645464

54655465
.. versionadded:: 3.10
54665466

5467+
.. versionchanged:: 3.14
5468+
5469+
Union objects are now instances of :class:`typing.Union`. Previously, they were instances
5470+
of :class:`types.UnionType`, which remains an alias for :class:`typing.Union`.
5471+
54675472

54685473
.. _typesother:
54695474

Doc/library/types.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ Standard names are defined for the following types:
314314

315315
.. versionadded:: 3.10
316316

317+
.. versionchanged:: 3.14
318+
319+
This is now an alias for :class:`typing.Union`.
320+
317321
.. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno)
318322

319323
The type of traceback objects such as found in ``sys.exception().__traceback__``.

Doc/library/typing.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@ Special forms
10861086
These can be used as types in annotations. They all support subscription using
10871087
``[]``, but each has a unique syntax.
10881088

1089-
.. data:: Union
1089+
.. class:: Union
10901090

10911091
Union type; ``Union[X, Y]`` is equivalent to ``X | Y`` and means either X or Y.
10921092

@@ -1121,6 +1121,14 @@ These can be used as types in annotations. They all support subscription using
11211121
Unions can now be written as ``X | Y``. See
11221122
:ref:`union type expressions<types-union>`.
11231123

1124+
.. versionchanged:: 3.14
1125+
:class:`types.UnionType` is now an alias for :class:`Union`, and both
1126+
``Union[int, str]`` and ``int | str`` create instances of the same class.
1127+
To check whether an object is a ``Union`` at runtime, use
1128+
``isinstance(obj, Union)``. For compatibility with earlier versions of
1129+
Python, use
1130+
``get_origin(obj) is typing.Union or get_origin(obj) is types.UnionType``.
1131+
11241132
.. data:: Optional
11251133

11261134
``Optional[X]`` is equivalent to ``X | None`` (or ``Union[X, None]``).

Doc/whatsnew/3.10.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,10 +722,10 @@ PEP 604: New Type Union Operator
722722
723723
A new type union operator was introduced which enables the syntax ``X | Y``.
724724
This provides a cleaner way of expressing 'either type X or type Y' instead of
725-
using :data:`typing.Union`, especially in type hints.
725+
using :class:`typing.Union`, especially in type hints.
726726
727727
In previous versions of Python, to apply a type hint for functions accepting
728-
arguments of multiple types, :data:`typing.Union` was used::
728+
arguments of multiple types, :class:`typing.Union` was used::
729729
730730
def square(number: Union[int, float]) -> Union[int, float]:
731731
return number ** 2

Doc/whatsnew/3.11.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,8 @@ fractions
740740
functools
741741
---------
742742

743-
* :func:`functools.singledispatch` now supports :data:`types.UnionType`
744-
and :data:`typing.Union` as annotations to the dispatch argument.::
743+
* :func:`functools.singledispatch` now supports :class:`types.UnionType`
744+
and :class:`typing.Union` as annotations to the dispatch argument.::
745745

746746
>>> from functools import singledispatch
747747
>>> @singledispatch

Include/internal/pycore_unionobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ PyAPI_FUNC(PyObject *) _Py_union_type_or(PyObject *, PyObject *);
1818
extern PyObject *_Py_subs_parameters(PyObject *, PyObject *, PyObject *, PyObject *);
1919
extern PyObject *_Py_make_parameters(PyObject *);
2020
extern PyObject *_Py_union_args(PyObject *self);
21+
extern PyObject *_Py_union_from_tuple(PyObject *args);
2122

2223
#ifdef __cplusplus
2324
}

Lib/functools.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -926,16 +926,11 @@ def dispatch(cls):
926926
dispatch_cache[cls] = impl
927927
return impl
928928

929-
def _is_union_type(cls):
930-
from typing import get_origin, Union
931-
return get_origin(cls) in {Union, UnionType}
932-
933929
def _is_valid_dispatch_type(cls):
934930
if isinstance(cls, type):
935931
return True
936-
from typing import get_args
937-
return (_is_union_type(cls) and
938-
all(isinstance(arg, type) for arg in get_args(cls)))
932+
return (isinstance(cls, UnionType) and
933+
all(isinstance(arg, type) for arg in cls.__args__))
939934

940935
def register(cls, func=None):
941936
"""generic_func.register(cls, func) -> func
@@ -967,7 +962,7 @@ def register(cls, func=None):
967962
from annotationlib import Format, ForwardRef
968963
argname, cls = next(iter(get_type_hints(func, format=Format.FORWARDREF).items()))
969964
if not _is_valid_dispatch_type(cls):
970-
if _is_union_type(cls):
965+
if isinstance(cls, UnionType):
971966
raise TypeError(
972967
f"Invalid annotation for {argname!r}. "
973968
f"{cls!r} not all arguments are classes."
@@ -983,10 +978,8 @@ def register(cls, func=None):
983978
f"{cls!r} is not a class."
984979
)
985980

986-
if _is_union_type(cls):
987-
from typing import get_args
988-
989-
for arg in get_args(cls):
981+
if isinstance(cls, UnionType):
982+
for arg in cls.__args__:
990983
registry[arg] = func
991984
else:
992985
registry[cls] = func

Lib/test/test_dataclasses/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2314,7 +2314,7 @@ def test_docstring_one_field_with_default_none(self):
23142314
class C:
23152315
x: Union[int, type(None)] = None
23162316

2317-
self.assertDocStrEqual(C.__doc__, "C(x:Optional[int]=None)")
2317+
self.assertDocStrEqual(C.__doc__, "C(x:int|None=None)")
23182318

23192319
def test_docstring_list_field(self):
23202320
@dataclass

0 commit comments

Comments
 (0)