From d5e4a770f5b4d2b323ffc0f2369417934279a561 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 3 Oct 2020 18:21:59 +0800 Subject: [PATCH 1/8] Add documentation for pep604 --- Doc/library/stdtypes.rst | 95 +++++++++++++++++++ Doc/library/types.rst | 5 + Doc/library/typing.rst | 4 + Doc/whatsnew/3.10.rst | 3 + .../2020-10-03-18-20-46.bpo-41428._ju1NE.rst | 1 + 5 files changed, 108 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 62f39da2a72a2d..93ccc7d4e4b7de 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4743,6 +4743,101 @@ define these methods must provide them as a normal Python accessible method. Compared to the overhead of setting up the runtime context, the overhead of a single class dictionary lookup is negligible. +.. _types-union: + +Union Type +==================== + +A union object holds the value of the ``|`` (bitwise or) operation on +multiple :ref:`type objects`. This enables cleaner type +hinting syntax compared to :data:`typing.Union`. + +.. describe:: X | Y | ... + + Defines a union object which holds types *X*, *Y*, and so forth. X | Y + means either X or Y. It is syntactically equivalent to + ``typing.Union[X, Y, ...]``. + Example:: + + def square(number: int | float) -> int | float: + return number ** 2 + +.. describe:: union_object == other + + Union objects can be tested for equality with other union objects. Details: + + * Unions of unions are flattened, e.g.:: + + (int | str) | float == int | str | float + + * Redundant arguments are skipped, e.g.:: + + int | str | int == int | str + + * When comparing unions, the argument order is ignored, e.g.:: + + int | str == str | int + + * Compatible with :data:`typing.Union`:: + + int | str == typing.Union[int, str] + + * Optional values are equivalent to :data:`typing.Optional`:: + + str | None == typing.Optional[str] + +.. describe:: isinstance(obj, union_object) + + Calls to :func:`isinstance` are also supported with a Union object:: + + >>> isinstance("", int | str) + True + + Union objects containing parametrized generics cannot be used:: + + >>> isinstance(1, int | list[int]) + TypeError: isinstance() argument 2 cannot contain a parameterized generic + +.. describe:: issubclass(obj, union_object) + + Calls to :func:`issubclass` are also supported with a Union Object.:: + + >>> issubclass(bool, int | str) + True + + Union objects containing parametrized generics cannot be used:: + + >>> issubclass(bool, bool | list[str]) + TypeError: issubclass() argument 2 cannot contain a parameterized generic + + +The type for the Union object is :data:`types.Union`. An object +cannot be instantiated from the type. + +.. note:: + The :meth:`__or__` method for type objects was added to support the syntax + X | Y. If a metaclass implements :meth:`__or__`, the Union may + override it:: + + class M(type): + def __or__(self, other): + return "Hello" + + class C(metaclass=M): + pass + + # 'Hello' + print(C | int) + + # 'int | __main__.C' + print(int | C) + +.. seealso:: + + :pep:`604` -- PEP proposing the Union object and type. + +.. versionadded:: 3.10 + .. _typesother: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 25fa750f2ccacf..c32ddbe318f86c 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -256,6 +256,11 @@ Standard names are defined for the following types: .. versionadded:: 3.10 +.. data:: Union + + The type of :ref:`builtins.Union`. + + .. versionadded:: 3.10 .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3b824d0a4a8da0..8d6a75135110cb 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -544,6 +544,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. versionchanged:: 3.7 Don't remove explicit subclasses from unions at runtime. + .. versionchanged:: 3.10 + Unions can now be written as X | Y. See :ref:`builtins.Union + `. + .. data:: Optional Optional type. diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 957a3e791ecb69..9a4042a6463900 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -82,6 +82,9 @@ New Features * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used to require that all the iterables have an equal length. +* :pep:`604`: Builtin Union type to allow writing X | Y in place of + ``typing.Union[X, Y]``. + (Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) Other Language Changes ====================== diff --git a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst new file mode 100644 index 00000000000000..12086b4ec65f2e --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst @@ -0,0 +1 @@ +Add documentation for :pep:`604` (Allow writing union types as X | Y). From 06a16f4e2bc946696d0012665c537c2e7a63a9a9 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 3 Oct 2020 21:56:32 +0800 Subject: [PATCH 2/8] add index for union --- Doc/library/stdtypes.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 93ccc7d4e4b7de..ac4938046562a9 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4748,6 +4748,10 @@ single class dictionary lookup is negligible. Union Type ==================== +.. index:: + object: Union + pair: union; type + A union object holds the value of the ``|`` (bitwise or) operation on multiple :ref:`type objects`. This enables cleaner type hinting syntax compared to :data:`typing.Union`. From fff1e29b6a6fb4f3711258b86cce45d20b2e91f9 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 4 Oct 2020 00:17:07 +0800 Subject: [PATCH 3/8] Example to clarify types.Union instantiation --- Doc/library/stdtypes.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ac4938046562a9..8046391f0f7d72 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4816,7 +4816,13 @@ hinting syntax compared to :data:`typing.Union`. The type for the Union object is :data:`types.Union`. An object -cannot be instantiated from the type. +cannot be instantiated from the type:: + + >>> import types + >>> isinstance(int | str, types.Union) + True + >>> types.Union() + TypeError: cannot create 'types.Union' instances .. note:: The :meth:`__or__` method for type objects was added to support the syntax From a2167db4eef6ee782437f5180afdc6cffdaee92f Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 4 Oct 2020 11:10:16 +0800 Subject: [PATCH 4/8] Implement suggestions by gvanrossum and pablosgal --- Doc/library/stdtypes.rst | 50 +++++++++---------- Doc/library/types.rst | 2 +- Doc/library/typing.rst | 4 +- Doc/whatsnew/3.10.rst | 21 ++++++-- .../2020-10-03-18-20-46.bpo-41428._ju1NE.rst | 2 +- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 8046391f0f7d72..f7e8a68aebddd8 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4746,21 +4746,21 @@ single class dictionary lookup is negligible. .. _types-union: Union Type -==================== +========== .. index:: object: Union pair: union; type A union object holds the value of the ``|`` (bitwise or) operation on -multiple :ref:`type objects`. This enables cleaner type -hinting syntax compared to :data:`typing.Union`. +multiple :ref:`type objects`. These types are intended +primarily for type annotations. The union type expression +enables cleaner type hinting syntax compared to :data:`typing.Union`. .. describe:: X | Y | ... - Defines a union object which holds types *X*, *Y*, and so forth. X | Y - means either X or Y. It is syntactically equivalent to - ``typing.Union[X, Y, ...]``. + Defines a union object which holds types *X*, *Y*, and so forth. ``X | Y`` + means either X or Y. It is equivalent to ``typing.Union[X, Y, ...]``. Example:: def square(number: int | float) -> int | float: @@ -4774,19 +4774,19 @@ hinting syntax compared to :data:`typing.Union`. (int | str) | float == int | str | float - * Redundant arguments are skipped, e.g.:: + * Redundant types are removed, e.g.:: int | str | int == int | str - * When comparing unions, the argument order is ignored, e.g.:: + * When comparing unions, the order is ignored, e.g.:: int | str == str | int - * Compatible with :data:`typing.Union`:: + * It is compatible with :data:`typing.Union`:: int | str == typing.Union[int, str] - * Optional values are equivalent to :data:`typing.Optional`:: + * Optional types can be spelled as a union with None:: str | None == typing.Optional[str] @@ -4797,7 +4797,7 @@ hinting syntax compared to :data:`typing.Union`. >>> isinstance("", int | str) True - Union objects containing parametrized generics cannot be used:: + However, union objects containing parameterized generics cannot be used:: >>> isinstance(1, int | list[int]) TypeError: isinstance() argument 2 cannot contain a parameterized generic @@ -4809,7 +4809,8 @@ hinting syntax compared to :data:`typing.Union`. >>> issubclass(bool, int | str) True - Union objects containing parametrized generics cannot be used:: + However, union objects containing parameterized :ref:`generics` + cannot be used:: >>> issubclass(bool, bool | list[str]) TypeError: issubclass() argument 2 cannot contain a parameterized generic @@ -4826,21 +4827,20 @@ cannot be instantiated from the type:: .. note:: The :meth:`__or__` method for type objects was added to support the syntax - X | Y. If a metaclass implements :meth:`__or__`, the Union may + ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may override it:: - class M(type): - def __or__(self, other): - return "Hello" - - class C(metaclass=M): - pass - - # 'Hello' - print(C | int) - - # 'int | __main__.C' - print(int | C) + >>> class M(type): + ... def __or__(self, other): + ... return "Hello" + ... + >>> class C(metaclass=M): + ... pass + ... + >>> C | int + 'Hello' + >>> int | C + int | __main__.C .. seealso:: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index c32ddbe318f86c..e4a8dec5cb95a1 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -258,7 +258,7 @@ Standard names are defined for the following types: .. data:: Union - The type of :ref:`builtins.Union`. + The type of :ref:`union type expressions`. .. versionadded:: 3.10 diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 8d6a75135110cb..ffae6ff65bc4ee 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -545,8 +545,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn Don't remove explicit subclasses from unions at runtime. .. versionchanged:: 3.10 - Unions can now be written as X | Y. See :ref:`builtins.Union - `. + Unions can now be written as ``X | Y``. See + :ref:`union type expressions`. .. data:: Optional diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 9a4042a6463900..657037045887d8 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -82,9 +82,24 @@ New Features * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used to require that all the iterables have an equal length. -* :pep:`604`: Builtin Union type to allow writing X | Y in place of - ``typing.Union[X, Y]``. - (Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) +PEP604: New Type Operators +-------------------------- +There is a new syntax ``X | Y`` that allows for type union expressions. This +provides a cleaner way of writing 'either type X or type Y' instead of +using :data:`typing.Union`. + +For example:: + + def square(number: int | float) -> int | float: + return number ** 2 + +Which is equivalent to, but more readable than using ``typing.Union``:: + + def square(number: Union[int, float]) -> Union[int, float]: + return number ** 2 + +See :pep:`604` more details. +(Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) Other Language Changes ====================== diff --git a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst index 12086b4ec65f2e..2c333934560197 100644 --- a/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst +++ b/Misc/NEWS.d/next/Documentation/2020-10-03-18-20-46.bpo-41428._ju1NE.rst @@ -1 +1 @@ -Add documentation for :pep:`604` (Allow writing union types as X | Y). +Add documentation for :pep:`604` (Allow writing union types as ``X | Y``). From d983b027413906dc412463edbcf16a100af11e41 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 4 Oct 2020 11:31:50 +0800 Subject: [PATCH 5/8] Add reference to generics --- Doc/library/stdtypes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f7e8a68aebddd8..5af34bee64d617 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4797,7 +4797,8 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. >>> isinstance("", int | str) True - However, union objects containing parameterized generics cannot be used:: + However, union objects containing parameterized :ref:`generics` + cannot be used:: >>> isinstance(1, int | list[int]) TypeError: isinstance() argument 2 cannot contain a parameterized generic From d225dceb7ea403dda530bd03aa873fb0c6c98de8 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 4 Oct 2020 17:09:18 +0800 Subject: [PATCH 6/8] PEP12 styling, fix grammar/wording, give full errors --- Doc/library/stdtypes.rst | 27 ++++++++++++++++----------- Doc/whatsnew/3.10.rst | 22 +++++++++++++--------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 5af34bee64d617..442acd260a2ad0 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4753,14 +4753,14 @@ Union Type pair: union; type A union object holds the value of the ``|`` (bitwise or) operation on -multiple :ref:`type objects`. These types are intended -primarily for type annotations. The union type expression -enables cleaner type hinting syntax compared to :data:`typing.Union`. +multiple :ref:`type objects`. These types are intended +primarily for type annotations. The union type expression enables cleaner +type hinting syntax compared to :data:`typing.Union`. .. describe:: X | Y | ... Defines a union object which holds types *X*, *Y*, and so forth. ``X | Y`` - means either X or Y. It is equivalent to ``typing.Union[X, Y, ...]``. + means either X or Y. It is equivalent to ``typing.Union[X, Y, ...]``. Example:: def square(number: int | float) -> int | float: @@ -4768,7 +4768,7 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. .. describe:: union_object == other - Union objects can be tested for equality with other union objects. Details: + Union objects can be tested for equality with other union objects. Details: * Unions of unions are flattened, e.g.:: @@ -4786,7 +4786,7 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. int | str == typing.Union[int, str] - * Optional types can be spelled as a union with None:: + * Optional types can be spelled as a union with ``None``:: str | None == typing.Optional[str] @@ -4801,6 +4801,8 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. cannot be used:: >>> isinstance(1, int | list[int]) + Traceback (most recent call last): + File "", line 1, in TypeError: isinstance() argument 2 cannot contain a parameterized generic .. describe:: issubclass(obj, union_object) @@ -4814,21 +4816,24 @@ enables cleaner type hinting syntax compared to :data:`typing.Union`. cannot be used:: >>> issubclass(bool, bool | list[str]) + Traceback (most recent call last): + File "", line 1, in TypeError: issubclass() argument 2 cannot contain a parameterized generic - -The type for the Union object is :data:`types.Union`. An object -cannot be instantiated from the type:: +The type for the Union object is :data:`types.Union`. An object cannot be +instantiated from the type:: >>> import types >>> isinstance(int | str, types.Union) True >>> types.Union() + Traceback (most recent call last): + File "", line 1, in TypeError: cannot create 'types.Union' instances .. note:: The :meth:`__or__` method for type objects was added to support the syntax - ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may + ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may override it:: >>> class M(type): @@ -4845,7 +4850,7 @@ cannot be instantiated from the type:: .. seealso:: - :pep:`604` -- PEP proposing the Union object and type. + :pep:`604` -- PEP proposing the ``X | Y`` syntax and the Union type. .. versionadded:: 3.10 diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 657037045887d8..ca4dcfa6d6e8e1 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -82,23 +82,27 @@ New Features * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used to require that all the iterables have an equal length. -PEP604: New Type Operators --------------------------- -There is a new syntax ``X | Y`` that allows for type union expressions. This -provides a cleaner way of writing 'either type X or type Y' instead of +PEP604: New Type Operator +------------------------- +A new type union operator was introduced which enables the syntax ``X | Y``. +This provides a cleaner way of expressing 'either type X or type Y' instead of using :data:`typing.Union`. -For example:: +In previous versions of Python, to apply a type hint for functions accepting +arguments of multiple types, :data:`typing.Union` was used:: - def square(number: int | float) -> int | float: + def square(number: Union[int, float]) -> Union[int, float]: return number ** 2 -Which is equivalent to, but more readable than using ``typing.Union``:: - def square(number: Union[int, float]) -> Union[int, float]: +Now, type hints can be written in a more succinct manner:: + + def square(number: int | float) -> int | float: return number ** 2 -See :pep:`604` more details. + +See :pep:`604` for more details. + (Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) Other Language Changes From 6ab4717b50b321384a0e111b1e6bb0e992fccedb Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 5 Oct 2020 10:12:57 +0800 Subject: [PATCH 7/8] Apply reviews, add comments to replace link --- Doc/library/stdtypes.rst | 21 ++++++++++++++++----- Doc/whatsnew/3.10.rst | 3 ++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 442acd260a2ad0..04dfea276d2b1e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4760,7 +4760,7 @@ type hinting syntax compared to :data:`typing.Union`. .. describe:: X | Y | ... Defines a union object which holds types *X*, *Y*, and so forth. ``X | Y`` - means either X or Y. It is equivalent to ``typing.Union[X, Y, ...]``. + means either X or Y. It is equivalent to ``typing.Union[X, Y]``. Example:: def square(number: int | float) -> int | float: @@ -4797,8 +4797,14 @@ type hinting syntax compared to :data:`typing.Union`. >>> isinstance("", int | str) True - However, union objects containing parameterized :ref:`generics` - cannot be used:: + .. + At the time of writing this, there is no documentation for parameterized + generics or PEP 585. Thus the link currently points to PEP 585 itself. + Please change the link for parameterized generics to reference the correct + documentation once documentation for PEP 585 becomes available. + + However, union objects containing `parameterized generics + `_ cannot be used:: >>> isinstance(1, int | list[int]) Traceback (most recent call last): @@ -4812,8 +4818,13 @@ type hinting syntax compared to :data:`typing.Union`. >>> issubclass(bool, int | str) True - However, union objects containing parameterized :ref:`generics` - cannot be used:: + .. + Once again, please change the link below for parameterized generics to + reference the correct documentation once documentation for PEP 585 + becomes available. + + However, union objects containing `parameterized generics + `_ cannot be used:: >>> issubclass(bool, bool | list[str]) Traceback (most recent call last): diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index ca4dcfa6d6e8e1..9c3a0287d55095 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -84,9 +84,10 @@ New Features PEP604: New Type Operator ------------------------- + A new type union operator was introduced which enables the syntax ``X | Y``. This provides a cleaner way of expressing 'either type X or type Y' instead of -using :data:`typing.Union`. +using :data:`typing.Union`, especially in type hints (annotations). In previous versions of Python, to apply a type hint for functions accepting arguments of multiple types, :data:`typing.Union` was used:: From a77b261463dd0b8540daf51ce170faf2d93b9c7c Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 5 Oct 2020 10:16:18 +0800 Subject: [PATCH 8/8] Update ACKS --- Misc/ACKS | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/ACKS b/Misc/ACKS index 9be0e777ca2942..08449fe08269bd 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1257,6 +1257,7 @@ Grant Olson Furkan Onder Koray Oner Ethan Onstott +Ken Jin Ooi Piet van Oostrum Tomas Oppelstrup Jason Orendorff