Skip to content

Commit db7b61b

Browse files
JukkaLbrianschubertJelleZijlstra
authored
Make "X | Y" union syntax more prominent in documentation (#17835)
Soon 4 out of 5 supported Python versions will support the `X | Y` syntax, so we can make it more prominent (Python 3.13 will be out soon, and 3.8 will reach end of life). Use it in most examples. The syntax is available starting from Python 3.10, but it can be used in annotations in earlier Python versions as well if using `from __future__ import annotations`. --------- Co-authored-by: Brian Schubert <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 0dfb718 commit db7b61b

14 files changed

+149
-139
lines changed

docs/source/cheat_sheet_py3.rst

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ Useful built-in types
7272
# On earlier versions, use Union
7373
x: list[Union[int, str]] = [3, 5, "test", "fun"]
7474
75-
# Use Optional[X] for a value that could be None
76-
# Optional[X] is the same as X | None or Union[X, None]
77-
x: Optional[str] = "something" if some_condition() else None
75+
# Use X | None for a value that could be None on Python 3.10+
76+
# Use Optional[X] on 3.9 and earlier; Optional[X] is the same as 'X | None'
77+
x: str | None = "something" if some_condition() else None
7878
if x is not None:
7979
# Mypy understands x won't be None here because of the if-statement
8080
print(x.upper())
@@ -122,13 +122,14 @@ Functions
122122
i += 1
123123
124124
# You can of course split a function annotation over multiple lines
125-
def send_email(address: Union[str, list[str]],
126-
sender: str,
127-
cc: Optional[list[str]],
128-
bcc: Optional[list[str]],
129-
subject: str = '',
130-
body: Optional[list[str]] = None
131-
) -> bool:
125+
def send_email(
126+
address: str | list[str],
127+
sender: str,
128+
cc: list[str] | None,
129+
bcc: list[str] | None,
130+
subject: str = '',
131+
body: list[str] | None = None,
132+
) -> bool:
132133
...
133134
134135
# Mypy understands positional-only and keyword-only arguments
@@ -231,7 +232,7 @@ When you're puzzled or when things are complicated
231232
# If you initialize a variable with an empty container or "None"
232233
# you may have to help mypy a bit by providing an explicit type annotation
233234
x: list[str] = []
234-
x: Optional[str] = None
235+
x: str | None = None
235236
236237
# Use Any if you don't know the type of something or it's too
237238
# dynamic to write a type for

docs/source/command_line.rst

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,11 @@ The following flags adjust how mypy handles values of type ``None``.
420420

421421
.. option:: --implicit-optional
422422

423-
This flag causes mypy to treat arguments with a ``None``
424-
default value as having an implicit :py:data:`~typing.Optional` type.
423+
This flag causes mypy to treat parameters with a ``None``
424+
default value as having an implicit optional type (``T | None``).
425425

426426
For example, if this flag is set, mypy would assume that the ``x``
427-
parameter is actually of type ``Optional[int]`` in the code snippet below
427+
parameter is actually of type ``int | None`` in the code snippet below,
428428
since the default parameter is ``None``:
429429

430430
.. code-block:: python
@@ -438,7 +438,7 @@ The following flags adjust how mypy handles values of type ``None``.
438438

439439
.. option:: --no-strict-optional
440440

441-
This flag effectively disables checking of :py:data:`~typing.Optional`
441+
This flag effectively disables checking of optional
442442
types and ``None`` values. With this option, mypy doesn't
443443
generally check the use of ``None`` values -- it is treated
444444
as compatible with every type.
@@ -575,26 +575,24 @@ of the above sections.
575575
.. option:: --local-partial-types
576576

577577
In mypy, the most common cases for partial types are variables initialized using ``None``,
578-
but without explicit ``Optional`` annotations. By default, mypy won't check partial types
578+
but without explicit ``X | None`` annotations. By default, mypy won't check partial types
579579
spanning module top level or class top level. This flag changes the behavior to only allow
580580
partial types at local level, therefore it disallows inferring variable type for ``None``
581581
from two assignments in different scopes. For example:
582582

583583
.. code-block:: python
584584
585-
from typing import Optional
586-
587585
a = None # Need type annotation here if using --local-partial-types
588-
b: Optional[int] = None
586+
b: int | None = None
589587
590588
class Foo:
591589
bar = None # Need type annotation here if using --local-partial-types
592-
baz: Optional[int] = None
590+
baz: int | None = None
593591
594592
def __init__(self) -> None:
595593
self.bar = 1
596594
597-
reveal_type(Foo().bar) # Union[int, None] without --local-partial-types
595+
reveal_type(Foo().bar) # 'int | None' without --local-partial-types
598596
599597
Note: this option is always implicitly enabled in mypy daemon and will become
600598
enabled by default for mypy in a future release.

docs/source/common_issues.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ This is best understood via an example:
803803

804804
.. code-block:: python
805805
806-
def foo(x: Optional[int]) -> Callable[[], int]:
806+
def foo(x: int | None) -> Callable[[], int]:
807807
if x is None:
808808
x = 5
809809
print(x + 1) # mypy correctly deduces x must be an int here

docs/source/config_file.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,8 @@ section of the command line docs.
574574
:type: boolean
575575
:default: False
576576

577-
Causes mypy to treat arguments with a ``None``
578-
default value as having an implicit :py:data:`~typing.Optional` type.
577+
Causes mypy to treat parameters with a ``None``
578+
default value as having an implicit optional type (``T | None``).
579579

580580
**Note:** This was True by default in mypy versions 0.980 and earlier.
581581

@@ -584,7 +584,7 @@ section of the command line docs.
584584
:type: boolean
585585
:default: True
586586

587-
Effectively disables checking of :py:data:`~typing.Optional`
587+
Effectively disables checking of optional
588588
types and ``None`` values. With this option, mypy doesn't
589589
generally check the use of ``None`` values -- it is treated
590590
as compatible with every type.

docs/source/error_code_list.rst

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ Example:
5959

6060
.. code-block:: python
6161
62-
from typing import Union
63-
6462
class Cat:
6563
def sleep(self) -> None: ...
6664
def miaow(self) -> None: ...
@@ -69,10 +67,10 @@ Example:
6967
def sleep(self) -> None: ...
7068
def follow_me(self) -> None: ...
7169
72-
def func(animal: Union[Cat, Dog]) -> None:
70+
def func(animal: Cat | Dog) -> None:
7371
# OK: 'sleep' is defined for both Cat and Dog
7472
animal.sleep()
75-
# Error: Item "Cat" of "Union[Cat, Dog]" has no attribute "follow_me" [union-attr]
73+
# Error: Item "Cat" of "Cat | Dog" has no attribute "follow_me" [union-attr]
7674
animal.follow_me()
7775
7876
You can often work around these errors by using ``assert isinstance(obj, ClassName)``
@@ -142,9 +140,7 @@ Example:
142140

143141
.. code-block:: python
144142
145-
from typing import Optional
146-
147-
def first(x: list[int]) -> Optional[int]:
143+
def first(x: list[int]) -> int:
148144
return x[0] if x else 0
149145
150146
t = (5, 4)
@@ -165,15 +161,15 @@ Example:
165161

166162
.. code-block:: python
167163
168-
from typing import overload, Optional
164+
from typing import overload
169165
170166
@overload
171167
def inc_maybe(x: None) -> None: ...
172168
173169
@overload
174170
def inc_maybe(x: int) -> int: ...
175171
176-
def inc_maybe(x: Optional[int]) -> Optional[int]:
172+
def inc_maybe(x: int | None) -> int | None:
177173
if x is None:
178174
return None
179175
else:
@@ -273,16 +269,14 @@ Example:
273269

274270
.. code-block:: python
275271
276-
from typing import Optional, Union
277-
278272
class Base:
279273
def method(self,
280-
arg: int) -> Optional[int]:
274+
arg: int) -> int | None:
281275
...
282276
283277
class Derived(Base):
284278
def method(self,
285-
arg: Union[int, str]) -> int: # OK
279+
arg: int | str) -> int: # OK
286280
...
287281
288282
class DerivedBad(Base):

docs/source/generics.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,9 @@ Let us illustrate this by few simple examples:
571571
class Square(Shape): ...
572572
573573
* Most immutable container types, such as :py:class:`~collections.abc.Sequence`
574-
and :py:class:`~frozenset` are covariant. :py:data:`~typing.Union` is
575-
also covariant in all variables: ``Union[Triangle, int]`` is
576-
a subtype of ``Union[Shape, int]``.
574+
and :py:class:`~frozenset` are covariant. Union types are
575+
also covariant in all union items: ``Triangle | int`` is
576+
a subtype of ``Shape | int``.
577577

578578
.. code-block:: python
579579

docs/source/getting_started.rst

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,19 +186,23 @@ For example, a ``RuntimeError`` instance can be passed to a function that is ann
186186
as taking an ``Exception``.
187187

188188
As another example, suppose you want to write a function that can accept *either*
189-
ints or strings, but no other types. You can express this using the
190-
:py:data:`~typing.Union` type. For example, ``int`` is a subtype of ``Union[int, str]``:
189+
ints or strings, but no other types. You can express this using a
190+
union type. For example, ``int`` is a subtype of ``int | str``:
191191

192192
.. code-block:: python
193193
194-
from typing import Union
195-
196-
def normalize_id(user_id: Union[int, str]) -> str:
194+
def normalize_id(user_id: int | str) -> str:
197195
if isinstance(user_id, int):
198196
return f'user-{100_000 + user_id}'
199197
else:
200198
return user_id
201199
200+
.. note::
201+
202+
If using Python 3.9 or earlier, use ``typing.Union[int, str]`` instead of
203+
``int | str``, or use ``from __future__ import annotations`` at the top of
204+
the file (see :ref:`runtime_troubles`).
205+
202206
The :py:mod:`typing` module contains many other useful types.
203207

204208
For a quick overview, look through the :ref:`mypy cheatsheet <cheat-sheet-py3>`.
@@ -210,7 +214,7 @@ generic types or your own type aliases), look through the
210214
.. note::
211215

212216
When adding types, the convention is to import types
213-
using the form ``from typing import Union`` (as opposed to doing
217+
using the form ``from typing import <name>`` (as opposed to doing
214218
just ``import typing`` or ``import typing as t`` or ``from typing import *``).
215219

216220
For brevity, we often omit imports from :py:mod:`typing` or :py:mod:`collections.abc`

0 commit comments

Comments
 (0)