Skip to content

Commit a664337

Browse files
committed
Small changes + added docs
1 parent ba75620 commit a664337

File tree

3 files changed

+117
-7
lines changed

3 files changed

+117
-7
lines changed

docs/source/kinds_of_types.rst

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,96 @@ more specific type:
241241
since the caller may have to use :py:func:`isinstance` before doing anything
242242
interesting with the value.
243243

244+
.. _alternative_union_syntax:
245+
246+
Alternative union syntax
247+
------------------------
248+
249+
`PEP 604 <https://www.python.org/dev/peps/pep-0604/>`_ introduced an alternative way
250+
for writing union types. Starting with **Python 3.10** it is possible to write
251+
``Union[int, str]`` as ``int | str``. Any of the following options is possible
252+
253+
.. code-block:: python
254+
255+
from typing import List
256+
257+
# Use as Union
258+
t1: int | str # equivalent to Union[int, str]
259+
260+
# Use as Optional
261+
t2: int | None # equivalent to Optional[int]
262+
263+
# Use in generics
264+
t3: List[int | str] # equivalent to List[Union[int, str]]
265+
266+
# Use in type aliases
267+
T4 = int | None
268+
x: T4
269+
270+
# Quoted variable annotations
271+
t5: "int | str"
272+
273+
# Quoted function annotations
274+
def f(t6: "int | str") -> None: ...
275+
276+
# Type comments
277+
t6 = 42 # type: int | str
278+
279+
It is possible to use most of these even for earlier versions. However there are some
280+
limitations to be aware of.
281+
282+
.. _alternative_union_syntax_stub_files:
283+
284+
Stub files
285+
""""""""""
286+
287+
All options are supported, regardless of the Python version the project uses.
288+
289+
.. _alternative_union_syntax_37:
290+
291+
Python 3.7 - 3.9
292+
""""""""""""""""
293+
294+
It is necessary to add ``from __future__ import annotations`` to delay the evaluation
295+
of type annotations. Not using it would result in a ``TypeError``.
296+
This does not apply for **type comments**, **quoted function** and **quoted variable** annotations,
297+
as those also work for earlier versions, see :ref:`below <alternative_union_syntax_older_version>`.
298+
299+
.. warning::
300+
301+
Type aliases are **NOT** supported! Those result in a ``TypeError`` regardless
302+
if the evaluation of type annotations is delayed.
303+
304+
Dynamic evaluation of annotations is **NOT** possible (e.g. ``typing.get_type_hints`` and ``eval``).
305+
See `note PEP 604 <https://www.python.org/dev/peps/pep-0604/#change-only-pep-484-type-hints-to-accept-the-syntax-type1-type2>`_.
306+
Use ``typing.Union`` instead!
307+
308+
.. code-block:: python
309+
310+
from __future__ import annotations
311+
312+
t1: int | None
313+
314+
# Type aliases
315+
T2 = int | None # TypeError!
316+
317+
.. _alternative_union_syntax_older_version:
318+
319+
Older versions
320+
""""""""""""""
321+
322+
+------------------------------------------+-----------+-----------+-----------+
323+
| Python Version | 3.6 | 3.0 - 3.5 | 2.7 |
324+
+==========================================+===========+===========+===========+
325+
| Type comments | yes | yes | yes |
326+
+------------------------------------------+-----------+-----------+-----------+
327+
| Quoted function annotations | yes | yes | |
328+
+------------------------------------------+-----------+-----------+-----------+
329+
| Quoted variable annotations | yes | | |
330+
+------------------------------------------+-----------+-----------+-----------+
331+
| Everything else | | | |
332+
+------------------------------------------+-----------+-----------+-----------+
333+
244334
.. _strict_optional:
245335

246336
Optional types and the None type

mypy/fastparse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def parse_type_comment(type_comment: str,
242242
line=line,
243243
override_column=column,
244244
assume_str_is_unicode=assume_str_is_unicode,
245-
is_type_comment=True).visit(typ.body)
245+
is_evaluated=False).visit(typ.body)
246246
return ignored, converted
247247

248248

@@ -1279,14 +1279,14 @@ def __init__(self,
12791279
line: int = -1,
12801280
override_column: int = -1,
12811281
assume_str_is_unicode: bool = True,
1282-
is_type_comment: bool = False,
1282+
is_evaluated: bool = True,
12831283
) -> None:
12841284
self.errors = errors
12851285
self.line = line
12861286
self.override_column = override_column
12871287
self.node_stack = [] # type: List[AST]
12881288
self.assume_str_is_unicode = assume_str_is_unicode
1289-
self.is_type_comment = is_type_comment
1289+
self.is_evaluated = is_evaluated
12901290

12911291
def convert_column(self, column: int) -> int:
12921292
"""Apply column override if defined; otherwise return column.
@@ -1436,7 +1436,7 @@ def visit_BinOp(self, n: ast3.BinOp) -> Type:
14361436
return UnionType([left, right],
14371437
line=self.line,
14381438
column=self.convert_column(n.col_offset),
1439-
is_evaluated=(not self.is_type_comment),
1439+
is_evaluated=self.is_evaluated,
14401440
uses_pep604_syntax=True)
14411441

14421442
def visit_NameConstant(self, n: NameConstant) -> Type:

test-data/unit/check-union-or-syntax.test

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,41 @@ reveal_type(x) # N: Revealed type is 'builtins.list[Union[builtins.int, builtin
7474
[builtins fixtures/list.pyi]
7575

7676

77-
[case testUnionOrSyntaxWithQuotedTypes]
78-
# flags: --python-version 3.10
77+
[case testUnionOrSyntaxWithQuotedFunctionTypes]
78+
# flags: --python-version 3.4
7979
from typing import Union
8080
def f(x: 'Union[int, str, None]') -> 'Union[int, None]':
8181
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str, None]'
8282
return 42
8383
reveal_type(f) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]'
8484

85-
# flags: --python-version 3.10
8685
def g(x: "int | str | None") -> "int | None":
8786
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str, None]'
8887
return 42
8988
reveal_type(g) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]'
9089

9190

91+
[case testUnionOrSyntaxWithQuotedVariableTypes]
92+
# flags: --python-version 3.6
93+
y: "int | str" = 42
94+
reveal_type(y) # N: Revealed type is 'Union[builtins.int, builtins.str]'
95+
96+
97+
[case testUnionOrSyntaxWithTypeAliasWorking]
98+
# flags: --python-version 3.10
99+
from typing import Union
100+
T = Union[int, str]
101+
x: T
102+
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str]'
103+
104+
105+
[case testUnionOrSyntaxWithTypeAliasNotAllowed]
106+
# flags: --python-version 3.9
107+
from __future__ import annotations
108+
T = int | str # E: Unsupported left operand type for | ("Type[int]")
109+
[builtins fixtures/tuple.pyi]
110+
111+
92112
[case testUnionOrSyntaxInComment]
93113
# flags: --python-version 3.6
94114
x = 1 # type: int | str

0 commit comments

Comments
 (0)