Skip to content

Commit

Permalink
bpo-41923: PEP 613: Add TypeAlias to typing module (#22532)
Browse files Browse the repository at this point in the history
This special marker annotation is intended to help in distinguishing
proper PEP 484-compliant type aliases from regular top-level variable
assignments.
  • Loading branch information
east825 authored and adorilson committed Mar 11, 2021
1 parent 4714f2b commit f80ff0e
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 2 deletions.
13 changes: 13 additions & 0 deletions Doc/library/typing.rst
Expand Up @@ -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
============

Expand Down Expand Up @@ -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
"""""""""""""

Expand Down
25 changes: 23 additions & 2 deletions Doc/whatsnew/3.10.rst
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_typing.py
Expand Up @@ -24,6 +24,7 @@
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
Expand Down Expand Up @@ -4176,6 +4177,45 @@ def test_annotated_in_other_types(self):
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__."""

Expand Down
16 changes: 16 additions & 0 deletions Lib/typing.py
Expand Up @@ -113,6 +113,7 @@
'runtime_checkable',
'Text',
'TYPE_CHECKING',
'TypeAlias',
]

# The pseudo-submodules 're' and 'io' are part of the public
Expand Down Expand Up @@ -460,6 +461,21 @@ def open_helper(file: str, mode: MODE) -> str:
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."""

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Expand Up @@ -611,6 +611,7 @@ Christoph Gohlke
Tim Golden
Yonatan Goldschmidt
Mark Gollahon
Mikhail Golubev
Guilherme Gonçalves
Tiago Gonçalves
Chris Gonnerman
Expand Down
@@ -0,0 +1 @@
Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation.

0 comments on commit f80ff0e

Please sign in to comment.