bpo-41923: PEP 613: Add TypeAlias to typing module (#22532)

This special marker annotation is intended to help in distinguishing
proper PEP 484-compliant type aliases from regular top-level variable
assignments.
This commit is contained in:
Mikhail Golubev 2020-10-08 00:44:31 +03:00 committed by GitHub
parent f90dc36c15
commit 4f3c25043d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 2 deletions

View File

@ -34,6 +34,8 @@ In the function ``greeting``, the argument ``name`` is expected to be of type
:class:`str` and the return type :class:`str`. Subtypes are accepted as :class:`str` and the return type :class:`str`. Subtypes are accepted as
arguments. arguments.
.. _type-aliases:
Type aliases Type aliases
============ ============
@ -489,6 +491,17 @@ These can be used as types in annotations and do not support ``[]``.
.. versionadded:: 3.5.4 .. versionadded:: 3.5.4
.. versionadded:: 3.6.2 .. versionadded:: 3.6.2
.. data:: TypeAlias
Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
For example::
from typing import TypeAlias
Factors: TypeAlias = list[int]
.. versionadded:: 3.10
Special forms Special forms
""""""""""""" """""""""""""

View File

@ -99,8 +99,29 @@ in :issue:`38605`.)
* :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used * :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used
to require that all the iterables have an equal length. to require that all the iterables have an equal length.
PEP604: New Type Operator PEP 613: TypeAlias Annotation
------------------------- -----------------------------
:pep:`484` introduced the concept of type aliases, only requiring them to be
top-level unannotated assignments. This simplicity sometimes made it difficult
for type checkers to distinguish between type aliases and ordinary assignments,
especially when forward references or invalid types were involved. Compare::
StrCache = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
Now the :mod:`typing` module has a special annotation :data:`TypeAlias` to
declare type aliases more explicitly::
StrCache: TypeAlias = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
See :pep:`613` for more details.
(Contributed by Mikhail Golubev in :issue:`41923`.)
PEP604: New Type Union Operator
-------------------------------
A new type union operator was introduced which enables the syntax ``X | Y``. 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 This provides a cleaner way of expressing 'either type X or type Y' instead of

View File

@ -24,6 +24,7 @@ from typing import NamedTuple, TypedDict
from typing import IO, TextIO, BinaryIO from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match from typing import Pattern, Match
from typing import Annotated, ForwardRef from typing import Annotated, ForwardRef
from typing import TypeAlias
import abc import abc
import typing import typing
import weakref import weakref
@ -4176,6 +4177,45 @@ class AnnotatedTests(BaseTestCase):
self.assertEqual(X[int], List[Annotated[int, 5]]) self.assertEqual(X[int], List[Annotated[int, 5]])
class TypeAliasTests(BaseTestCase):
def test_canonical_usage_with_variable_annotation(self):
Alias: TypeAlias = Employee
def test_canonical_usage_with_type_comment(self):
Alias = Employee # type: TypeAlias
def test_cannot_instantiate(self):
with self.assertRaises(TypeError):
TypeAlias()
def test_no_isinstance(self):
with self.assertRaises(TypeError):
isinstance(42, TypeAlias)
def test_no_issubclass(self):
with self.assertRaises(TypeError):
issubclass(Employee, TypeAlias)
with self.assertRaises(TypeError):
issubclass(TypeAlias, Employee)
def test_cannot_subclass(self):
with self.assertRaises(TypeError):
class C(TypeAlias):
pass
with self.assertRaises(TypeError):
class C(type(TypeAlias)):
pass
def test_repr(self):
self.assertEqual(repr(TypeAlias), 'typing.TypeAlias')
def test_cannot_subscript(self):
with self.assertRaises(TypeError):
TypeAlias[int]
class AllTests(BaseTestCase): class AllTests(BaseTestCase):
"""Tests for __all__.""" """Tests for __all__."""

View File

@ -113,6 +113,7 @@ __all__ = [
'runtime_checkable', 'runtime_checkable',
'Text', 'Text',
'TYPE_CHECKING', 'TYPE_CHECKING',
'TypeAlias',
] ]
# The pseudo-submodules 're' and 'io' are part of the public # The pseudo-submodules 're' and 'io' are part of the public
@ -460,6 +461,21 @@ def Literal(self, parameters):
return _GenericAlias(self, parameters) return _GenericAlias(self, parameters)
@_SpecialForm
def TypeAlias(self, parameters):
"""Special marker indicating that an assignment should
be recognized as a proper type alias definition by type
checkers.
For example::
Predicate: TypeAlias = Callable[..., bool]
It's invalid when used anywhere except as in the example above.
"""
raise TypeError(f"{self} is not subscriptable")
class ForwardRef(_Final, _root=True): class ForwardRef(_Final, _root=True):
"""Internal wrapper to hold a forward reference.""" """Internal wrapper to hold a forward reference."""

View File

@ -611,6 +611,7 @@ Christoph Gohlke
Tim Golden Tim Golden
Yonatan Goldschmidt Yonatan Goldschmidt
Mark Gollahon Mark Gollahon
Mikhail Golubev
Guilherme Gonçalves Guilherme Gonçalves
Tiago Gonçalves Tiago Gonçalves
Chris Gonnerman Chris Gonnerman

View File

@ -0,0 +1 @@
Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation.