8000 Add PEP 613 TypeAlias to typing_extensions (#732) · python/typing@6d287f0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6d287f0

Browse files
authored
Add PEP 613 TypeAlias to typing_extensions (#732)
1 parent 8c78fa7 commit 6d287f0

File tree

4 files changed

+222
-1
lines changed

4 files changed

+222
-1
lines changed

typing_extensions/src_py2/test_typing_extensions.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing_extensions import Annotated, NoReturn, ClassVar, IntVar
99
from typing_extensions import ContextManager, Counter, Deque, DefaultDict
10-
from typing_extensions import NewType, overload
10+
from typing_extensions import NewType, TypeAlias, overload
1111
from typing import Dict, List
1212
import typing
1313
import typing_extensions
@@ -377,6 +377,47 @@ def test_annotated_in_other_types(self):
377377
self.assertEqual(X[int], List[Annotated[int, 5]])
378378

379379

380+
class TypeAliasTests(BaseTestCase):
381+
def test_canonical_usage(self):
382+
Alias = Employee # type: TypeAlias
383+
384+
def test_cannot_instantiate(self):
385+
with self.assertRaises(TypeError):
386+
TypeAlias()
387+
388+
def test_no_isinstance(self):
389+
with self.assertRaises(TypeError):
390+
isinstance(42, TypeAlias)
391+
392+
def test_no_issubclass(self):
393+
with self.assertRaises(TypeError):
394+
issubclass(Employee, TypeAlias)
395+
396+
with self.assertRaises(TypeError):
397+
issubclass(TypeAlias, Employee)
398+
399+
def test_cannot_subclass(self):
400+
with self.assertRaises(TypeError):
401+
class C(TypeAlias):
402+
pass
403+
404+
with self.assertRaises(TypeError):
405+
class C(type(TypeAlias)):
406+
pass
407+
408+
def test_repr(self):
409+
if hasattr(typing, 'TypeAlias'):
410+
self.assertEqual(repr(TypeAlias), 'typing.TypeAlias')
411+
self.assertEqual(repr(type(TypeAlias)), 'typing.TypeAlias')
412+
else:
413+
self.assertEqual(repr(TypeAlias), 'typing_extensions.TypeAlias')
414+
self.assertEqual(repr(type(TypeAlias)), 'typing_extensions.TypeAlias')
415+
416+
def test_cannot_subscript(self):
417+
with self.assertRaises(TypeError):
418+
TypeAlias[int]
419+
420+
380421
class AllTests(BaseTestCase):
381422

382423
def test_typing_extensions_includes_standard(self):

typing_extensions/src_py2/typing_extensions.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,43 @@ class Annotated(object):
241241
__slots__ = ()
242242

243243

244+
class _TypeAliasMeta(typing.TypingMeta):
245+
"""Metaclass for TypeAlias"""
246+
247+
def __new__(cls, name, bases, namespace):
248+
cls.assert_no_subclassing(bases)
249+
self = super(_TypeAliasMeta, cls).__new__(cls, name, bases, namespace)
250+
return self
251+
252+
def __repr__(self):
253+
return 'typing_extensions.TypeAlias'
254+
255+
256+
class _TypeAliasBase(typing._FinalTypingBase):
257+
"""Special marker indicating that an assignment should
258+
be recognized as a proper type alias definition by type
259+
checkers.
260+
261+
For example::
262+
263+
Predicate = Callable[..., bool] # type: TypeAlias
264+
265+
It's invalid when used anywhere except as in the example above.
266+
"""
267+
__metaclass__ = _TypeAliasMeta
268+
__slots__ = ()
269+
270+
def __instancecheck__(self, obj):
271+
raise TypeError("TypeAlias cannot be used with isinstance().")
272+
273+
def __subclasscheck__(self, cls):
274+
raise TypeError("TypeAlias cannot be used with issubclass().")
275+
276+
def __repr__(self):
277+
return 'typing_extensions.TypeAlias'
278+
279+
280+
TypeAlias = _TypeAliasBase(_root=True)
281+
244282
# This alias exists for backwards compatibility.
245283
runtime = runtime_checkable

typing_extensions/src_py3/test_typing_extensions.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from typing import Generic
1414
from typing import no_type_check
1515
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict
16+
from typing_extensions import TypeAlias
1617
try:
1718
from typing_extensions import Protocol, runtime, runtime_checkable
1819
except ImportError:
@@ -1826,6 +1827,52 @@ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]":
18261827
)
18271828

18281829

1830+
class TypeAliasTests(BaseTestCase):
1831+
@skipUnless(PY36, 'Python 3.6 required')
1832+
def test_canonical_usage_with_variable_annotation(self):
1833+
ns = {}
1834+
exec('Alias: TypeAlias = Employee', globals(), ns)
1835+
1836+
def test_canonical_usage_with_type_comment(self):
1837+
Alias = Employee # type: TypeAlias
1838+
1839+
def test_cannot_instantiate(self):
1840+
with self.assertRaises(TypeError):
1841+
TypeAlias()
1842+
1843+
def test_no_isinstance(self):
1844+
with self.assertRaises(TypeError):
1845+
isinstance(42, TypeAlias)
1846+
1847+
def test_no_issubclass(self):
1848+
with self.assertRaises(TypeError):
1849+
issubclass(Employee, TypeAlias)
1850+
1851+
if SUBCLASS_CHECK_FORBIDDEN:
1852+
with self.assertRaises(TypeError):
1853+
issubclass(TypeAlias, Employee)
1854+
1855+
def test_cannot_subclass(self):
1856+
with self.assertRaises(TypeError):
1857+
class C(TypeAlias):
1858+
pass
1859+
1860+
if SUBCLASS_CHECK_FORBIDDEN:
1861+
with self.assertRaises(TypeError):
1862+
class C(type(TypeAlias)):
1863+
pass
1864+
1865+
def test_repr(self):
1866+
if hasattr(typing, 'TypeAlias'):
1867+
self.assertEqual(repr(TypeAlias), 'typing.TypeAlias')
1868+
else:
1869+
self.assertEqual(repr(TypeAlias), 'typing_extensions.TypeAlias')
1870+
1871+
def test_cannot_subscript(self):
1872+
with self.assertRaises(TypeError):
1873+
TypeAlias[int]
1874+
1875+
18291876
class AllTests(BaseTestCase):
18301877

18311878
def test_typing_extensions_includes_standard(self):

typing_extensions/src_py3/typing_extensions.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,3 +2071,98 @@ def get_args(tp):
20712071
res = (list(res[:-1]), res[-1])
20722072
return res
20732073
return ()
2074+
2075+
2076+
if hasattr(typing, 'TypeAlias'):
2077+
TypeAlias = typing.TypeAlias
2078+
elif sys.version_info[:2] >= (3, 9):
2079+
class _TypeAliasForm(typing._SpecialForm, _root=True):
2080+
def __repr__(self):
2081+
return 'typing_extensions.' + self._name
2082+
2083+
@_TypeAliasForm
2084+
def TypeAlias(self, parameters):
2085+
"""Special marker indicating that an assignment should
2086+
be recognized as a proper type alias definition by type
2087+
checkers.
2088+
2089+
For example::
2090+
2091+
Predicate: TypeAlias = Callable[..., bool]
2092+
2093+
It's invalid when used anywhere except as in the example above.
2094+
"""
2095+
raise TypeError("{} is not subscriptable".format(self))
2096+
2097+
elif sys.version_info[:2] >= (3, 7):
2098+
class _TypeAliasForm(typing._SpecialForm, _root=True):
2099+
def __repr__(self):
2100+
return 'typing_extensions.' + self._name
2101+
2102+
TypeAlias = _TypeAliasForm('TypeAlias',
2103+
doc="""Special marker indicating that an assignment should
2104+
be recognized as a proper type alias definition by type
2105+
checkers.
2106+
2107+
For example::
2108+
2109+
Predicate: TypeAlias = Callable[..., bool]
2110+
2111+
It's invalid when used anywhere except as in the example
2112+
above.""")
2113+
2114+
elif hasattr(typing, '_FinalTypingBase'):
2115+
class _TypeAliasMeta(typing.TypingMeta):
2116+
"""Metaclass for TypeAlias"""
2117+
2118+
def __repr__(self):
2119+
return 'typing_extensions.TypeAlias'
2120+
2121+
class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True):
2122+
"""Special marker indicating that an assignment should
2123+
be recognized as a proper type alias definition by type
2124+
checkers.
2125+
2126+
For example::
2127+
2128+
Predicate: TypeAlias = Callable[..., bool]
2129+
2130+
It's invalid when used anywhere except as in the example above.
2131+
"""
2132+
__slots__ = ()
2133+
2134+
def __instancecheck__(self, obj):
2135+
raise TypeError("TypeAlias cannot be used with isinstance().")
2136+
2137+
def __subclasscheck__(self, cls):
2138+
raise TypeError("TypeAlias cannot be used with issubclass().")
2139+
2140+
def __repr__(self):
2141+
return 'typing_extensions.TypeAlias'
2142+
2143+
TypeAlias = _TypeAliasBase(_root=True)
2144+
else:
2145+
class _TypeAliasMeta(typing.TypingMeta):
2146+
"""Metaclass for TypeAlias"""
2147+
2148+
def __instancecheck__(self, obj):
2149+
raise TypeError("TypeAlias cannot be used with isinstance().")
2150+
2151+
def __subclasscheck__(self, cls):
2152+
raise TypeError("TypeAlias cannot be used with issubclass().")
2153+
2154+
def __call__(self, *args, **kwargs):
2155+
raise TypeError("Cannot instantiate TypeAlias")
2156+
2157+
class TypeAlias(metaclass=_TypeAliasMeta, _root=True):
2158+
"""Special marker indicating that an assignment should
2159+
be recognized as a proper type alias definition by type
2160+
checkers.
2161+
2162+
For example::
2163+
2164+
Predicate: TypeAlias = Callable[..., bool]
2165+
2166+
It's invalid when used anywhere except as in the example above.
2167+
"""
2168+
__slots__ = ()

0 commit comments

Comments
 (0)
0