2016-04-18 08:52:05 +08:00
|
|
|
import contextlib
|
2016-05-18 23:35:00 +08:00
|
|
|
import collections
|
2015-11-19 13:12:58 +08:00
|
|
|
import pickle
|
2015-05-23 01:14:11 +08:00
|
|
|
import re
|
|
|
|
import sys
|
2016-06-09 02:19:11 +08:00
|
|
|
from unittest import TestCase, main, skipUnless, SkipTest
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
from typing import Any
|
|
|
|
from typing import TypeVar, AnyStr
|
|
|
|
from typing import T, KT, VT # Not in __all__.
|
|
|
|
from typing import Union, Optional
|
2016-09-28 06:20:12 +08:00
|
|
|
from typing import Tuple, List, MutableMapping
|
2015-05-23 01:14:11 +08:00
|
|
|
from typing import Callable
|
2016-09-12 06:34:56 +08:00
|
|
|
from typing import Generic, ClassVar
|
2015-05-23 01:14:11 +08:00
|
|
|
from typing import cast
|
|
|
|
from typing import get_type_hints
|
|
|
|
from typing import no_type_check, no_type_check_decorator
|
2016-05-25 07:38:22 +08:00
|
|
|
from typing import Type
|
2016-06-09 02:19:11 +08:00
|
|
|
from typing import NewType
|
2015-05-23 01:14:11 +08:00
|
|
|
from typing import NamedTuple
|
|
|
|
from typing import IO, TextIO, BinaryIO
|
|
|
|
from typing import Pattern, Match
|
2016-10-09 11:27:22 +08:00
|
|
|
import abc
|
2015-05-23 01:14:11 +08:00
|
|
|
import typing
|
2016-09-28 06:20:12 +08:00
|
|
|
try:
|
|
|
|
import collections.abc as collections_abc
|
|
|
|
except ImportError:
|
|
|
|
import collections as collections_abc # Fallback for PY3.2.
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class BaseTestCase(TestCase):
|
|
|
|
|
|
|
|
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
|
|
|
|
if not issubclass(cls, class_or_tuple):
|
|
|
|
message = '%r is not a subclass of %r' % (cls, class_or_tuple)
|
|
|
|
if msg is not None:
|
|
|
|
message += ' : %s' % msg
|
|
|
|
raise self.failureException(message)
|
|
|
|
|
|
|
|
def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
|
|
|
|
if issubclass(cls, class_or_tuple):
|
|
|
|
message = '%r is a subclass of %r' % (cls, class_or_tuple)
|
|
|
|
if msg is not None:
|
|
|
|
message += ' : %s' % msg
|
|
|
|
raise self.failureException(message)
|
|
|
|
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
class Employee:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Manager(Employee):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Founder(Employee):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ManagingFounder(Manager, Founder):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class AnyTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_any_instance_type_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance(42, Any)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
def test_any_subclass_type_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Employee, Any)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Any, Employee)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(Any), 'typing.Any')
|
|
|
|
|
|
|
|
def test_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(42, Any)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Any[int] # Any is not a generic type.
|
|
|
|
|
|
|
|
def test_cannot_subclass(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class A(Any):
|
|
|
|
pass
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class A(type(Any)):
|
|
|
|
pass
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_cannot_instantiate(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Any()
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(Any)()
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_cannot_subscript(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Any[int]
|
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
def test_any_works_with_alias(self):
|
2015-05-23 01:14:11 +08:00
|
|
|
# These expressions must simply not fail.
|
|
|
|
typing.Match[Any]
|
|
|
|
typing.Pattern[Any]
|
|
|
|
typing.IO[Any]
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class TypeVarTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basic_plain(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
# T equals itself.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(T, T)
|
2015-08-05 18:11:06 +08:00
|
|
|
# T is an instance of TypeVar
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(T, TypeVar)
|
2015-08-05 18:11:06 +08:00
|
|
|
|
|
|
|
def test_typevar_instance_type_error(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance(42, T)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
def test_typevar_subclass_type_error(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(int, T)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(T, int)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_constrained_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
X = TypeVar('X', int)
|
2016-04-05 23:28:52 +08:00
|
|
|
X
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_union_unique(self):
|
|
|
|
X = TypeVar('X')
|
|
|
|
Y = TypeVar('Y')
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotEqual(X, Y)
|
|
|
|
self.assertEqual(Union[X], X)
|
|
|
|
self.assertNotEqual(Union[X], Union[X, Y])
|
|
|
|
self.assertEqual(Union[X, X], X)
|
|
|
|
self.assertNotEqual(Union[X, int], Union[X])
|
|
|
|
self.assertNotEqual(Union[X, int], Union[int])
|
|
|
|
self.assertEqual(Union[X, int].__union_params__, (X, int))
|
|
|
|
self.assertEqual(Union[X, int].__union_set_params__, {X, int})
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_union_constrained(self):
|
|
|
|
A = TypeVar('A', str, bytes)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotEqual(Union[A, str], Union[A])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(T), '~T')
|
|
|
|
self.assertEqual(repr(KT), '~KT')
|
|
|
|
self.assertEqual(repr(VT), '~VT')
|
|
|
|
self.assertEqual(repr(AnyStr), '~AnyStr')
|
|
|
|
T_co = TypeVar('T_co', covariant=True)
|
|
|
|
self.assertEqual(repr(T_co), '+T_co')
|
|
|
|
T_contra = TypeVar('T_contra', contravariant=True)
|
|
|
|
self.assertEqual(repr(T_contra), '-T_contra')
|
|
|
|
|
|
|
|
def test_no_redefinition(self):
|
|
|
|
self.assertNotEqual(TypeVar('T'), TypeVar('T'))
|
|
|
|
self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str))
|
|
|
|
|
|
|
|
def test_cannot_subclass_vars(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class V(TypeVar('T')):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cannot_subclass_var_itself(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class V(TypeVar):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cannot_instantiate_vars(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
TypeVar('A')()
|
|
|
|
|
|
|
|
def test_bound_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
TypeVar('X', bound=42)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
TypeVar('X', str, float, bound=Employee)
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class UnionTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
u = Union[int, float]
|
|
|
|
self.assertNotEqual(u, Union)
|
2016-09-28 06:20:12 +08:00
|
|
|
|
|
|
|
def test_subclass_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(int, Union)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Union, int)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(int, Union[int, str])
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Union[int, str], int)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_union_any(self):
|
|
|
|
u = Union[Any]
|
|
|
|
self.assertEqual(u, Any)
|
2016-10-03 23:40:50 +08:00
|
|
|
u1 = Union[int, Any]
|
|
|
|
u2 = Union[Any, int]
|
|
|
|
u3 = Union[Any, object]
|
|
|
|
self.assertEqual(u1, u2)
|
|
|
|
self.assertNotEqual(u1, Any)
|
|
|
|
self.assertNotEqual(u2, Any)
|
|
|
|
self.assertNotEqual(u3, Any)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_union_object(self):
|
|
|
|
u = Union[object]
|
|
|
|
self.assertEqual(u, object)
|
|
|
|
u = Union[int, object]
|
|
|
|
self.assertEqual(u, object)
|
|
|
|
u = Union[object, int]
|
|
|
|
self.assertEqual(u, object)
|
|
|
|
|
|
|
|
def test_unordered(self):
|
|
|
|
u1 = Union[int, float]
|
|
|
|
u2 = Union[float, int]
|
|
|
|
self.assertEqual(u1, u2)
|
|
|
|
|
|
|
|
def test_single_class_disappears(self):
|
|
|
|
t = Union[Employee]
|
|
|
|
self.assertIs(t, Employee)
|
|
|
|
|
|
|
|
def test_base_class_disappears(self):
|
|
|
|
u = Union[Employee, Manager, int]
|
|
|
|
self.assertEqual(u, Union[int, Employee])
|
|
|
|
u = Union[Manager, int, Employee]
|
|
|
|
self.assertEqual(u, Union[int, Employee])
|
|
|
|
u = Union[Employee, Manager]
|
|
|
|
self.assertIs(u, Employee)
|
|
|
|
|
|
|
|
def test_union_union(self):
|
|
|
|
u = Union[int, float]
|
|
|
|
v = Union[u, Employee]
|
|
|
|
self.assertEqual(v, Union[int, float, Employee])
|
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(Union), 'typing.Union')
|
|
|
|
u = Union[Employee, int]
|
|
|
|
self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__)
|
|
|
|
u = Union[int, Employee]
|
|
|
|
self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__)
|
|
|
|
|
|
|
|
def test_cannot_subclass(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class C(Union):
|
|
|
|
pass
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class C(type(Union)):
|
|
|
|
pass
|
2015-05-23 01:14:11 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class C(Union[int, str]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cannot_instantiate(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Union()
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(Union)()
|
2015-05-23 01:14:11 +08:00
|
|
|
u = Union[int, float]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
u()
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(u)()
|
|
|
|
|
|
|
|
def test_union_generalization(self):
|
|
|
|
self.assertFalse(Union[str, typing.Iterable[int]] == str)
|
|
|
|
self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
|
|
|
|
self.assertTrue(Union[str, typing.Iterable] == typing.Iterable)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_optional(self):
|
|
|
|
o = Optional[int]
|
|
|
|
u = Union[int, None]
|
|
|
|
self.assertEqual(o, u)
|
|
|
|
|
|
|
|
def test_empty(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Union[()]
|
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_union_instance_type_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance(42, Union[int, str])
|
2015-10-20 05:55:47 +08:00
|
|
|
|
|
|
|
def test_union_str_pattern(self):
|
|
|
|
# Shouldn't crash; see http://bugs.python.org/issue25390
|
|
|
|
A = Union[str, Pattern]
|
2016-04-05 23:28:52 +08:00
|
|
|
A
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-06-09 02:19:11 +08:00
|
|
|
def test_etree(self):
|
|
|
|
# See https://github.com/python/typing/issues/229
|
|
|
|
# (Only relevant for Python 2.)
|
|
|
|
try:
|
|
|
|
from xml.etree.cElementTree import Element
|
|
|
|
except ImportError:
|
|
|
|
raise SkipTest("cElementTree not found")
|
|
|
|
Union[Element, str] # Shouldn't crash
|
|
|
|
|
|
|
|
def Elem(*args):
|
|
|
|
return Element(*args)
|
|
|
|
|
|
|
|
Union[Elem, str] # Nor should this
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class TupleTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Tuple[int, str], Tuple)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(Tuple, Tuple[int, str])
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(tuple, Tuple[int, str])
|
|
|
|
|
|
|
|
class TP(tuple): ...
|
2015-05-23 01:14:11 +08:00
|
|
|
self.assertTrue(issubclass(tuple, Tuple))
|
2016-09-28 06:20:12 +08:00
|
|
|
self.assertTrue(issubclass(TP, Tuple))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-18 22:37:41 +08:00
|
|
|
def test_equality(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Tuple[int], Tuple[int])
|
|
|
|
self.assertEqual(Tuple[int, ...], Tuple[int, ...])
|
|
|
|
self.assertNotEqual(Tuple[int], Tuple[int, int])
|
|
|
|
self.assertNotEqual(Tuple[int], Tuple[int, ...])
|
2016-04-18 22:37:41 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_tuple_subclass(self):
|
|
|
|
class MyTuple(tuple):
|
|
|
|
pass
|
|
|
|
self.assertTrue(issubclass(MyTuple, Tuple))
|
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_tuple_instance_type_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance((0, 0), Tuple[int, int])
|
2016-09-28 06:20:12 +08:00
|
|
|
self.assertIsInstance((0, 0), Tuple)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(Tuple), 'typing.Tuple')
|
2016-06-09 02:19:11 +08:00
|
|
|
self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]')
|
2015-05-23 01:14:11 +08:00
|
|
|
self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]')
|
|
|
|
self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]')
|
|
|
|
|
|
|
|
def test_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(42, Tuple)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(42, Tuple[int])
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class CallableTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_self_subclass(self):
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int]))
|
|
|
|
self.assertTrue(issubclass(type(lambda x: x), Callable))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_eq_hash(self):
|
|
|
|
self.assertEqual(Callable[[int], int], Callable[[int], int])
|
|
|
|
self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1)
|
|
|
|
self.assertNotEqual(Callable[[int], int], Callable[[int], str])
|
|
|
|
self.assertNotEqual(Callable[[int], int], Callable[[str], int])
|
|
|
|
self.assertNotEqual(Callable[[int], int], Callable[[int, int], int])
|
|
|
|
self.assertNotEqual(Callable[[int], int], Callable[[], int])
|
|
|
|
self.assertNotEqual(Callable[[int], int], Callable)
|
|
|
|
|
|
|
|
def test_cannot_subclass(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
|
|
|
|
class C(Callable):
|
|
|
|
pass
|
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
|
|
|
|
class C(type(Callable)):
|
|
|
|
pass
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
|
|
|
|
class C(Callable[[int], int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cannot_instantiate(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Callable()
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(Callable)()
|
2015-05-23 01:14:11 +08:00
|
|
|
c = Callable[[int], str]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
c()
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(c)()
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_callable_instance_works(self):
|
2015-09-05 03:15:54 +08:00
|
|
|
def f():
|
|
|
|
pass
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(f, Callable)
|
|
|
|
self.assertNotIsInstance(None, Callable)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_callable_instance_type_error(self):
|
2015-09-05 03:15:54 +08:00
|
|
|
def f():
|
|
|
|
pass
|
2015-08-05 18:11:06 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(f, Callable[[], None])
|
2015-08-05 18:11:06 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(f, Callable[[], Any])
|
2015-08-05 18:11:06 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotIsInstance(None, Callable[[], None])
|
2015-08-05 18:11:06 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotIsInstance(None, Callable[[], Any])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
ct0 = Callable[[], bool]
|
|
|
|
self.assertEqual(repr(ct0), 'typing.Callable[[], bool]')
|
|
|
|
ct2 = Callable[[str, float], int]
|
|
|
|
self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]')
|
|
|
|
ctv = Callable[..., str]
|
|
|
|
self.assertEqual(repr(ctv), 'typing.Callable[..., str]')
|
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_callable_with_ellipsis(self):
|
|
|
|
|
|
|
|
def foo(a: Callable[..., T]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
|
|
|
{'a': Callable[..., T]})
|
|
|
|
|
2016-08-24 02:01:50 +08:00
|
|
|
def test_ellipsis_in_generic(self):
|
|
|
|
# Shouldn't crash; see https://github.com/python/typing/issues/259
|
|
|
|
typing.List[Callable[..., str]]
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
XK = TypeVar('XK', str, bytes)
|
|
|
|
XV = TypeVar('XV')
|
|
|
|
|
|
|
|
|
|
|
|
class SimpleMapping(Generic[XK, XV]):
|
|
|
|
|
|
|
|
def __getitem__(self, key: XK) -> XV:
|
|
|
|
...
|
|
|
|
|
|
|
|
def __setitem__(self, key: XK, value: XV):
|
|
|
|
...
|
|
|
|
|
|
|
|
def get(self, key: XK, default: XV = None) -> XV:
|
|
|
|
...
|
|
|
|
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
class MySimpleMapping(SimpleMapping[XK, XV]):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.store = {}
|
|
|
|
|
|
|
|
def __getitem__(self, key: str):
|
|
|
|
return self.store[key]
|
|
|
|
|
|
|
|
def __setitem__(self, key: str, value):
|
|
|
|
self.store[key] = value
|
|
|
|
|
|
|
|
def get(self, key: str, default=None):
|
|
|
|
try:
|
|
|
|
return self.store[key]
|
|
|
|
except KeyError:
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class ProtocolTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_int(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(int, typing.SupportsInt)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsInt)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_float(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(float, typing.SupportsFloat)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsFloat)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_complex(self):
|
|
|
|
|
|
|
|
# Note: complex itself doesn't have __complex__.
|
|
|
|
class C:
|
|
|
|
def __complex__(self):
|
|
|
|
return 0j
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(C, typing.SupportsComplex)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsComplex)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_bytes(self):
|
|
|
|
|
|
|
|
# Note: bytes itself doesn't have __bytes__.
|
|
|
|
class B:
|
|
|
|
def __bytes__(self):
|
|
|
|
return b''
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(B, typing.SupportsBytes)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsBytes)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_abs(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(float, typing.SupportsAbs)
|
|
|
|
self.assertIsSubclass(int, typing.SupportsAbs)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsAbs)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_supports_round(self):
|
2016-04-05 23:28:52 +08:00
|
|
|
issubclass(float, typing.SupportsRound)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(float, typing.SupportsRound)
|
|
|
|
self.assertIsSubclass(int, typing.SupportsRound)
|
|
|
|
self.assertNotIsSubclass(str, typing.SupportsRound)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_reversible(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(list, typing.Reversible)
|
|
|
|
self.assertNotIsSubclass(int, typing.Reversible)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_protocol_instance_type_error(self):
|
|
|
|
with self.assertRaises(TypeError):
|
2016-04-05 23:28:52 +08:00
|
|
|
isinstance(0, typing.SupportsAbs)
|
2015-08-05 18:11:06 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class GenericTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
X = SimpleMapping[str, Any]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(X.__parameters__, ())
|
2015-05-23 01:14:11 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-05 23:28:52 +08:00
|
|
|
X[str]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
X[str, str]
|
|
|
|
Y = SimpleMapping[XK, str]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Y.__parameters__, (XK,))
|
2016-04-05 23:28:52 +08:00
|
|
|
Y[str]
|
2015-05-23 01:14:11 +08:00
|
|
|
with self.assertRaises(TypeError):
|
2016-04-05 23:28:52 +08:00
|
|
|
Y[str, str]
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
def test_generic_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance([], List[int])
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(list, List[int])
|
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_init(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
S = TypeVar('S')
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Generic[T, T]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Generic[T, S, T]
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(SimpleMapping),
|
2016-04-05 23:28:52 +08:00
|
|
|
__name__ + '.' + 'SimpleMapping<~XK, ~XV>')
|
2015-05-23 01:14:11 +08:00
|
|
|
self.assertEqual(repr(MySimpleMapping),
|
2016-04-05 23:28:52 +08:00
|
|
|
__name__ + '.' + 'MySimpleMapping<~XK, ~XV>')
|
|
|
|
|
|
|
|
def test_chain_repr(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
S = TypeVar('S')
|
|
|
|
|
|
|
|
class C(Generic[T]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
X = C[Tuple[S, T]]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(X, C[Tuple[S, T]])
|
|
|
|
self.assertNotEqual(X, C[Tuple[T, S]])
|
2016-04-05 23:28:52 +08:00
|
|
|
|
|
|
|
Y = X[T, int]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Y, X[T, int])
|
|
|
|
self.assertNotEqual(Y, X[S, int])
|
|
|
|
self.assertNotEqual(Y, X[T, str])
|
2016-04-05 23:28:52 +08:00
|
|
|
|
|
|
|
Z = Y[str]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Z, Y[str])
|
|
|
|
self.assertNotEqual(Z, Y[int])
|
|
|
|
self.assertNotEqual(Z, Y[T])
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertTrue(str(Z).endswith(
|
|
|
|
'.C<~T>[typing.Tuple[~S, ~T]]<~S, ~T>[~T, int]<~T>[str]'))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
def test_dict(self):
|
|
|
|
T = TypeVar('T')
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
class B(Generic[T]):
|
|
|
|
pass
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
b = B()
|
|
|
|
b.foo = 42
|
|
|
|
self.assertEqual(b.__dict__, {'foo': 42})
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
class C(B[int]):
|
|
|
|
pass
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
c = C()
|
|
|
|
c.bar = 'abc'
|
|
|
|
self.assertEqual(c.__dict__, {'bar': 'abc'})
|
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
def test_false_subclasses(self):
|
|
|
|
class MyMapping(MutableMapping[str, str]): pass
|
|
|
|
self.assertNotIsInstance({}, MyMapping)
|
|
|
|
self.assertNotIsSubclass(dict, MyMapping)
|
|
|
|
|
2016-10-03 23:40:50 +08:00
|
|
|
def test_abc_bases(self):
|
|
|
|
class MM(MutableMapping[str, str]):
|
2016-09-28 06:20:12 +08:00
|
|
|
def __getitem__(self, k):
|
|
|
|
return None
|
|
|
|
def __setitem__(self, k, v):
|
|
|
|
pass
|
|
|
|
def __delitem__(self, k):
|
|
|
|
pass
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(())
|
|
|
|
def __len__(self):
|
|
|
|
return 0
|
2016-10-03 23:40:50 +08:00
|
|
|
# this should just work
|
|
|
|
MM().update()
|
|
|
|
self.assertIsInstance(MM(), collections_abc.MutableMapping)
|
|
|
|
self.assertIsInstance(MM(), MutableMapping)
|
|
|
|
self.assertNotIsInstance(MM(), List)
|
|
|
|
self.assertNotIsInstance({}, MM)
|
|
|
|
|
|
|
|
def test_multiple_bases(self):
|
|
|
|
class MM1(MutableMapping[str, str], collections_abc.MutableMapping):
|
|
|
|
pass
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
# consistent MRO not possible
|
|
|
|
class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
|
2016-09-28 06:20:12 +08:00
|
|
|
pass
|
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
def test_pickle(self):
|
2016-04-05 23:28:52 +08:00
|
|
|
global C # pickle wants to reference the class by name
|
2015-11-19 13:12:58 +08:00
|
|
|
T = TypeVar('T')
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
class B(Generic[T]):
|
|
|
|
pass
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
class C(B[int]):
|
|
|
|
pass
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-11-19 13:12:58 +08:00
|
|
|
c = C()
|
|
|
|
c.foo = 42
|
|
|
|
c.bar = 'abc'
|
2015-11-21 00:33:02 +08:00
|
|
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
|
|
|
z = pickle.dumps(c, proto)
|
|
|
|
x = pickle.loads(z)
|
|
|
|
self.assertEqual(x.foo, 42)
|
|
|
|
self.assertEqual(x.bar, 'abc')
|
|
|
|
self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'})
|
2015-11-19 13:12:58 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
B = SimpleMapping[XK, Any]
|
|
|
|
|
|
|
|
class C(Generic[B]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_repr_2(self):
|
|
|
|
PY32 = sys.version_info[:2] < (3, 3)
|
|
|
|
|
|
|
|
class C(Generic[T]):
|
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(C.__module__, __name__)
|
2015-05-23 01:14:11 +08:00
|
|
|
if not PY32:
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(C.__qualname__,
|
|
|
|
'GenericTests.test_repr_2.<locals>.C')
|
|
|
|
self.assertEqual(repr(C).split('.')[-1], 'C<~T>')
|
2015-05-23 01:14:11 +08:00
|
|
|
X = C[int]
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(X.__module__, __name__)
|
2015-05-23 01:14:11 +08:00
|
|
|
if not PY32:
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(X.__qualname__, 'C')
|
|
|
|
self.assertEqual(repr(X).split('.')[-1], 'C<~T>[int]')
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
class Y(C[int]):
|
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Y.__module__, __name__)
|
2015-05-23 01:14:11 +08:00
|
|
|
if not PY32:
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Y.__qualname__,
|
|
|
|
'GenericTests.test_repr_2.<locals>.Y')
|
|
|
|
self.assertEqual(repr(Y).split('.')[-1], 'Y')
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_eq_1(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(Generic, Generic)
|
|
|
|
self.assertEqual(Generic[T], Generic[T])
|
|
|
|
self.assertNotEqual(Generic[KT], Generic[VT])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_eq_2(self):
|
|
|
|
|
|
|
|
class A(Generic[T]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class B(Generic[T]):
|
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(A, A)
|
|
|
|
self.assertNotEqual(A, B)
|
|
|
|
self.assertEqual(A[T], A[T])
|
|
|
|
self.assertNotEqual(A[T], B[T])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_multiple_inheritance(self):
|
|
|
|
|
|
|
|
class A(Generic[T, VT]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class B(Generic[KT, T]):
|
|
|
|
pass
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
class C(A[T, VT], Generic[VT, T, KT], B[KT, T]):
|
2015-05-23 01:14:11 +08:00
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(C.__parameters__, (VT, T, KT))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_nested(self):
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
G = Generic
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
class Visitor(G[T]):
|
|
|
|
|
|
|
|
a = None
|
|
|
|
|
|
|
|
def set(self, a: T):
|
|
|
|
self.a = a
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
return self.a
|
|
|
|
|
|
|
|
def visit(self) -> T:
|
|
|
|
return self.a
|
|
|
|
|
|
|
|
V = Visitor[typing.List[int]]
|
|
|
|
|
|
|
|
class IntListVisitor(V):
|
|
|
|
|
|
|
|
def append(self, x: int):
|
|
|
|
self.a.append(x)
|
|
|
|
|
|
|
|
a = IntListVisitor()
|
|
|
|
a.set([])
|
|
|
|
a.append(1)
|
|
|
|
a.append(42)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(a.get(), [1, 42])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_type_erasure(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
|
|
class Node(Generic[T]):
|
2015-09-05 03:15:54 +08:00
|
|
|
def __init__(self, label: T,
|
|
|
|
left: 'Node[T]' = None,
|
|
|
|
right: 'Node[T]' = None):
|
2015-05-23 01:14:11 +08:00
|
|
|
self.label = label # type: T
|
|
|
|
self.left = left # type: Optional[Node[T]]
|
|
|
|
self.right = right # type: Optional[Node[T]]
|
|
|
|
|
|
|
|
def foo(x: T):
|
|
|
|
a = Node(x)
|
|
|
|
b = Node[T](x)
|
|
|
|
c = Node[Any](x)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIs(type(a), Node)
|
|
|
|
self.assertIs(type(b), Node)
|
|
|
|
self.assertIs(type(c), Node)
|
|
|
|
self.assertEqual(a.label, x)
|
|
|
|
self.assertEqual(b.label, x)
|
|
|
|
self.assertEqual(c.label, x)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
foo(42)
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
def test_implicit_any(self):
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
|
|
class C(Generic[T]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class D(C):
|
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(D.__parameters__, ())
|
2016-04-05 23:28:52 +08:00
|
|
|
|
|
|
|
with self.assertRaises(Exception):
|
|
|
|
D[int]
|
|
|
|
with self.assertRaises(Exception):
|
|
|
|
D[Any]
|
|
|
|
with self.assertRaises(Exception):
|
|
|
|
D[T]
|
|
|
|
|
2016-09-12 06:34:56 +08:00
|
|
|
class ClassVarTests(BaseTestCase):
|
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ClassVar[1]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ClassVar[int, str]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ClassVar[int][str]
|
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(ClassVar), 'typing.ClassVar')
|
|
|
|
cv = ClassVar[int]
|
|
|
|
self.assertEqual(repr(cv), 'typing.ClassVar[int]')
|
|
|
|
cv = ClassVar[Employee]
|
|
|
|
self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__)
|
|
|
|
|
|
|
|
def test_cannot_subclass(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class C(type(ClassVar)):
|
|
|
|
pass
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class C(type(ClassVar[int])):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cannot_init(self):
|
2016-09-28 06:20:12 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ClassVar()
|
2016-09-12 06:34:56 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(ClassVar)()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
type(ClassVar[Optional[int]])()
|
|
|
|
|
|
|
|
def test_no_isinstance(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance(1, ClassVar[int])
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(int, ClassVar)
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class CastTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(cast(int, 42), 42)
|
|
|
|
self.assertEqual(cast(float, 42), 42)
|
|
|
|
self.assertIs(type(cast(float, 42)), int)
|
|
|
|
self.assertEqual(cast(Any, 42), 42)
|
|
|
|
self.assertEqual(cast(list, 42), 42)
|
|
|
|
self.assertEqual(cast(Union[str, float], 42), 42)
|
|
|
|
self.assertEqual(cast(AnyStr, 42), 42)
|
|
|
|
self.assertEqual(cast(None, 42), 42)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_errors(self):
|
|
|
|
# Bogus calls are not expected to fail.
|
|
|
|
cast(42, 42)
|
|
|
|
cast('hello', 42)
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class ForwardRefTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
|
|
|
|
class Node(Generic[T]):
|
|
|
|
|
|
|
|
def __init__(self, label: T):
|
|
|
|
self.label = label
|
|
|
|
self.left = self.right = None
|
|
|
|
|
|
|
|
def add_both(self,
|
|
|
|
left: 'Optional[Node[T]]',
|
|
|
|
right: 'Node[T]' = None,
|
|
|
|
stuff: int = None,
|
|
|
|
blah=None):
|
|
|
|
self.left = left
|
|
|
|
self.right = right
|
|
|
|
|
|
|
|
def add_left(self, node: Optional['Node[T]']):
|
|
|
|
self.add_both(node, None)
|
|
|
|
|
|
|
|
def add_right(self, node: 'Node[T]' = None):
|
|
|
|
self.add_both(None, node)
|
|
|
|
|
|
|
|
t = Node[int]
|
|
|
|
both_hints = get_type_hints(t.add_both, globals(), locals())
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(both_hints['left'], Optional[Node[T]])
|
|
|
|
self.assertEqual(both_hints['right'], Optional[Node[T]])
|
|
|
|
self.assertEqual(both_hints['left'], both_hints['right'])
|
|
|
|
self.assertEqual(both_hints['stuff'], Optional[int])
|
|
|
|
self.assertNotIn('blah', both_hints)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
left_hints = get_type_hints(t.add_left, globals(), locals())
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(left_hints['node'], Optional[Node[T]])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
right_hints = get_type_hints(t.add_right, globals(), locals())
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(right_hints['node'], Optional[Node[T]])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_forwardref_instance_type_error(self):
|
|
|
|
fr = typing._ForwardRef('int')
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
isinstance(42, fr)
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_union_forward(self):
|
|
|
|
|
|
|
|
def foo(a: Union['T']):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
|
|
|
{'a': Union[T]})
|
|
|
|
|
|
|
|
def test_tuple_forward(self):
|
|
|
|
|
|
|
|
def foo(a: Tuple['T']):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
|
|
|
{'a': Tuple[T]})
|
|
|
|
|
|
|
|
def test_callable_forward(self):
|
|
|
|
|
|
|
|
def foo(a: Callable[['T'], 'T']):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
|
|
|
{'a': Callable[[T], T]})
|
|
|
|
|
2015-08-05 18:11:06 +08:00
|
|
|
def test_callable_with_ellipsis_forward(self):
|
|
|
|
|
|
|
|
def foo(a: 'Callable[..., T]'):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(get_type_hints(foo, globals(), locals()),
|
|
|
|
{'a': Callable[..., T]})
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_syntax_error(self):
|
|
|
|
|
|
|
|
with self.assertRaises(SyntaxError):
|
|
|
|
Generic['/T']
|
|
|
|
|
|
|
|
def test_delayed_syntax_error(self):
|
|
|
|
|
|
|
|
def foo(a: 'Node[T'):
|
|
|
|
pass
|
|
|
|
|
|
|
|
with self.assertRaises(SyntaxError):
|
|
|
|
get_type_hints(foo)
|
|
|
|
|
|
|
|
def test_type_error(self):
|
|
|
|
|
|
|
|
def foo(a: Tuple['42']):
|
|
|
|
pass
|
|
|
|
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
get_type_hints(foo)
|
|
|
|
|
|
|
|
def test_name_error(self):
|
|
|
|
|
|
|
|
def foo(a: 'Noode[T]'):
|
|
|
|
pass
|
|
|
|
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
get_type_hints(foo, locals())
|
|
|
|
|
|
|
|
def test_no_type_check(self):
|
|
|
|
|
|
|
|
@no_type_check
|
|
|
|
def foo(a: 'whatevers') -> {}:
|
|
|
|
pass
|
|
|
|
|
|
|
|
th = get_type_hints(foo)
|
|
|
|
self.assertEqual(th, {})
|
|
|
|
|
|
|
|
def test_no_type_check_class(self):
|
|
|
|
|
|
|
|
@no_type_check
|
|
|
|
class C:
|
|
|
|
def foo(a: 'whatevers') -> {}:
|
|
|
|
pass
|
|
|
|
|
|
|
|
cth = get_type_hints(C.foo)
|
|
|
|
self.assertEqual(cth, {})
|
|
|
|
ith = get_type_hints(C().foo)
|
|
|
|
self.assertEqual(ith, {})
|
|
|
|
|
|
|
|
def test_meta_no_type_check(self):
|
|
|
|
|
|
|
|
@no_type_check_decorator
|
|
|
|
def magic_decorator(deco):
|
|
|
|
return deco
|
|
|
|
|
|
|
|
self.assertEqual(magic_decorator.__name__, 'magic_decorator')
|
|
|
|
|
|
|
|
@magic_decorator
|
|
|
|
def foo(a: 'whatevers') -> {}:
|
|
|
|
pass
|
|
|
|
|
|
|
|
@magic_decorator
|
|
|
|
class C:
|
|
|
|
def foo(a: 'whatevers') -> {}:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertEqual(foo.__name__, 'foo')
|
|
|
|
th = get_type_hints(foo)
|
|
|
|
self.assertEqual(th, {})
|
|
|
|
cth = get_type_hints(C.foo)
|
|
|
|
self.assertEqual(cth, {})
|
|
|
|
ith = get_type_hints(C().foo)
|
|
|
|
self.assertEqual(ith, {})
|
|
|
|
|
|
|
|
def test_default_globals(self):
|
|
|
|
code = ("class C:\n"
|
|
|
|
" def foo(self, a: 'C') -> 'D': pass\n"
|
|
|
|
"class D:\n"
|
|
|
|
" def bar(self, b: 'D') -> C: pass\n"
|
|
|
|
)
|
|
|
|
ns = {}
|
|
|
|
exec(code, ns)
|
|
|
|
hints = get_type_hints(ns['C'].foo)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']})
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class OverloadTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_overload_exists(self):
|
|
|
|
from typing import overload
|
|
|
|
|
|
|
|
def test_overload_fails(self):
|
|
|
|
from typing import overload
|
|
|
|
|
|
|
|
with self.assertRaises(RuntimeError):
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
@overload
|
|
|
|
def blah():
|
|
|
|
pass
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
blah()
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
def test_overload_succeeds(self):
|
|
|
|
from typing import overload
|
|
|
|
|
|
|
|
@overload
|
|
|
|
def blah():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def blah():
|
|
|
|
pass
|
|
|
|
|
|
|
|
blah()
|
|
|
|
|
|
|
|
|
|
|
|
PY35 = sys.version_info[:2] >= (3, 5)
|
2015-12-04 09:31:24 +08:00
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
PY35_TESTS = """
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
T_a = TypeVar('T')
|
2015-12-04 09:31:24 +08:00
|
|
|
|
|
|
|
class AwaitableWrapper(typing.Awaitable[T_a]):
|
|
|
|
|
|
|
|
def __init__(self, value):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def __await__(self) -> typing.Iterator[T_a]:
|
|
|
|
yield
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
class AsyncIteratorWrapper(typing.AsyncIterator[T_a]):
|
|
|
|
|
|
|
|
def __init__(self, value: typing.Iterable[T_a]):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
def __aiter__(self) -> typing.AsyncIterator[T_a]:
|
|
|
|
return self
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def __anext__(self) -> T_a:
|
|
|
|
data = yield from self.value
|
|
|
|
if data:
|
|
|
|
return data
|
|
|
|
else:
|
|
|
|
raise StopAsyncIteration
|
2016-04-05 23:28:52 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
if PY35:
|
|
|
|
exec(PY35_TESTS)
|
2015-12-04 09:31:24 +08:00
|
|
|
|
2016-09-12 06:34:56 +08:00
|
|
|
PY36 = sys.version_info[:2] >= (3, 6)
|
|
|
|
|
|
|
|
PY36_TESTS = """
|
|
|
|
from test import ann_module, ann_module2, ann_module3
|
|
|
|
from collections import ChainMap
|
|
|
|
|
|
|
|
class B:
|
|
|
|
x: ClassVar[Optional['B']] = None
|
|
|
|
y: int
|
|
|
|
class CSub(B):
|
|
|
|
z: ClassVar['CSub'] = B()
|
|
|
|
class G(Generic[T]):
|
|
|
|
lst: ClassVar[List[T]] = []
|
|
|
|
|
|
|
|
class CoolEmployee(NamedTuple):
|
|
|
|
name: str
|
|
|
|
cool: int
|
|
|
|
"""
|
|
|
|
|
|
|
|
if PY36:
|
|
|
|
exec(PY36_TESTS)
|
|
|
|
|
|
|
|
gth = get_type_hints
|
|
|
|
|
|
|
|
class GetTypeHintTests(BaseTestCase):
|
|
|
|
@skipUnless(PY36, 'Python 3.6 required')
|
|
|
|
def test_get_type_hints_modules(self):
|
|
|
|
self.assertEqual(gth(ann_module), {'x': int, 'y': str})
|
|
|
|
self.assertEqual(gth(ann_module2), {})
|
|
|
|
self.assertEqual(gth(ann_module3), {})
|
|
|
|
|
|
|
|
@skipUnless(PY36, 'Python 3.6 required')
|
|
|
|
def test_get_type_hints_classes(self):
|
|
|
|
self.assertEqual(gth(ann_module.C, ann_module.__dict__),
|
|
|
|
ChainMap({'y': Optional[ann_module.C]}, {}))
|
|
|
|
self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})')
|
|
|
|
self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type},
|
|
|
|
{}, {}))
|
|
|
|
self.assertEqual(gth(ann_module.D),
|
|
|
|
ChainMap({'j': str, 'k': str,
|
|
|
|
'y': Optional[ann_module.C]}, {}))
|
|
|
|
self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {}))
|
|
|
|
self.assertEqual(gth(ann_module.h_class),
|
|
|
|
ChainMap({}, {'y': Optional[ann_module.C]}, {}))
|
|
|
|
self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str},
|
|
|
|
{}))
|
|
|
|
self.assertEqual(gth(ann_module.foo), {'x': int})
|
|
|
|
|
|
|
|
@skipUnless(PY36, 'Python 3.6 required')
|
|
|
|
def test_respect_no_type_check(self):
|
|
|
|
@no_type_check
|
|
|
|
class NoTpCheck:
|
|
|
|
class Inn:
|
|
|
|
def __init__(self, x: 'not a type'): ...
|
|
|
|
self.assertTrue(NoTpCheck.__no_type_check__)
|
|
|
|
self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__)
|
|
|
|
self.assertEqual(gth(ann_module2.NTC.meth), {})
|
|
|
|
class ABase(Generic[T]):
|
|
|
|
def meth(x: int): ...
|
|
|
|
@no_type_check
|
|
|
|
class Der(ABase): ...
|
|
|
|
self.assertEqual(gth(ABase.meth), {'x': int})
|
|
|
|
|
|
|
|
|
|
|
|
def test_previous_behavior(self):
|
|
|
|
def testf(x, y): ...
|
|
|
|
testf.__annotations__['x'] = 'int'
|
|
|
|
self.assertEqual(gth(testf), {'x': int})
|
|
|
|
|
|
|
|
@skipUnless(PY36, 'Python 3.6 required')
|
|
|
|
def test_get_type_hints_ClassVar(self):
|
|
|
|
self.assertEqual(gth(B, globals()),
|
|
|
|
ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {}))
|
|
|
|
self.assertEqual(gth(CSub, globals()),
|
|
|
|
ChainMap({'z': ClassVar[CSub]},
|
|
|
|
{'y': int, 'x': ClassVar[Optional[B]]}, {}))
|
|
|
|
self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{}))
|
|
|
|
|
2015-12-04 09:31:24 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class CollectionsAbcTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_hashable(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(42, typing.Hashable)
|
|
|
|
self.assertNotIsInstance([], typing.Hashable)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_iterable(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.Iterable)
|
2015-09-05 03:15:54 +08:00
|
|
|
# Due to ABC caching, the second time takes a separate code
|
|
|
|
# path and could fail. So call this a few times.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.Iterable)
|
|
|
|
self.assertIsInstance([], typing.Iterable)
|
|
|
|
self.assertNotIsInstance(42, typing.Iterable)
|
2015-09-05 03:15:54 +08:00
|
|
|
# Just in case, also test issubclass() a few times.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(list, typing.Iterable)
|
|
|
|
self.assertIsSubclass(list, typing.Iterable)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_iterator(self):
|
|
|
|
it = iter([])
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(it, typing.Iterator)
|
|
|
|
self.assertNotIsInstance(42, typing.Iterator)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
@skipUnless(PY35, 'Python 3.5 required')
|
2015-12-04 09:31:24 +08:00
|
|
|
def test_awaitable(self):
|
2016-04-05 23:28:52 +08:00
|
|
|
ns = {}
|
|
|
|
exec(
|
|
|
|
"async def foo() -> typing.Awaitable[int]:\n"
|
|
|
|
" return await AwaitableWrapper(42)\n",
|
|
|
|
globals(), ns)
|
|
|
|
foo = ns['foo']
|
2015-12-04 09:31:24 +08:00
|
|
|
g = foo()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(g, typing.Awaitable)
|
|
|
|
self.assertNotIsInstance(foo, typing.Awaitable)
|
2015-12-04 09:31:24 +08:00
|
|
|
g.send(None) # Run foo() till completion, to avoid warning.
|
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
@skipUnless(PY35, 'Python 3.5 required')
|
2015-12-04 09:31:24 +08:00
|
|
|
def test_async_iterable(self):
|
|
|
|
base_it = range(10) # type: Iterator[int]
|
|
|
|
it = AsyncIteratorWrapper(base_it)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(it, typing.AsyncIterable)
|
|
|
|
self.assertIsInstance(it, typing.AsyncIterable)
|
|
|
|
self.assertNotIsInstance(42, typing.AsyncIterable)
|
2015-12-04 09:31:24 +08:00
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
@skipUnless(PY35, 'Python 3.5 required')
|
2015-12-04 09:31:24 +08:00
|
|
|
def test_async_iterator(self):
|
|
|
|
base_it = range(10) # type: Iterator[int]
|
|
|
|
it = AsyncIteratorWrapper(base_it)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(it, typing.AsyncIterator)
|
|
|
|
self.assertNotIsInstance(42, typing.AsyncIterator)
|
2015-12-04 09:31:24 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_sized(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.Sized)
|
|
|
|
self.assertNotIsInstance(42, typing.Sized)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_container(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.Container)
|
|
|
|
self.assertNotIsInstance(42, typing.Container)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-08-24 02:01:50 +08:00
|
|
|
def test_collection(self):
|
|
|
|
if hasattr(typing, 'Collection'):
|
|
|
|
self.assertIsInstance(tuple(), typing.Collection)
|
|
|
|
self.assertIsInstance(frozenset(), typing.Collection)
|
|
|
|
self.assertIsSubclass(dict, typing.Collection)
|
|
|
|
self.assertNotIsInstance(42, typing.Collection)
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_abstractset(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(set(), typing.AbstractSet)
|
|
|
|
self.assertNotIsInstance(42, typing.AbstractSet)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_mutableset(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(set(), typing.MutableSet)
|
|
|
|
self.assertNotIsInstance(frozenset(), typing.MutableSet)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_mapping(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance({}, typing.Mapping)
|
|
|
|
self.assertNotIsInstance(42, typing.Mapping)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_mutablemapping(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance({}, typing.MutableMapping)
|
|
|
|
self.assertNotIsInstance(42, typing.MutableMapping)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_sequence(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.Sequence)
|
|
|
|
self.assertNotIsInstance(42, typing.Sequence)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_mutablesequence(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance([], typing.MutableSequence)
|
|
|
|
self.assertNotIsInstance((), typing.MutableSequence)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_bytestring(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(b'', typing.ByteString)
|
|
|
|
self.assertIsInstance(bytearray(b''), typing.ByteString)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_list(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(list, typing.List)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_set(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(set, typing.Set)
|
|
|
|
self.assertNotIsSubclass(frozenset, typing.Set)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_frozenset(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(frozenset, typing.FrozenSet)
|
|
|
|
self.assertNotIsSubclass(set, typing.FrozenSet)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_dict(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(dict, typing.Dict)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_no_list_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.List()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.List[T]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.List[int]()
|
|
|
|
|
2016-05-18 23:35:00 +08:00
|
|
|
def test_list_subclass(self):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
class MyList(typing.List[int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
a = MyList()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(a, MyList)
|
2016-05-18 23:35:00 +08:00
|
|
|
self.assertIsInstance(a, typing.Sequence)
|
|
|
|
|
|
|
|
self.assertIsSubclass(MyList, list)
|
|
|
|
self.assertNotIsSubclass(list, MyList)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_no_dict_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Dict()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Dict[KT, VT]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Dict[str, int]()
|
|
|
|
|
2016-05-18 23:35:00 +08:00
|
|
|
def test_dict_subclass(self):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
class MyDict(typing.Dict[str, int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
d = MyDict()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(d, MyDict)
|
2016-05-18 23:35:00 +08:00
|
|
|
self.assertIsInstance(d, typing.MutableMapping)
|
|
|
|
|
|
|
|
self.assertIsSubclass(MyDict, dict)
|
|
|
|
self.assertNotIsSubclass(dict, MyDict)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-05 23:28:52 +08:00
|
|
|
def test_no_defaultdict_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.DefaultDict()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.DefaultDict[KT, VT]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.DefaultDict[str, int]()
|
|
|
|
|
2016-05-18 23:35:00 +08:00
|
|
|
def test_defaultdict_subclass(self):
|
2016-04-05 23:28:52 +08:00
|
|
|
|
|
|
|
class MyDefDict(typing.DefaultDict[str, int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
dd = MyDefDict()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(dd, MyDefDict)
|
2016-04-05 23:28:52 +08:00
|
|
|
|
2016-05-18 23:35:00 +08:00
|
|
|
self.assertIsSubclass(MyDefDict, collections.defaultdict)
|
|
|
|
self.assertNotIsSubclass(collections.defaultdict, MyDefDict)
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
def test_no_set_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Set()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Set[T]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Set[int]()
|
|
|
|
|
|
|
|
def test_set_subclass_instantiation(self):
|
|
|
|
|
|
|
|
class MySet(typing.Set[int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
d = MySet()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(d, MySet)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_no_frozenset_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.FrozenSet()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.FrozenSet[T]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.FrozenSet[int]()
|
|
|
|
|
|
|
|
def test_frozenset_subclass_instantiation(self):
|
|
|
|
|
|
|
|
class MyFrozenSet(typing.FrozenSet[int]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
d = MyFrozenSet()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(d, MyFrozenSet)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_no_tuple_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Tuple()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Tuple[T]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Tuple[int]()
|
|
|
|
|
|
|
|
def test_generator(self):
|
|
|
|
def foo():
|
|
|
|
yield 42
|
|
|
|
g = foo()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(type(g), typing.Generator)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_no_generator_instantiation(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Generator()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Generator[T, T, T]()
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
typing.Generator[int, int, int]()
|
|
|
|
|
|
|
|
def test_subclassing(self):
|
|
|
|
|
|
|
|
class MMA(typing.MutableMapping):
|
|
|
|
pass
|
|
|
|
|
|
|
|
with self.assertRaises(TypeError): # It's abstract
|
|
|
|
MMA()
|
|
|
|
|
|
|
|
class MMC(MMA):
|
2016-10-03 23:40:50 +08:00
|
|
|
def __getitem__(self, k):
|
|
|
|
return None
|
|
|
|
def __setitem__(self, k, v):
|
|
|
|
pass
|
|
|
|
def __delitem__(self, k):
|
|
|
|
pass
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(())
|
2015-05-23 01:14:11 +08:00
|
|
|
def __len__(self):
|
|
|
|
return 0
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(len(MMC()), 0)
|
2016-10-09 11:27:22 +08:00
|
|
|
assert callable(MMC.update)
|
|
|
|
self.assertIsInstance(MMC(), typing.Mapping)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
class MMB(typing.MutableMapping[KT, VT]):
|
2016-10-03 23:40:50 +08:00
|
|
|
def __getitem__(self, k):
|
|
|
|
return None
|
|
|
|
def __setitem__(self, k, v):
|
|
|
|
pass
|
|
|
|
def __delitem__(self, k):
|
|
|
|
pass
|
|
|
|
def __iter__(self):
|
|
|
|
return iter(())
|
2015-05-23 01:14:11 +08:00
|
|
|
def __len__(self):
|
|
|
|
return 0
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(len(MMB()), 0)
|
|
|
|
self.assertEqual(len(MMB[str, str]()), 0)
|
|
|
|
self.assertEqual(len(MMB[KT, VT]()), 0)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-05-18 23:35:00 +08:00
|
|
|
self.assertNotIsSubclass(dict, MMA)
|
|
|
|
self.assertNotIsSubclass(dict, MMB)
|
|
|
|
|
|
|
|
self.assertIsSubclass(MMA, typing.Mapping)
|
|
|
|
self.assertIsSubclass(MMB, typing.Mapping)
|
|
|
|
self.assertIsSubclass(MMC, typing.Mapping)
|
|
|
|
|
2016-10-09 11:27:22 +08:00
|
|
|
self.assertIsInstance(MMB[KT, VT](), typing.Mapping)
|
|
|
|
self.assertIsInstance(MMB[KT, VT](), collections.Mapping)
|
|
|
|
|
|
|
|
self.assertIsSubclass(MMA, collections.Mapping)
|
|
|
|
self.assertIsSubclass(MMB, collections.Mapping)
|
|
|
|
self.assertIsSubclass(MMC, collections.Mapping)
|
|
|
|
|
|
|
|
self.assertIsSubclass(MMB[str, str], typing.Mapping)
|
|
|
|
self.assertIsSubclass(MMC, MMA)
|
|
|
|
|
|
|
|
class I(typing.Iterable): ...
|
|
|
|
self.assertNotIsSubclass(list, I)
|
|
|
|
|
|
|
|
class G(typing.Generator[int, int, int]): ...
|
|
|
|
def g(): yield 0
|
|
|
|
self.assertIsSubclass(G, typing.Generator)
|
|
|
|
self.assertIsSubclass(G, typing.Iterable)
|
|
|
|
if hasattr(collections, 'Generator'):
|
|
|
|
self.assertIsSubclass(G, collections.Generator)
|
|
|
|
self.assertIsSubclass(G, collections.Iterable)
|
|
|
|
self.assertNotIsSubclass(type(g), G)
|
|
|
|
|
|
|
|
def test_subclassing_subclasshook(self):
|
|
|
|
|
|
|
|
class Base(typing.Iterable):
|
|
|
|
@classmethod
|
|
|
|
def __subclasshook__(cls, other):
|
|
|
|
if other.__name__ == 'Foo':
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
class C(Base): ...
|
|
|
|
class Foo: ...
|
|
|
|
class Bar: ...
|
|
|
|
self.assertIsSubclass(Foo, Base)
|
|
|
|
self.assertIsSubclass(Foo, C)
|
|
|
|
self.assertNotIsSubclass(Bar, C)
|
|
|
|
|
|
|
|
def test_subclassing_register(self):
|
|
|
|
|
|
|
|
class A(typing.Container): ...
|
|
|
|
class B(A): ...
|
|
|
|
|
|
|
|
class C: ...
|
|
|
|
A.register(C)
|
|
|
|
self.assertIsSubclass(C, A)
|
|
|
|
self.assertNotIsSubclass(C, B)
|
|
|
|
|
|
|
|
class D: ...
|
|
|
|
B.register(D)
|
|
|
|
self.assertIsSubclass(D, A)
|
|
|
|
self.assertIsSubclass(D, B)
|
|
|
|
|
|
|
|
class M(): ...
|
|
|
|
collections.MutableMapping.register(M)
|
|
|
|
self.assertIsSubclass(M, typing.Mapping)
|
|
|
|
|
|
|
|
def test_collections_as_base(self):
|
|
|
|
|
|
|
|
class M(collections.Mapping): ...
|
|
|
|
self.assertIsSubclass(M, typing.Mapping)
|
|
|
|
self.assertIsSubclass(M, typing.Iterable)
|
|
|
|
|
|
|
|
class S(collections.MutableSequence): ...
|
|
|
|
self.assertIsSubclass(S, typing.MutableSequence)
|
|
|
|
self.assertIsSubclass(S, typing.Iterable)
|
|
|
|
|
|
|
|
class I(collections.Iterable): ...
|
|
|
|
self.assertIsSubclass(I, typing.Iterable)
|
|
|
|
|
|
|
|
class A(collections.Mapping, metaclass=abc.ABCMeta): ...
|
|
|
|
class B: ...
|
|
|
|
A.register(B)
|
|
|
|
self.assertIsSubclass(B, typing.Mapping)
|
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class OtherABCTests(BaseTestCase):
|
2016-04-18 08:52:05 +08:00
|
|
|
|
|
|
|
@skipUnless(hasattr(typing, 'ContextManager'),
|
|
|
|
'requires typing.ContextManager')
|
|
|
|
def test_contextmanager(self):
|
|
|
|
@contextlib.contextmanager
|
|
|
|
def manager():
|
|
|
|
yield 42
|
|
|
|
|
|
|
|
cm = manager()
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(cm, typing.ContextManager)
|
|
|
|
self.assertNotIsInstance(42, typing.ContextManager)
|
2016-04-18 08:52:05 +08:00
|
|
|
|
|
|
|
|
2016-05-25 07:38:22 +08:00
|
|
|
class TypeTests(BaseTestCase):
|
|
|
|
|
|
|
|
def test_type_basic(self):
|
|
|
|
|
|
|
|
class User: pass
|
|
|
|
class BasicUser(User): pass
|
|
|
|
class ProUser(User): pass
|
|
|
|
|
|
|
|
def new_user(user_class: Type[User]) -> User:
|
|
|
|
return user_class()
|
|
|
|
|
|
|
|
joe = new_user(BasicUser)
|
|
|
|
|
|
|
|
def test_type_typevar(self):
|
|
|
|
|
|
|
|
class User: pass
|
|
|
|
class BasicUser(User): pass
|
|
|
|
class ProUser(User): pass
|
|
|
|
|
|
|
|
U = TypeVar('U', bound=User)
|
|
|
|
|
|
|
|
def new_user(user_class: Type[U]) -> U:
|
|
|
|
return user_class()
|
|
|
|
|
|
|
|
joe = new_user(BasicUser)
|
|
|
|
|
2016-09-12 06:34:56 +08:00
|
|
|
def test_type_optional(self):
|
|
|
|
A = Optional[Type[BaseException]]
|
|
|
|
|
|
|
|
def foo(a: A) -> Optional[BaseException]:
|
|
|
|
if a is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return a()
|
|
|
|
|
|
|
|
assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt)
|
|
|
|
assert foo(None) is None
|
|
|
|
|
2016-05-25 07:38:22 +08:00
|
|
|
|
2016-06-09 02:19:11 +08:00
|
|
|
class NewTypeTests(BaseTestCase):
|
|
|
|
|
|
|
|
def test_basic(self):
|
|
|
|
UserId = NewType('UserId', int)
|
|
|
|
UserName = NewType('UserName', str)
|
|
|
|
self.assertIsInstance(UserId(5), int)
|
|
|
|
self.assertIsInstance(UserName('Joe'), str)
|
|
|
|
self.assertEqual(UserId(5) + 1, 6)
|
|
|
|
|
|
|
|
def test_errors(self):
|
|
|
|
UserId = NewType('UserId', int)
|
|
|
|
UserName = NewType('UserName', str)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
issubclass(UserId, int)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
class D(UserName):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class NamedTupleTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
Emp = NamedTuple('Emp', [('name', str), ('id', int)])
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(Emp, tuple)
|
2015-05-23 01:14:11 +08:00
|
|
|
joe = Emp('Joe', 42)
|
|
|
|
jim = Emp(name='Jim', id=1)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsInstance(joe, Emp)
|
|
|
|
self.assertIsInstance(joe, tuple)
|
|
|
|
self.assertEqual(joe.name, 'Joe')
|
|
|
|
self.assertEqual(joe.id, 42)
|
|
|
|
self.assertEqual(jim.name, 'Jim')
|
|
|
|
self.assertEqual(jim.id, 1)
|
|
|
|
self.assertEqual(Emp.__name__, 'Emp')
|
|
|
|
self.assertEqual(Emp._fields, ('name', 'id'))
|
|
|
|
self.assertEqual(Emp._field_types, dict(name=str, id=int))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-09-12 06:34:56 +08:00
|
|
|
@skipUnless(PY36, 'Python 3.6 required')
|
|
|
|
def test_annotation_usage(self):
|
|
|
|
tim = CoolEmployee('Tim', 9000)
|
|
|
|
self.assertIsInstance(tim, CoolEmployee)
|
|
|
|
self.assertIsInstance(tim, tuple)
|
|
|
|
self.assertEqual(tim.name, 'Tim')
|
|
|
|
self.assertEqual(tim.cool, 9000)
|
|
|
|
self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
|
|
|
|
self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
|
|
|
|
self.assertEqual(CoolEmployee._field_types, dict(name=str, cool=int))
|
|
|
|
|
2015-11-20 00:16:31 +08:00
|
|
|
def test_pickle(self):
|
|
|
|
global Emp # pickle wants to reference the class by name
|
|
|
|
Emp = NamedTuple('Emp', [('name', str), ('id', int)])
|
|
|
|
jane = Emp('jane', 37)
|
2015-11-21 00:33:02 +08:00
|
|
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
|
|
|
z = pickle.dumps(jane, proto)
|
|
|
|
jane2 = pickle.loads(z)
|
|
|
|
self.assertEqual(jane2, jane)
|
2015-11-20 00:16:31 +08:00
|
|
|
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class IOTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_io(self):
|
|
|
|
|
|
|
|
def stuff(a: IO) -> AnyStr:
|
|
|
|
return a.readline()
|
|
|
|
|
|
|
|
a = stuff.__annotations__['a']
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(a.__parameters__, (AnyStr,))
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_textio(self):
|
|
|
|
|
|
|
|
def stuff(a: TextIO) -> str:
|
|
|
|
return a.readline()
|
|
|
|
|
|
|
|
a = stuff.__annotations__['a']
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(a.__parameters__, ())
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_binaryio(self):
|
|
|
|
|
|
|
|
def stuff(a: BinaryIO) -> bytes:
|
|
|
|
return a.readline()
|
|
|
|
|
|
|
|
a = stuff.__annotations__['a']
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(a.__parameters__, ())
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_io_submodule(self):
|
|
|
|
from typing.io import IO, TextIO, BinaryIO, __all__, __name__
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIs(IO, typing.IO)
|
|
|
|
self.assertIs(TextIO, typing.TextIO)
|
|
|
|
self.assertIs(BinaryIO, typing.BinaryIO)
|
|
|
|
self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO']))
|
|
|
|
self.assertEqual(__name__, 'typing.io')
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class RETests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
# Much of this is really testing _TypeAlias.
|
|
|
|
|
|
|
|
def test_basics(self):
|
|
|
|
pat = re.compile('[a-z]+', re.I)
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(pat.__class__, Pattern)
|
|
|
|
self.assertIsSubclass(type(pat), Pattern)
|
2016-09-28 06:20:12 +08:00
|
|
|
self.assertIsInstance(pat, Pattern)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
mat = pat.search('12345abcde.....')
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIsSubclass(mat.__class__, Match)
|
|
|
|
self.assertIsSubclass(type(mat), Match)
|
2016-09-28 06:20:12 +08:00
|
|
|
self.assertIsInstance(mat, Match)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
2016-09-28 06:20:12 +08:00
|
|
|
# these should just work
|
2015-05-23 01:14:11 +08:00
|
|
|
p = Pattern[Union[str, bytes]]
|
|
|
|
m = Match[Union[bytes, str]]
|
|
|
|
|
|
|
|
def test_errors(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
# Doesn't fit AnyStr.
|
|
|
|
Pattern[int]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
# Can't change type vars?
|
|
|
|
Match[T]
|
|
|
|
m = Match[Union[str, bytes]]
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
# Too complicated?
|
|
|
|
m[str]
|
2015-08-05 18:11:06 +08:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
# We don't support isinstance().
|
|
|
|
isinstance(42, Pattern[str])
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_repr(self):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]')
|
|
|
|
self.assertEqual(repr(Pattern[str]), 'Pattern[str]')
|
|
|
|
self.assertEqual(repr(Pattern[bytes]), 'Pattern[bytes]')
|
|
|
|
self.assertEqual(repr(Match), 'Match[~AnyStr]')
|
|
|
|
self.assertEqual(repr(Match[str]), 'Match[str]')
|
|
|
|
self.assertEqual(repr(Match[bytes]), 'Match[bytes]')
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_re_submodule(self):
|
|
|
|
from typing.re import Match, Pattern, __all__, __name__
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIs(Match, typing.Match)
|
|
|
|
self.assertIs(Pattern, typing.Pattern)
|
|
|
|
self.assertEqual(set(__all__), set(['Match', 'Pattern']))
|
|
|
|
self.assertEqual(__name__, 'typing.re')
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
def test_cannot_subclass(self):
|
|
|
|
with self.assertRaises(TypeError) as ex:
|
|
|
|
|
|
|
|
class A(typing.Match):
|
|
|
|
pass
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertEqual(str(ex.exception),
|
2016-09-28 06:20:12 +08:00
|
|
|
"Cannot subclass typing._TypeAlias")
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
|
2016-04-20 00:49:37 +08:00
|
|
|
class AllTests(BaseTestCase):
|
2015-05-23 01:14:11 +08:00
|
|
|
"""Tests for __all__."""
|
|
|
|
|
|
|
|
def test_all(self):
|
|
|
|
from typing import __all__ as a
|
|
|
|
# Just spot-check the first and last of every category.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIn('AbstractSet', a)
|
|
|
|
self.assertIn('ValuesView', a)
|
|
|
|
self.assertIn('cast', a)
|
|
|
|
self.assertIn('overload', a)
|
2016-04-18 08:52:05 +08:00
|
|
|
if hasattr(contextlib, 'AbstractContextManager'):
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIn('ContextManager', a)
|
2016-04-05 23:28:52 +08:00
|
|
|
# Check that io and re are not exported.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotIn('io', a)
|
|
|
|
self.assertNotIn('re', a)
|
2015-05-23 01:14:11 +08:00
|
|
|
# Spot-check that stdlib modules aren't exported.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertNotIn('os', a)
|
|
|
|
self.assertNotIn('sys', a)
|
2016-04-18 08:52:05 +08:00
|
|
|
# Check that Text is defined.
|
2016-04-20 00:49:37 +08:00
|
|
|
self.assertIn('Text', a)
|
2015-05-23 01:14:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|