summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikhail Golubev <qsolo825@gmail.com>2020-10-07 21:44:31 (GMT)
committerGitHub <noreply@github.com>2020-10-07 21:44:31 (GMT)
commit4f3c25043d651a13c41cffcee703f7d5cb677cc7 (patch)
tree08093971ec6aefd8b762e8da9d20434c09a3c3ae
parentf90dc36c15d7fee0efaf6d39e97be0bdf2683e93 (diff)
downloadcpython-4f3c25043d651a13c41cffcee703f7d5cb677cc7.zip
cpython-4f3c25043d651a13c41cffcee703f7d5cb677cc7.tar.gz
cpython-4f3c25043d651a13c41cffcee703f7d5cb677cc7.tar.bz2
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.
-rw-r--r--Doc/library/typing.rst13
-rw-r--r--Doc/whatsnew/3.10.rst25
-rw-r--r--Lib/test/test_typing.py40
-rw-r--r--Lib/typing.py16
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Library/2020-10-03-23-14-50.bpo-41923.Buonw9.rst1
6 files changed, 94 insertions, 2 deletions
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index a72632e..f4b2718 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 <type-aliases>`.
+ 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 2bcdba6..4ada4be 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 4bef42f..57dd73c 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 4cf33c1..0f457ab 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 08449fe..7d445c5 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 0000000..dd9a1f7
--- /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.