From: Mikhail Golubev Date: Wed, 7 Oct 2020 21:44:31 +0000 (+0300) Subject: bpo-41923: PEP 613: Add TypeAlias to typing module (#22532) X-Git-Tag: v3.10.0a2~233 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4f3c25043d651a13c41cffcee703f7d5cb677cc7;p=thirdparty%2FPython%2Fcpython.git 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. --- diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a72632e61b07..f4b2718cdc2f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -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 arguments. +.. _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.6.2 +.. data:: TypeAlias + + Special annotation for explicitly declaring a :ref:`type alias `. + For example:: + + from typing import TypeAlias + + Factors: TypeAlias = list[int] + + .. versionadded:: 3.10 + Special forms """"""""""""" diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 2bcdba69957b..4ada4be3b667 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -99,8 +99,29 @@ in :issue:`38605`.) * :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 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``. This provides a cleaner way of expressing 'either type X or type Y' instead of diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4bef42f4f32f..57dd73c529da 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -24,6 +24,7 @@ from typing import NamedTuple, TypedDict from typing import IO, TextIO, BinaryIO from typing import Pattern, Match from typing import Annotated, ForwardRef +from typing import TypeAlias import abc import typing import weakref @@ -4176,6 +4177,45 @@ class AnnotatedTests(BaseTestCase): 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): """Tests for __all__.""" diff --git a/Lib/typing.py b/Lib/typing.py index 4cf33c1ae926..0f457ab1f56d 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -113,6 +113,7 @@ __all__ = [ 'runtime_checkable', 'Text', 'TYPE_CHECKING', + 'TypeAlias', ] # The pseudo-submodules 're' and 'io' are part of the public @@ -460,6 +461,21 @@ def Literal(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): """Internal wrapper to hold a forward reference.""" diff --git a/Misc/ACKS b/Misc/ACKS index 08449fe08269..7d445c572145 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -611,6 +611,7 @@ Christoph Gohlke Tim Golden Yonatan Goldschmidt Mark Gollahon +Mikhail Golubev Guilherme Gonçalves Tiago Gonçalves Chris Gonnerman diff --git a/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst b/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst new file mode 100644 index 000000000000..dd9a1f709f33 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst @@ -0,0 +1 @@ +Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation.