diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py new file mode 100644 index 000000000..83bfef14d --- /dev/null +++ b/Lib/test/test_typing.py @@ -0,0 +1,3909 @@ +import contextlib +import collections +import pickle +import re +import sys +from unittest import TestCase, main, skipUnless, SkipTest, skip +from copy import copy, deepcopy + +from typing import Any, NoReturn +from typing import TypeVar, AnyStr +from typing import T, KT, VT # Not in __all__. +from typing import Union, Optional, Literal +from typing import Tuple, List, MutableMapping +from typing import Callable +from typing import Generic, ClassVar, Final, final, Protocol +from typing import cast, runtime_checkable +from typing import get_type_hints +from typing import get_origin, get_args +from typing import no_type_check, no_type_check_decorator +from typing import Type +from typing import NewType +from typing import NamedTuple, TypedDict +from typing import IO, TextIO, BinaryIO +from typing import Pattern, Match +import abc +import typing +import weakref +import types + +from test import mod_generics_cache + + +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) + + def clear_caches(self): + for f in typing._cleanups: + f() + + +class Employee: + pass + + +class Manager(Employee): + pass + + +class Founder(Employee): + pass + + +class ManagingFounder(Manager, Founder): + pass + + +class AnyTests(BaseTestCase): + + def test_any_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, Any) + + def test_any_subclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(Employee, Any) + with self.assertRaises(TypeError): + issubclass(Any, Employee) + + 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 + with self.assertRaises(TypeError): + class A(type(Any)): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + Any() + with self.assertRaises(TypeError): + type(Any)() + + def test_any_works_with_alias(self): + # These expressions must simply not fail. + typing.Match[Any] + typing.Pattern[Any] + typing.IO[Any] + + +class NoReturnTests(BaseTestCase): + + def test_noreturn_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, NoReturn) + + def test_noreturn_subclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(Employee, NoReturn) + with self.assertRaises(TypeError): + issubclass(NoReturn, Employee) + + def test_repr(self): + self.assertEqual(repr(NoReturn), 'typing.NoReturn') + + def test_not_generic(self): + with self.assertRaises(TypeError): + NoReturn[int] + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class A(NoReturn): + pass + with self.assertRaises(TypeError): + class A(type(NoReturn)): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + NoReturn() + with self.assertRaises(TypeError): + type(NoReturn)() + + +class TypeVarTests(BaseTestCase): + + def test_basic_plain(self): + T = TypeVar('T') + # T equals itself. + self.assertEqual(T, T) + # T is an instance of TypeVar + self.assertIsInstance(T, TypeVar) + + def test_typevar_instance_type_error(self): + T = TypeVar('T') + with self.assertRaises(TypeError): + isinstance(42, T) + + def test_typevar_subclass_type_error(self): + T = TypeVar('T') + with self.assertRaises(TypeError): + issubclass(int, T) + with self.assertRaises(TypeError): + issubclass(T, int) + + def test_constrained_error(self): + with self.assertRaises(TypeError): + X = TypeVar('X', int) + X + + def test_union_unique(self): + X = TypeVar('X') + Y = TypeVar('Y') + 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].__args__, (X, int)) + self.assertEqual(Union[X, int].__parameters__, (X,)) + self.assertIs(Union[X, int].__origin__, Union) + + def test_union_constrained(self): + A = TypeVar('A', str, bytes) + self.assertNotEqual(Union[A, str], Union[A]) + + 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) + + def test_missing__name__(self): + # See bpo-39942 + code = ("import typing\n" + "T = typing.TypeVar('T')\n" + ) + exec(code, {}) + + def test_no_bivariant(self): + with self.assertRaises(ValueError): + TypeVar('T', covariant=True, contravariant=True) + + +class UnionTests(BaseTestCase): + + def test_basics(self): + u = Union[int, float] + self.assertNotEqual(u, Union) + + 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) + + def test_union_any(self): + u = Union[Any] + self.assertEqual(u, Any) + 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) + + def test_union_object(self): + u = Union[object] + self.assertEqual(u, object) + u1 = Union[int, object] + u2 = Union[object, int] + self.assertEqual(u1, u2) + self.assertNotEqual(u1, object) + self.assertNotEqual(u2, 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_kept(self): + u = Union[Employee, Manager] + self.assertNotEqual(u, Employee) + self.assertIn(Employee, u.__args__) + self.assertIn(Manager, u.__args__) + + 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__) + T = TypeVar('T') + u = Union[T, int][int] + self.assertEqual(repr(u), repr(int)) + u = Union[List[int], int] + self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]') + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(Union): + pass + with self.assertRaises(TypeError): + class C(type(Union)): + pass + with self.assertRaises(TypeError): + class C(Union[int, str]): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + Union() + with self.assertRaises(TypeError): + type(Union)() + u = Union[int, float] + with self.assertRaises(TypeError): + u() + 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.assertIn(str, Union[str, typing.Iterable[int]].__args__) + self.assertIn(typing.Iterable[int], Union[str, typing.Iterable[int]].__args__) + + def test_union_compare_other(self): + self.assertNotEqual(Union, object) + self.assertNotEqual(Union, Any) + self.assertNotEqual(ClassVar, Union) + self.assertNotEqual(Optional, Union) + self.assertNotEqual([None], Optional) + self.assertNotEqual(Optional, typing.Mapping) + self.assertNotEqual(Optional[typing.MutableMapping], Union) + + def test_optional(self): + o = Optional[int] + u = Union[int, None] + self.assertEqual(o, u) + + def test_empty(self): + with self.assertRaises(TypeError): + Union[()] + + def test_union_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, Union[int, str]) + + def test_no_eval_union(self): + u = Union[int, str] + def f(x: u): ... + self.assertIs(get_type_hints(f)['x'], u) + + def test_function_repr_union(self): + def fun() -> int: ... + self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]') + + def test_union_str_pattern(self): + # Shouldn't crash; see http://bugs.python.org/issue25390 + A = Union[str, Pattern] + A + + 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 + + +class TupleTests(BaseTestCase): + + def test_basics(self): + with self.assertRaises(TypeError): + issubclass(Tuple, Tuple[int, str]) + with self.assertRaises(TypeError): + issubclass(tuple, Tuple[int, str]) + + class TP(tuple): ... + self.assertTrue(issubclass(tuple, Tuple)) + self.assertTrue(issubclass(TP, Tuple)) + + def test_equality(self): + 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, ...]) + + def test_tuple_subclass(self): + class MyTuple(tuple): + pass + self.assertTrue(issubclass(MyTuple, Tuple)) + + def test_tuple_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance((0, 0), Tuple[int, int]) + self.assertIsInstance((0, 0), Tuple) + + def test_repr(self): + self.assertEqual(repr(Tuple), 'typing.Tuple') + self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]') + 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]) + + +class CallableTests(BaseTestCase): + + def test_self_subclass(self): + with self.assertRaises(TypeError): + self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int])) + self.assertTrue(issubclass(type(lambda x: x), Callable)) + + 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_instantiate(self): + with self.assertRaises(TypeError): + Callable() + with self.assertRaises(TypeError): + type(Callable)() + c = Callable[[int], str] + with self.assertRaises(TypeError): + c() + with self.assertRaises(TypeError): + type(c)() + + def test_callable_wrong_forms(self): + with self.assertRaises(TypeError): + Callable[[...], int] + with self.assertRaises(TypeError): + Callable[(), int] + with self.assertRaises(TypeError): + Callable[[()], int] + with self.assertRaises(TypeError): + Callable[[int, 1], 2] + with self.assertRaises(TypeError): + Callable[int] + + def test_callable_instance_works(self): + def f(): + pass + self.assertIsInstance(f, Callable) + self.assertNotIsInstance(None, Callable) + + def test_callable_instance_type_error(self): + def f(): + pass + with self.assertRaises(TypeError): + self.assertIsInstance(f, Callable[[], None]) + with self.assertRaises(TypeError): + self.assertIsInstance(f, Callable[[], Any]) + with self.assertRaises(TypeError): + self.assertNotIsInstance(None, Callable[[], None]) + with self.assertRaises(TypeError): + self.assertNotIsInstance(None, Callable[[], Any]) + + 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]') + + def test_callable_with_ellipsis(self): + + def foo(a: Callable[..., T]): + pass + + self.assertEqual(get_type_hints(foo, globals(), locals()), + {'a': Callable[..., T]}) + + def test_ellipsis_in_generic(self): + # Shouldn't crash; see https://github.com/python/typing/issues/259 + typing.List[Callable[..., str]] + + +class LiteralTests(BaseTestCase): + def test_basics(self): + # All of these are allowed. + Literal[1] + Literal[1, 2, 3] + Literal["x", "y", "z"] + Literal[None] + Literal[True] + Literal[1, "2", False] + Literal[Literal[1, 2], Literal[4, 5]] + Literal[b"foo", u"bar"] + + def test_illegal_parameters_do_not_raise_runtime_errors(self): + # Type checkers should reject these types, but we do not + # raise errors at runtime to maintain maximium flexibility. + Literal[int] + Literal[3j + 2, ..., ()] + Literal[{"foo": 3, "bar": 4}] + Literal[T] + + def test_literals_inside_other_types(self): + List[Literal[1, 2, 3]] + List[Literal[("foo", "bar", "baz")]] + + def test_repr(self): + self.assertEqual(repr(Literal[1]), "typing.Literal[1]") + self.assertEqual(repr(Literal[1, True, "foo"]), "typing.Literal[1, True, 'foo']") + self.assertEqual(repr(Literal[int]), "typing.Literal[int]") + self.assertEqual(repr(Literal), "typing.Literal") + self.assertEqual(repr(Literal[None]), "typing.Literal[None]") + + def test_cannot_init(self): + with self.assertRaises(TypeError): + Literal() + with self.assertRaises(TypeError): + Literal[1]() + with self.assertRaises(TypeError): + type(Literal)() + with self.assertRaises(TypeError): + type(Literal[1])() + + def test_no_isinstance_or_issubclass(self): + with self.assertRaises(TypeError): + isinstance(1, Literal[1]) + with self.assertRaises(TypeError): + isinstance(int, Literal[1]) + with self.assertRaises(TypeError): + issubclass(1, Literal[1]) + with self.assertRaises(TypeError): + issubclass(int, Literal[1]) + + def test_no_subclassing(self): + with self.assertRaises(TypeError): + class Foo(Literal[1]): pass + with self.assertRaises(TypeError): + class Bar(Literal): pass + + def test_no_multiple_subscripts(self): + with self.assertRaises(TypeError): + Literal[1][1] + + +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: + ... + + +class MySimpleMapping(SimpleMapping[XK, XV]): + + 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 + + +class Coordinate(Protocol): + x: int + y: int + +@runtime_checkable +class Point(Coordinate, Protocol): + label: str + +class MyPoint: + x: int + y: int + label: str + +class XAxis(Protocol): + x: int + +class YAxis(Protocol): + y: int + +@runtime_checkable +class Position(XAxis, YAxis, Protocol): + pass + +@runtime_checkable +class Proto(Protocol): + attr: int + def meth(self, arg: str) -> int: + ... + +class Concrete(Proto): + pass + +class Other: + attr: int = 1 + def meth(self, arg: str) -> int: + if arg == 'this': + return 1 + return 0 + +class NT(NamedTuple): + x: int + y: int + +@runtime_checkable +class HasCallProtocol(Protocol): + __call__: typing.Callable + + +class ProtocolTests(BaseTestCase): + def test_basic_protocol(self): + @runtime_checkable + class P(Protocol): + def meth(self): + pass + + class C: pass + + class D: + def meth(self): + pass + + def f(): + pass + + self.assertIsSubclass(D, P) + self.assertIsInstance(D(), P) + self.assertNotIsSubclass(C, P) + self.assertNotIsInstance(C(), P) + self.assertNotIsSubclass(types.FunctionType, P) + self.assertNotIsInstance(f, P) + + def test_everything_implements_empty_protocol(self): + @runtime_checkable + class Empty(Protocol): + pass + + class C: + pass + + def f(): + pass + + for thing in (object, type, tuple, C, types.FunctionType): + self.assertIsSubclass(thing, Empty) + for thing in (object(), 1, (), typing, f): + self.assertIsInstance(thing, Empty) + + def test_function_implements_protocol(self): + def f(): + pass + + self.assertIsInstance(f, HasCallProtocol) + + def test_no_inheritance_from_nominal(self): + class C: pass + + class BP(Protocol): pass + + with self.assertRaises(TypeError): + class P(C, Protocol): + pass + with self.assertRaises(TypeError): + class P(Protocol, C): + pass + with self.assertRaises(TypeError): + class P(BP, C, Protocol): + pass + + class D(BP, C): pass + + class E(C, BP): pass + + self.assertNotIsInstance(D(), E) + self.assertNotIsInstance(E(), D) + + def test_no_instantiation(self): + class P(Protocol): pass + + with self.assertRaises(TypeError): + P() + + class C(P): pass + + self.assertIsInstance(C(), C) + T = TypeVar('T') + + class PG(Protocol[T]): pass + + with self.assertRaises(TypeError): + PG() + with self.assertRaises(TypeError): + PG[int]() + with self.assertRaises(TypeError): + PG[T]() + + class CG(PG[T]): pass + + self.assertIsInstance(CG[int](), CG) + + def test_cannot_instantiate_abstract(self): + @runtime_checkable + class P(Protocol): + @abc.abstractmethod + def ameth(self) -> int: + raise NotImplementedError + + class B(P): + pass + + class C(B): + def ameth(self) -> int: + return 26 + + with self.assertRaises(TypeError): + B() + self.assertIsInstance(C(), P) + + def test_subprotocols_extending(self): + class P1(Protocol): + def meth1(self): + pass + + @runtime_checkable + class P2(P1, Protocol): + def meth2(self): + pass + + class C: + def meth1(self): + pass + + def meth2(self): + pass + + class C1: + def meth1(self): + pass + + class C2: + def meth2(self): + pass + + self.assertNotIsInstance(C1(), P2) + self.assertNotIsInstance(C2(), P2) + self.assertNotIsSubclass(C1, P2) + self.assertNotIsSubclass(C2, P2) + self.assertIsInstance(C(), P2) + self.assertIsSubclass(C, P2) + + def test_subprotocols_merging(self): + class P1(Protocol): + def meth1(self): + pass + + class P2(Protocol): + def meth2(self): + pass + + @runtime_checkable + class P(P1, P2, Protocol): + pass + + class C: + def meth1(self): + pass + + def meth2(self): + pass + + class C1: + def meth1(self): + pass + + class C2: + def meth2(self): + pass + + self.assertNotIsInstance(C1(), P) + self.assertNotIsInstance(C2(), P) + self.assertNotIsSubclass(C1, P) + self.assertNotIsSubclass(C2, P) + self.assertIsInstance(C(), P) + self.assertIsSubclass(C, P) + + def test_protocols_issubclass(self): + T = TypeVar('T') + + @runtime_checkable + class P(Protocol): + def x(self): ... + + @runtime_checkable + class PG(Protocol[T]): + def x(self): ... + + class BadP(Protocol): + def x(self): ... + + class BadPG(Protocol[T]): + def x(self): ... + + class C: + def x(self): ... + + self.assertIsSubclass(C, P) + self.assertIsSubclass(C, PG) + self.assertIsSubclass(BadP, PG) + + with self.assertRaises(TypeError): + issubclass(C, PG[T]) + with self.assertRaises(TypeError): + issubclass(C, PG[C]) + with self.assertRaises(TypeError): + issubclass(C, BadP) + with self.assertRaises(TypeError): + issubclass(C, BadPG) + with self.assertRaises(TypeError): + issubclass(P, PG[T]) + with self.assertRaises(TypeError): + issubclass(PG, PG[int]) + + def test_protocols_issubclass_non_callable(self): + class C: + x = 1 + + @runtime_checkable + class PNonCall(Protocol): + x = 1 + + with self.assertRaises(TypeError): + issubclass(C, PNonCall) + self.assertIsInstance(C(), PNonCall) + PNonCall.register(C) + with self.assertRaises(TypeError): + issubclass(C, PNonCall) + self.assertIsInstance(C(), PNonCall) + + # check that non-protocol subclasses are not affected + class D(PNonCall): ... + + self.assertNotIsSubclass(C, D) + self.assertNotIsInstance(C(), D) + D.register(C) + self.assertIsSubclass(C, D) + self.assertIsInstance(C(), D) + with self.assertRaises(TypeError): + issubclass(D, PNonCall) + + def test_protocols_isinstance(self): + T = TypeVar('T') + + @runtime_checkable + class P(Protocol): + def meth(x): ... + + @runtime_checkable + class PG(Protocol[T]): + def meth(x): ... + + class BadP(Protocol): + def meth(x): ... + + class BadPG(Protocol[T]): + def meth(x): ... + + class C: + def meth(x): ... + + self.assertIsInstance(C(), P) + self.assertIsInstance(C(), PG) + with self.assertRaises(TypeError): + isinstance(C(), PG[T]) + with self.assertRaises(TypeError): + isinstance(C(), PG[C]) + with self.assertRaises(TypeError): + isinstance(C(), BadP) + with self.assertRaises(TypeError): + isinstance(C(), BadPG) + + def test_protocols_isinstance_py36(self): + class APoint: + def __init__(self, x, y, label): + self.x = x + self.y = y + self.label = label + + class BPoint: + label = 'B' + + def __init__(self, x, y): + self.x = x + self.y = y + + class C: + def __init__(self, attr): + self.attr = attr + + def meth(self, arg): + return 0 + + class Bad: pass + + self.assertIsInstance(APoint(1, 2, 'A'), Point) + self.assertIsInstance(BPoint(1, 2), Point) + self.assertNotIsInstance(MyPoint(), Point) + self.assertIsInstance(BPoint(1, 2), Position) + self.assertIsInstance(Other(), Proto) + self.assertIsInstance(Concrete(), Proto) + self.assertIsInstance(C(42), Proto) + self.assertNotIsInstance(Bad(), Proto) + self.assertNotIsInstance(Bad(), Point) + self.assertNotIsInstance(Bad(), Position) + self.assertNotIsInstance(Bad(), Concrete) + self.assertNotIsInstance(Other(), Concrete) + self.assertIsInstance(NT(1, 2), Position) + + def test_protocols_isinstance_init(self): + T = TypeVar('T') + + @runtime_checkable + class P(Protocol): + x = 1 + + @runtime_checkable + class PG(Protocol[T]): + x = 1 + + class C: + def __init__(self, x): + self.x = x + + self.assertIsInstance(C(1), P) + self.assertIsInstance(C(1), PG) + + def test_protocol_checks_after_subscript(self): + class P(Protocol[T]): pass + class C(P[T]): pass + class Other1: pass + class Other2: pass + CA = C[Any] + + self.assertNotIsInstance(Other1(), C) + self.assertNotIsSubclass(Other2, C) + + class D1(C[Any]): pass + class D2(C[Any]): pass + CI = C[int] + + self.assertIsInstance(D1(), C) + self.assertIsSubclass(D2, C) + + def test_protocols_support_register(self): + @runtime_checkable + class P(Protocol): + x = 1 + + class PM(Protocol): + def meth(self): pass + + class D(PM): pass + + class C: pass + + D.register(C) + P.register(C) + self.assertIsInstance(C(), P) + self.assertIsInstance(C(), D) + + def test_none_on_non_callable_doesnt_block_implementation(self): + @runtime_checkable + class P(Protocol): + x = 1 + + class A: + x = 1 + + class B(A): + x = None + + class C: + def __init__(self): + self.x = None + + self.assertIsInstance(B(), P) + self.assertIsInstance(C(), P) + + def test_none_on_callable_blocks_implementation(self): + @runtime_checkable + class P(Protocol): + def x(self): ... + + class A: + def x(self): ... + + class B(A): + x = None + + class C: + def __init__(self): + self.x = None + + self.assertNotIsInstance(B(), P) + self.assertNotIsInstance(C(), P) + + def test_non_protocol_subclasses(self): + class P(Protocol): + x = 1 + + @runtime_checkable + class PR(Protocol): + def meth(self): pass + + class NonP(P): + x = 1 + + class NonPR(PR): pass + + class C: + x = 1 + + class D: + def meth(self): pass + + self.assertNotIsInstance(C(), NonP) + self.assertNotIsInstance(D(), NonPR) + self.assertNotIsSubclass(C, NonP) + self.assertNotIsSubclass(D, NonPR) + self.assertIsInstance(NonPR(), PR) + self.assertIsSubclass(NonPR, PR) + + def test_custom_subclasshook(self): + class P(Protocol): + x = 1 + + class OKClass: pass + + class BadClass: + x = 1 + + class C(P): + @classmethod + def __subclasshook__(cls, other): + return other.__name__.startswith("OK") + + self.assertIsInstance(OKClass(), C) + self.assertNotIsInstance(BadClass(), C) + self.assertIsSubclass(OKClass, C) + self.assertNotIsSubclass(BadClass, C) + + def test_issubclass_fails_correctly(self): + @runtime_checkable + class P(Protocol): + x = 1 + + class C: pass + + with self.assertRaises(TypeError): + issubclass(C(), P) + + def test_defining_generic_protocols(self): + T = TypeVar('T') + S = TypeVar('S') + + @runtime_checkable + class PR(Protocol[T, S]): + def meth(self): pass + + class P(PR[int, T], Protocol[T]): + y = 1 + + with self.assertRaises(TypeError): + PR[int] + with self.assertRaises(TypeError): + P[int, str] + with self.assertRaises(TypeError): + PR[int, 1] + with self.assertRaises(TypeError): + PR[int, ClassVar] + + class C(PR[int, T]): pass + + self.assertIsInstance(C[str](), C) + + def test_defining_generic_protocols_old_style(self): + T = TypeVar('T') + S = TypeVar('S') + + @runtime_checkable + class PR(Protocol, Generic[T, S]): + def meth(self): pass + + class P(PR[int, str], Protocol): + y = 1 + + with self.assertRaises(TypeError): + issubclass(PR[int, str], PR) + self.assertIsSubclass(P, PR) + with self.assertRaises(TypeError): + PR[int] + with self.assertRaises(TypeError): + PR[int, 1] + + class P1(Protocol, Generic[T]): + def bar(self, x: T) -> str: ... + + class P2(Generic[T], Protocol): + def bar(self, x: T) -> str: ... + + @runtime_checkable + class PSub(P1[str], Protocol): + x = 1 + + class Test: + x = 1 + + def bar(self, x: str) -> str: + return x + + self.assertIsInstance(Test(), PSub) + with self.assertRaises(TypeError): + PR[int, ClassVar] + + def test_init_called(self): + T = TypeVar('T') + + class P(Protocol[T]): pass + + class C(P[T]): + def __init__(self): + self.test = 'OK' + + self.assertEqual(C[int]().test, 'OK') + + def test_protocols_bad_subscripts(self): + T = TypeVar('T') + S = TypeVar('S') + with self.assertRaises(TypeError): + class P(Protocol[T, T]): pass + with self.assertRaises(TypeError): + class P(Protocol[int]): pass + with self.assertRaises(TypeError): + class P(Protocol[T], Protocol[S]): pass + with self.assertRaises(TypeError): + class P(typing.Mapping[T, S], Protocol[T]): pass + + def test_generic_protocols_repr(self): + T = TypeVar('T') + S = TypeVar('S') + + class P(Protocol[T, S]): pass + + self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) + self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) + + def test_generic_protocols_eq(self): + T = TypeVar('T') + S = TypeVar('S') + + class P(Protocol[T, S]): pass + + self.assertEqual(P, P) + self.assertEqual(P[int, T], P[int, T]) + self.assertEqual(P[T, T][Tuple[T, S]][int, str], + P[Tuple[int, str], Tuple[int, str]]) + + def test_generic_protocols_special_from_generic(self): + T = TypeVar('T') + + class P(Protocol[T]): pass + + self.assertEqual(P.__parameters__, (T,)) + self.assertEqual(P[int].__parameters__, ()) + self.assertEqual(P[int].__args__, (int,)) + self.assertIs(P[int].__origin__, P) + + def test_generic_protocols_special_from_protocol(self): + @runtime_checkable + class PR(Protocol): + x = 1 + + class P(Protocol): + def meth(self): + pass + + T = TypeVar('T') + + class PG(Protocol[T]): + x = 1 + + def meth(self): + pass + + self.assertTrue(P._is_protocol) + self.assertTrue(PR._is_protocol) + self.assertTrue(PG._is_protocol) + self.assertFalse(P._is_runtime_protocol) + self.assertTrue(PR._is_runtime_protocol) + self.assertTrue(PG[int]._is_protocol) + self.assertEqual(typing._get_protocol_attrs(P), {'meth'}) + self.assertEqual(typing._get_protocol_attrs(PR), {'x'}) + self.assertEqual(frozenset(typing._get_protocol_attrs(PG)), + frozenset({'x', 'meth'})) + + def test_no_runtime_deco_on_nominal(self): + with self.assertRaises(TypeError): + @runtime_checkable + class C: pass + + class Proto(Protocol): + x = 1 + + with self.assertRaises(TypeError): + @runtime_checkable + class Concrete(Proto): + pass + + def test_none_treated_correctly(self): + @runtime_checkable + class P(Protocol): + x = None # type: int + + class B(object): pass + + self.assertNotIsInstance(B(), P) + + class C: + x = 1 + + class D: + x = None + + self.assertIsInstance(C(), P) + self.assertIsInstance(D(), P) + + class CI: + def __init__(self): + self.x = 1 + + class DI: + def __init__(self): + self.x = None + + self.assertIsInstance(C(), P) + self.assertIsInstance(D(), P) + + def test_protocols_in_unions(self): + class P(Protocol): + x = None # type: int + + Alias = typing.Union[typing.Iterable, P] + Alias2 = typing.Union[P, typing.Iterable] + self.assertEqual(Alias, Alias2) + + def test_protocols_pickleable(self): + global P, CP # pickle wants to reference the class by name + T = TypeVar('T') + + @runtime_checkable + class P(Protocol[T]): + x = 1 + + class CP(P[int]): + pass + + c = CP() + c.foo = 42 + c.bar = 'abc' + 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.x, 1) + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + s = pickle.dumps(P) + D = pickle.loads(s) + + class E: + x = 1 + + self.assertIsInstance(E(), D) + + def test_supports_int(self): + self.assertIsSubclass(int, typing.SupportsInt) + self.assertNotIsSubclass(str, typing.SupportsInt) + + def test_supports_float(self): + self.assertIsSubclass(float, typing.SupportsFloat) + self.assertNotIsSubclass(str, typing.SupportsFloat) + + def test_supports_complex(self): + + # Note: complex itself doesn't have __complex__. + class C: + def __complex__(self): + return 0j + + self.assertIsSubclass(C, typing.SupportsComplex) + self.assertNotIsSubclass(str, typing.SupportsComplex) + + def test_supports_bytes(self): + + # Note: bytes itself doesn't have __bytes__. + class B: + def __bytes__(self): + return b'' + + self.assertIsSubclass(B, typing.SupportsBytes) + self.assertNotIsSubclass(str, typing.SupportsBytes) + + def test_supports_abs(self): + self.assertIsSubclass(float, typing.SupportsAbs) + self.assertIsSubclass(int, typing.SupportsAbs) + self.assertNotIsSubclass(str, typing.SupportsAbs) + + def test_supports_round(self): + issubclass(float, typing.SupportsRound) + self.assertIsSubclass(float, typing.SupportsRound) + self.assertIsSubclass(int, typing.SupportsRound) + self.assertNotIsSubclass(str, typing.SupportsRound) + + def test_reversible(self): + self.assertIsSubclass(list, typing.Reversible) + self.assertNotIsSubclass(int, typing.Reversible) + + def test_supports_index(self): + self.assertIsSubclass(int, typing.SupportsIndex) + self.assertNotIsSubclass(str, typing.SupportsIndex) + + def test_bundled_protocol_instance_works(self): + self.assertIsInstance(0, typing.SupportsAbs) + class C1(typing.SupportsInt): + def __int__(self) -> int: + return 42 + class C2(C1): + pass + c = C2() + self.assertIsInstance(c, C1) + + def test_collections_protocols_allowed(self): + @runtime_checkable + class Custom(collections.abc.Iterable, Protocol): + def close(self): ... + + class A: pass + class B: + def __iter__(self): + return [] + def close(self): + return 0 + + self.assertIsSubclass(B, Custom) + self.assertNotIsSubclass(A, Custom) + + def test_builtin_protocol_whitelist(self): + with self.assertRaises(TypeError): + class CustomProtocol(TestCase, Protocol): + pass + + class CustomContextManager(typing.ContextManager, Protocol): + pass + +class GenericTests(BaseTestCase): + + def test_basics(self): + X = SimpleMapping[str, Any] + self.assertEqual(X.__parameters__, ()) + with self.assertRaises(TypeError): + X[str] + with self.assertRaises(TypeError): + X[str, str] + Y = SimpleMapping[XK, str] + self.assertEqual(Y.__parameters__, (XK,)) + Y[str] + with self.assertRaises(TypeError): + Y[str, str] + SM1 = SimpleMapping[str, int] + with self.assertRaises(TypeError): + issubclass(SM1, SimpleMapping) + self.assertIsInstance(SM1(), SimpleMapping) + + def test_generic_errors(self): + T = TypeVar('T') + S = TypeVar('S') + with self.assertRaises(TypeError): + Generic[T]() + with self.assertRaises(TypeError): + Generic[T][T] + with self.assertRaises(TypeError): + Generic[T][S] + with self.assertRaises(TypeError): + class C(Generic[T], Generic[T]): ... + with self.assertRaises(TypeError): + isinstance([], List[int]) + with self.assertRaises(TypeError): + issubclass(list, List[int]) + with self.assertRaises(TypeError): + class NewGeneric(Generic): ... + with self.assertRaises(TypeError): + class MyGeneric(Generic[T], Generic[S]): ... + with self.assertRaises(TypeError): + class MyGeneric(List[T], Generic[S]): ... + + 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] + + def test_init_subclass(self): + class X(typing.Generic[T]): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls.attr = 42 + class Y(X): + pass + self.assertEqual(Y.attr, 42) + with self.assertRaises(AttributeError): + X.attr + X.attr = 1 + Y.attr = 2 + class Z(Y): + pass + class W(X[int]): + pass + self.assertEqual(Y.attr, 2) + self.assertEqual(Z.attr, 42) + self.assertEqual(W.attr, 42) + + def test_repr(self): + self.assertEqual(repr(SimpleMapping), + f"") + self.assertEqual(repr(MySimpleMapping), + f"") + + def test_chain_repr(self): + T = TypeVar('T') + S = TypeVar('S') + + class C(Generic[T]): + pass + + X = C[Tuple[S, T]] + self.assertEqual(X, C[Tuple[S, T]]) + self.assertNotEqual(X, C[Tuple[T, S]]) + + Y = X[T, int] + self.assertEqual(Y, X[T, int]) + self.assertNotEqual(Y, X[S, int]) + self.assertNotEqual(Y, X[T, str]) + + Z = Y[str] + self.assertEqual(Z, Y[str]) + self.assertNotEqual(Z, Y[int]) + self.assertNotEqual(Z, Y[T]) + + self.assertTrue(str(Z).endswith( + '.C[typing.Tuple[str, int]]')) + + def test_new_repr(self): + T = TypeVar('T') + U = TypeVar('U', covariant=True) + S = TypeVar('S') + + self.assertEqual(repr(List), 'typing.List') + self.assertEqual(repr(List[T]), 'typing.List[~T]') + self.assertEqual(repr(List[U]), 'typing.List[+U]') + self.assertEqual(repr(List[S][T][int]), 'typing.List[int]') + self.assertEqual(repr(List[int]), 'typing.List[int]') + + def test_new_repr_complex(self): + T = TypeVar('T') + TS = TypeVar('TS') + + self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]') + self.assertEqual(repr(List[Tuple[T, TS]][int, T]), + 'typing.List[typing.Tuple[int, ~T]]') + self.assertEqual( + repr(List[Tuple[T, T]][List[int]]), + 'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]' + ) + + def test_new_repr_bare(self): + T = TypeVar('T') + self.assertEqual(repr(Generic[T]), 'typing.Generic[~T]') + self.assertEqual(repr(typing.Protocol[T]), 'typing.Protocol[~T]') + class C(typing.Dict[Any, Any]): ... + # this line should just work + repr(C.__mro__) + + def test_dict(self): + T = TypeVar('T') + + class B(Generic[T]): + pass + + b = B() + b.foo = 42 + self.assertEqual(b.__dict__, {'foo': 42}) + + class C(B[int]): + pass + + c = C() + c.bar = 'abc' + self.assertEqual(c.__dict__, {'bar': 'abc'}) + + def test_subscripted_generics_as_proxies(self): + T = TypeVar('T') + class C(Generic[T]): + x = 'def' + self.assertEqual(C[int].x, 'def') + self.assertEqual(C[C[int]].x, 'def') + C[C[int]].x = 'changed' + self.assertEqual(C.x, 'changed') + self.assertEqual(C[str].x, 'changed') + C[List[str]].z = 'new' + self.assertEqual(C.z, 'new') + self.assertEqual(C[Tuple[int]].z, 'new') + + self.assertEqual(C().x, 'changed') + self.assertEqual(C[Tuple[str]]().z, 'new') + + class D(C[T]): + pass + self.assertEqual(D[int].x, 'changed') + self.assertEqual(D.z, 'new') + D.z = 'from derived z' + D[int].x = 'from derived x' + self.assertEqual(C.x, 'changed') + self.assertEqual(C[int].z, 'new') + self.assertEqual(D.x, 'from derived x') + self.assertEqual(D[str].z, 'from derived z') + + def test_abc_registry_kept(self): + T = TypeVar('T') + class C(collections.abc.Mapping, Generic[T]): ... + C.register(int) + self.assertIsInstance(1, C) + C[int] + self.assertIsInstance(1, C) + C._abc_registry_clear() + C._abc_caches_clear() # To keep refleak hunting mode clean + + def test_false_subclasses(self): + class MyMapping(MutableMapping[str, str]): pass + self.assertNotIsInstance({}, MyMapping) + self.assertNotIsSubclass(dict, MyMapping) + + def test_abc_bases(self): + class MM(MutableMapping[str, str]): + 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 + # 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 + class MM2(collections.abc.MutableMapping, MutableMapping[str, str]): + pass + self.assertEqual(MM2.__bases__, (collections.abc.MutableMapping, Generic)) + + def test_orig_bases(self): + T = TypeVar('T') + class C(typing.Dict[str, T]): ... + self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],)) + + def test_naive_runtime_checks(self): + def naive_dict_check(obj, tp): + # Check if a dictionary conforms to Dict type + if len(tp.__parameters__) > 0: + raise NotImplementedError + if tp.__args__: + KT, VT = tp.__args__ + return all( + isinstance(k, KT) and isinstance(v, VT) + for k, v in obj.items() + ) + self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int])) + self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int])) + with self.assertRaises(NotImplementedError): + naive_dict_check({1: 'x'}, typing.Dict[str, T]) + + def naive_generic_check(obj, tp): + # Check if an instance conforms to the generic class + if not hasattr(obj, '__orig_class__'): + raise NotImplementedError + return obj.__orig_class__ == tp + class Node(Generic[T]): ... + self.assertTrue(naive_generic_check(Node[int](), Node[int])) + self.assertFalse(naive_generic_check(Node[str](), Node[int])) + self.assertFalse(naive_generic_check(Node[str](), List)) + with self.assertRaises(NotImplementedError): + naive_generic_check([1, 2, 3], Node[int]) + + def naive_list_base_check(obj, tp): + # Check if list conforms to a List subclass + return all(isinstance(x, tp.__orig_bases__[0].__args__[0]) + for x in obj) + class C(List[int]): ... + self.assertTrue(naive_list_base_check([1, 2, 3], C)) + self.assertFalse(naive_list_base_check(['a', 'b'], C)) + + def test_multi_subscr_base(self): + T = TypeVar('T') + U = TypeVar('U') + V = TypeVar('V') + class C(List[T][U][V]): ... + class D(C, List[T][U][V]): ... + self.assertEqual(C.__parameters__, (V,)) + self.assertEqual(D.__parameters__, (V,)) + self.assertEqual(C[int].__parameters__, ()) + self.assertEqual(D[int].__parameters__, ()) + self.assertEqual(C[int].__args__, (int,)) + self.assertEqual(D[int].__args__, (int,)) + self.assertEqual(C.__bases__, (list, Generic)) + self.assertEqual(D.__bases__, (C, list, Generic)) + self.assertEqual(C.__orig_bases__, (List[T][U][V],)) + self.assertEqual(D.__orig_bases__, (C, List[T][U][V])) + + def test_subscript_meta(self): + T = TypeVar('T') + class Meta(type): ... + self.assertEqual(Type[Meta], Type[Meta]) + self.assertEqual(Union[T, int][Meta], Union[Meta, int]) + self.assertEqual(Callable[..., Meta].__args__, (Ellipsis, Meta)) + + def test_generic_hashes(self): + class A(Generic[T]): + ... + + class B(Generic[T]): + class A(Generic[T]): + ... + + self.assertEqual(A, A) + self.assertEqual(mod_generics_cache.A[str], mod_generics_cache.A[str]) + self.assertEqual(B.A, B.A) + self.assertEqual(mod_generics_cache.B.A[B.A[str]], + mod_generics_cache.B.A[B.A[str]]) + + self.assertNotEqual(A, B.A) + self.assertNotEqual(A, mod_generics_cache.A) + self.assertNotEqual(A, mod_generics_cache.B.A) + self.assertNotEqual(B.A, mod_generics_cache.A) + self.assertNotEqual(B.A, mod_generics_cache.B.A) + + self.assertNotEqual(A[str], B.A[str]) + self.assertNotEqual(A[List[Any]], B.A[List[Any]]) + self.assertNotEqual(A[str], mod_generics_cache.A[str]) + self.assertNotEqual(A[str], mod_generics_cache.B.A[str]) + self.assertNotEqual(B.A[int], mod_generics_cache.A[int]) + self.assertNotEqual(B.A[List[Any]], mod_generics_cache.B.A[List[Any]]) + + self.assertNotEqual(Tuple[A[str]], Tuple[B.A[str]]) + self.assertNotEqual(Tuple[A[List[Any]]], Tuple[B.A[List[Any]]]) + self.assertNotEqual(Union[str, A[str]], Union[str, mod_generics_cache.A[str]]) + self.assertNotEqual(Union[A[str], A[str]], + Union[A[str], mod_generics_cache.A[str]]) + self.assertNotEqual(typing.FrozenSet[A[str]], + typing.FrozenSet[mod_generics_cache.B.A[str]]) + + if sys.version_info[:2] > (3, 2): + self.assertTrue(repr(Tuple[A[str]]).endswith('.A[str]]')) + self.assertTrue(repr(Tuple[B.A[str]]).endswith('.B.A[str]]')) + self.assertTrue(repr(Tuple[mod_generics_cache.A[str]]) + .endswith('mod_generics_cache.A[str]]')) + self.assertTrue(repr(Tuple[mod_generics_cache.B.A[str]]) + .endswith('mod_generics_cache.B.A[str]]')) + + def test_extended_generic_rules_eq(self): + T = TypeVar('T') + U = TypeVar('U') + self.assertEqual(Tuple[T, T][int], Tuple[int, int]) + self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]]) + with self.assertRaises(TypeError): + Tuple[T, int][()] + with self.assertRaises(TypeError): + Tuple[T, U][T, ...] + + self.assertEqual(Union[T, int][int], int) + self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str]) + class Base: ... + class Derived(Base): ... + self.assertEqual(Union[T, Base][Union[Base, Derived]], Union[Base, Derived]) + with self.assertRaises(TypeError): + Union[T, int][1] + + self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT]) + self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]]) + with self.assertRaises(TypeError): + Callable[[T], U][..., int] + with self.assertRaises(TypeError): + Callable[[T], U][[], int] + + def test_extended_generic_rules_repr(self): + T = TypeVar('T') + self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''), + 'Union[Tuple, Callable]') + self.assertEqual(repr(Union[Tuple, Tuple[int]]).replace('typing.', ''), + 'Union[Tuple, Tuple[int]]') + self.assertEqual(repr(Callable[..., Optional[T]][int]).replace('typing.', ''), + 'Callable[..., Union[int, NoneType]]') + self.assertEqual(repr(Callable[[], List[T]][int]).replace('typing.', ''), + 'Callable[[], List[int]]') + + def test_generic_forward_ref(self): + def foobar(x: List[List['CC']]): ... + class CC: ... + self.assertEqual( + get_type_hints(foobar, globals(), locals()), + {'x': List[List[CC]]} + ) + T = TypeVar('T') + AT = Tuple[T, ...] + def barfoo(x: AT): ... + self.assertIs(get_type_hints(barfoo, globals(), locals())['x'], AT) + CT = Callable[..., List[T]] + def barfoo2(x: CT): ... + self.assertIs(get_type_hints(barfoo2, globals(), locals())['x'], CT) + + def test_extended_generic_rules_subclassing(self): + class T1(Tuple[T, KT]): ... + class T2(Tuple[T, ...]): ... + class C1(Callable[[T], T]): ... + class C2(Callable[..., int]): + def __call__(self): + return None + + self.assertEqual(T1.__parameters__, (T, KT)) + self.assertEqual(T1[int, str].__args__, (int, str)) + self.assertEqual(T1[int, T].__origin__, T1) + + self.assertEqual(T2.__parameters__, (T,)) + with self.assertRaises(TypeError): + T1[int] + with self.assertRaises(TypeError): + T2[int, str] + + self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]') + self.assertEqual(C2.__parameters__, ()) + self.assertIsInstance(C2(), collections.abc.Callable) + self.assertIsSubclass(C2, collections.abc.Callable) + self.assertIsSubclass(C1, collections.abc.Callable) + self.assertIsInstance(T1(), tuple) + self.assertIsSubclass(T2, tuple) + with self.assertRaises(TypeError): + issubclass(Tuple[int, ...], typing.Sequence) + with self.assertRaises(TypeError): + issubclass(Tuple[int, ...], typing.Iterable) + + def test_fail_with_bare_union(self): + with self.assertRaises(TypeError): + List[Union] + with self.assertRaises(TypeError): + Tuple[Optional] + with self.assertRaises(TypeError): + ClassVar[ClassVar] + with self.assertRaises(TypeError): + List[ClassVar[int]] + + def test_fail_with_bare_generic(self): + T = TypeVar('T') + with self.assertRaises(TypeError): + List[Generic] + with self.assertRaises(TypeError): + Tuple[Generic[T]] + with self.assertRaises(TypeError): + List[typing.Protocol] + + def test_type_erasure_special(self): + T = TypeVar('T') + # this is the only test that checks type caching + self.clear_caches() + class MyTup(Tuple[T, T]): ... + self.assertIs(MyTup[int]().__class__, MyTup) + self.assertIs(MyTup[int]().__orig_class__, MyTup[int]) + class MyCall(Callable[..., T]): + def __call__(self): return None + self.assertIs(MyCall[T]().__class__, MyCall) + self.assertIs(MyCall[T]().__orig_class__, MyCall[T]) + class MyDict(typing.Dict[T, T]): ... + self.assertIs(MyDict[int]().__class__, MyDict) + self.assertIs(MyDict[int]().__orig_class__, MyDict[int]) + class MyDef(typing.DefaultDict[str, T]): ... + self.assertIs(MyDef[int]().__class__, MyDef) + self.assertIs(MyDef[int]().__orig_class__, MyDef[int]) + # ChainMap was added in 3.3 + if sys.version_info >= (3, 3): + class MyChain(typing.ChainMap[str, T]): ... + self.assertIs(MyChain[int]().__class__, MyChain) + self.assertIs(MyChain[int]().__orig_class__, MyChain[int]) + + def test_all_repr_eq_any(self): + objs = (getattr(typing, el) for el in typing.__all__) + for obj in objs: + self.assertNotEqual(repr(obj), '') + self.assertEqual(obj, obj) + if getattr(obj, '__parameters__', None) and len(obj.__parameters__) == 1: + self.assertEqual(obj[Any].__args__, (Any,)) + if isinstance(obj, type): + for base in obj.__mro__: + self.assertNotEqual(repr(base), '') + self.assertEqual(base, base) + + def test_pickle(self): + global C # pickle wants to reference the class by name + T = TypeVar('T') + + class B(Generic[T]): + pass + + class C(B[int]): + pass + + c = C() + c.foo = 42 + c.bar = 'abc' + 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'}) + samples = [Any, Union, Tuple, Callable, ClassVar, + Union[int, str], ClassVar[List], Tuple[int, ...], Callable[[str], bytes], + typing.DefaultDict, typing.FrozenSet[int]] + for s in samples: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) + more_samples = [List, typing.Iterable, typing.Type, List[int], + typing.Type[typing.Mapping], typing.AbstractSet[Tuple[int, str]]] + for s in more_samples: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(s, proto) + x = pickle.loads(z) + self.assertEqual(s, x) + + def test_copy_and_deepcopy(self): + T = TypeVar('T') + class Node(Generic[T]): ... + things = [Union[T, int], Tuple[T, int], Callable[..., T], Callable[[int], int], + Tuple[Any, Any], Node[T], Node[int], Node[Any], typing.Iterable[T], + typing.Iterable[Any], typing.Iterable[int], typing.Dict[int, str], + typing.Dict[T, Any], ClassVar[int], ClassVar[List[T]], Tuple['T', 'T'], + Union['T', int], List['T'], typing.Mapping['T', int]] + for t in things + [Any]: + self.assertEqual(t, copy(t)) + self.assertEqual(t, deepcopy(t)) + + def test_immutability_by_copy_and_pickle(self): + # Special forms like Union, Any, etc., generic aliases to containers like List, + # Mapping, etc., and type variabcles are considered immutable by copy and pickle. + global TP, TPB, TPV # for pickle + TP = TypeVar('TP') + TPB = TypeVar('TPB', bound=int) + TPV = TypeVar('TPV', bytes, str) + for X in [TP, TPB, TPV, List, typing.Mapping, ClassVar, typing.Iterable, + Union, Any, Tuple, Callable]: + self.assertIs(copy(X), X) + self.assertIs(deepcopy(X), X) + self.assertIs(pickle.loads(pickle.dumps(X)), X) + # Check that local type variables are copyable. + TL = TypeVar('TL') + TLB = TypeVar('TLB', bound=int) + TLV = TypeVar('TLV', bytes, str) + for X in [TL, TLB, TLV]: + self.assertIs(copy(X), X) + self.assertIs(deepcopy(X), X) + + def test_copy_generic_instances(self): + T = TypeVar('T') + class C(Generic[T]): + def __init__(self, attr: T) -> None: + self.attr = attr + + c = C(42) + self.assertEqual(copy(c).attr, 42) + self.assertEqual(deepcopy(c).attr, 42) + self.assertIsNot(copy(c), c) + self.assertIsNot(deepcopy(c), c) + c.attr = 1 + self.assertEqual(copy(c).attr, 1) + self.assertEqual(deepcopy(c).attr, 1) + ci = C[int](42) + self.assertEqual(copy(ci).attr, 42) + self.assertEqual(deepcopy(ci).attr, 42) + self.assertIsNot(copy(ci), ci) + self.assertIsNot(deepcopy(ci), ci) + ci.attr = 1 + self.assertEqual(copy(ci).attr, 1) + self.assertEqual(deepcopy(ci).attr, 1) + self.assertEqual(ci.__orig_class__, C[int]) + + def test_weakref_all(self): + T = TypeVar('T') + things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any], + Optional[List[int]], typing.Mapping[int, str], + typing.re.Match[bytes], typing.Iterable['whatever']] + for t in things: + self.assertEqual(weakref.ref(t)(), t) + + def test_parameterized_slots(self): + T = TypeVar('T') + class C(Generic[T]): + __slots__ = ('potato',) + + c = C() + c_int = C[int]() + + c.potato = 0 + c_int.potato = 0 + with self.assertRaises(AttributeError): + c.tomato = 0 + with self.assertRaises(AttributeError): + c_int.tomato = 0 + + def foo(x: C['C']): ... + self.assertEqual(get_type_hints(foo, globals(), locals())['x'], C[C]) + self.assertEqual(copy(C[int]), deepcopy(C[int])) + + def test_parameterized_slots_dict(self): + T = TypeVar('T') + class D(Generic[T]): + __slots__ = {'banana': 42} + + d = D() + d_int = D[int]() + + d.banana = 'yes' + d_int.banana = 'yes' + with self.assertRaises(AttributeError): + d.foobar = 'no' + with self.assertRaises(AttributeError): + d_int.foobar = 'no' + + def test_errors(self): + with self.assertRaises(TypeError): + B = SimpleMapping[XK, Any] + + class C(Generic[B]): + pass + + def test_repr_2(self): + class C(Generic[T]): + pass + + self.assertEqual(C.__module__, __name__) + self.assertEqual(C.__qualname__, + 'GenericTests.test_repr_2..C') + X = C[int] + self.assertEqual(X.__module__, __name__) + self.assertEqual(repr(X).split('.')[-1], 'C[int]') + + class Y(C[int]): + pass + + self.assertEqual(Y.__module__, __name__) + self.assertEqual(Y.__qualname__, + 'GenericTests.test_repr_2..Y') + + def test_eq_1(self): + self.assertEqual(Generic, Generic) + self.assertEqual(Generic[T], Generic[T]) + self.assertNotEqual(Generic[KT], Generic[VT]) + + def test_eq_2(self): + + class A(Generic[T]): + pass + + class B(Generic[T]): + pass + + self.assertEqual(A, A) + self.assertNotEqual(A, B) + self.assertEqual(A[T], A[T]) + self.assertNotEqual(A[T], B[T]) + + def test_multiple_inheritance(self): + + class A(Generic[T, VT]): + pass + + class B(Generic[KT, T]): + pass + + class C(A[T, VT], Generic[VT, T, KT], B[KT, T]): + pass + + self.assertEqual(C.__parameters__, (VT, T, KT)) + + def test_multiple_inheritance_special(self): + S = TypeVar('S') + class B(Generic[S]): ... + class C(List[int], B): ... + self.assertEqual(C.__mro__, (C, list, B, Generic, object)) + + def test_init_subclass_super_called(self): + class FinalException(Exception): + pass + + class Final: + def __init_subclass__(cls, **kwargs) -> None: + for base in cls.__bases__: + if base is not Final and issubclass(base, Final): + raise FinalException(base) + super().__init_subclass__(**kwargs) + class Test(Generic[T], Final): + pass + with self.assertRaises(FinalException): + class Subclass(Test): + pass + with self.assertRaises(FinalException): + class Subclass(Test[int]): + pass + + def test_nested(self): + + G = Generic + + 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) + self.assertEqual(a.get(), [1, 42]) + + def test_type_erasure(self): + T = TypeVar('T') + + class Node(Generic[T]): + def __init__(self, label: T, + left: 'Node[T]' = None, + right: 'Node[T]' = None): + 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) + 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) + + foo(42) + + def test_implicit_any(self): + T = TypeVar('T') + + class C(Generic[T]): + pass + + class D(C): + pass + + self.assertEqual(D.__parameters__, ()) + + with self.assertRaises(Exception): + D[int] + with self.assertRaises(Exception): + D[Any] + with self.assertRaises(Exception): + D[T] + + def test_new_with_args(self): + + class A(Generic[T]): + pass + + class B: + def __new__(cls, arg): + # call object + obj = super().__new__(cls) + obj.arg = arg + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + pass + + c = C('foo') + self.assertEqual(c.arg, 'foo') + + def test_new_with_args2(self): + + class A: + def __init__(self, arg): + self.from_a = arg + # call object + super().__init__() + + # mro: C, Generic, A, object + class C(Generic[T], A): + def __init__(self, arg): + self.from_c = arg + # call Generic + super().__init__(arg) + + c = C('foo') + self.assertEqual(c.from_a, 'foo') + self.assertEqual(c.from_c, 'foo') + + def test_new_no_args(self): + + class A(Generic[T]): + pass + + with self.assertRaises(TypeError): + A('foo') + + class B: + def __new__(cls): + # call object + obj = super().__new__(cls) + obj.from_b = 'b' + return obj + + # mro: C, A, Generic, B, object + class C(A, B): + def __init__(self, arg): + self.arg = arg + + def __new__(cls, arg): + # call A + obj = super().__new__(cls) + obj.from_c = 'c' + return obj + + c = C('foo') + self.assertEqual(c.arg, 'foo') + self.assertEqual(c.from_b, 'b') + self.assertEqual(c.from_c, 'c') + + +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): + with self.assertRaises(TypeError): + ClassVar() + 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) + + +class FinalTests(BaseTestCase): + + def test_basics(self): + Final[int] # OK + with self.assertRaises(TypeError): + Final[1] + with self.assertRaises(TypeError): + Final[int, str] + with self.assertRaises(TypeError): + Final[int][str] + with self.assertRaises(TypeError): + Optional[Final[int]] + + def test_repr(self): + self.assertEqual(repr(Final), 'typing.Final') + cv = Final[int] + self.assertEqual(repr(cv), 'typing.Final[int]') + cv = Final[Employee] + self.assertEqual(repr(cv), 'typing.Final[%s.Employee]' % __name__) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(type(Final)): + pass + with self.assertRaises(TypeError): + class C(type(Final[int])): + pass + + def test_cannot_init(self): + with self.assertRaises(TypeError): + Final() + with self.assertRaises(TypeError): + type(Final)() + with self.assertRaises(TypeError): + type(Final[Optional[int]])() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(1, Final[int]) + with self.assertRaises(TypeError): + issubclass(int, Final) + + def test_final_unmodified(self): + def func(x): ... + self.assertIs(func, final(func)) + + +class CastTests(BaseTestCase): + + def test_basics(self): + 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) + + def test_errors(self): + # Bogus calls are not expected to fail. + cast(42, 42) + cast('hello', 42) + + +class ForwardRefTests(BaseTestCase): + + 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()) + 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) + + left_hints = get_type_hints(t.add_left, globals(), locals()) + self.assertEqual(left_hints['node'], Optional[Node[T]]) + + right_hints = get_type_hints(t.add_right, globals(), locals()) + self.assertEqual(right_hints['node'], Optional[Node[T]]) + + def test_forwardref_instance_type_error(self): + fr = typing.ForwardRef('int') + with self.assertRaises(TypeError): + isinstance(42, fr) + + def test_forwardref_subclass_type_error(self): + fr = typing.ForwardRef('int') + with self.assertRaises(TypeError): + issubclass(int, fr) + + def test_forward_equality(self): + fr = typing.ForwardRef('int') + self.assertEqual(fr, typing.ForwardRef('int')) + self.assertNotEqual(List['int'], List[int]) + + def test_forward_equality_gth(self): + c1 = typing.ForwardRef('C') + c1_gth = typing.ForwardRef('C') + c2 = typing.ForwardRef('C') + c2_gth = typing.ForwardRef('C') + + class C: + pass + def foo(a: c1_gth, b: c2_gth): + pass + + self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': C, 'b': C}) + self.assertEqual(c1, c2) + self.assertEqual(c1, c1_gth) + self.assertEqual(c1_gth, c2_gth) + self.assertEqual(List[c1], List[c1_gth]) + self.assertNotEqual(List[c1], List[C]) + self.assertNotEqual(List[c1_gth], List[C]) + self.assertEqual(Union[c1, c1_gth], Union[c1]) + self.assertEqual(Union[c1, c1_gth, int], Union[c1, int]) + + def test_forward_equality_hash(self): + c1 = typing.ForwardRef('int') + c1_gth = typing.ForwardRef('int') + c2 = typing.ForwardRef('int') + c2_gth = typing.ForwardRef('int') + + def foo(a: c1_gth, b: c2_gth): + pass + get_type_hints(foo, globals(), locals()) + + self.assertEqual(hash(c1), hash(c2)) + self.assertEqual(hash(c1_gth), hash(c2_gth)) + self.assertEqual(hash(c1), hash(c1_gth)) + + def test_forward_equality_namespace(self): + class A: + pass + def namespace1(): + a = typing.ForwardRef('A') + def fun(x: a): + pass + get_type_hints(fun, globals(), locals()) + return a + + def namespace2(): + a = typing.ForwardRef('A') + + class A: + pass + def fun(x: a): + pass + + get_type_hints(fun, globals(), locals()) + return a + + self.assertEqual(namespace1(), namespace1()) + self.assertNotEqual(namespace1(), namespace2()) + + def test_forward_repr(self): + self.assertEqual(repr(List['int']), "typing.List[ForwardRef('int')]") + + 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_forward_recursion_actually(self): + def namespace1(): + a = typing.ForwardRef('A') + A = a + def fun(x: a): pass + + ret = get_type_hints(fun, globals(), locals()) + return a + + def namespace2(): + a = typing.ForwardRef('A') + A = a + def fun(x: a): pass + + ret = get_type_hints(fun, globals(), locals()) + return a + + def cmp(o1, o2): + return o1 == o2 + + r1 = namespace1() + r2 = namespace2() + self.assertIsNot(r1, r2) + self.assertRaises(RecursionError, cmp, r1, r2) + + def test_union_forward_recursion(self): + ValueList = List['Value'] + Value = Union[str, ValueList] + + class C: + foo: List[Value] + class D: + foo: Union[Value, ValueList] + class E: + foo: Union[List[Value], ValueList] + class F: + foo: Union[Value, List[Value], ValueList] + + self.assertEqual(get_type_hints(C, globals(), locals()), get_type_hints(C, globals(), locals())) + self.assertEqual(get_type_hints(C, globals(), locals()), + {'foo': List[Union[str, List[Union[str, List['Value']]]]]}) + self.assertEqual(get_type_hints(D, globals(), locals()), + {'foo': Union[str, List[Union[str, List['Value']]]]}) + self.assertEqual(get_type_hints(E, globals(), locals()), + {'foo': Union[ + List[Union[str, List[Union[str, List['Value']]]]], + List[Union[str, List['Value']]] + ] + }) + self.assertEqual(get_type_hints(F, globals(), locals()), + {'foo': Union[ + str, + List[Union[str, List['Value']]], + List[Union[str, List[Union[str, List['Value']]]]] + ] + }) + + def test_callable_forward(self): + + def foo(a: Callable[['T'], 'T']): + pass + + self.assertEqual(get_type_hints(foo, globals(), locals()), + {'a': Callable[[T], T]}) + + def test_callable_with_ellipsis_forward(self): + + def foo(a: 'Callable[..., T]'): + pass + + self.assertEqual(get_type_hints(foo, globals(), locals()), + {'a': Callable[..., T]}) + + 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_no_type_check_no_bases(self): + class C: + def meth(self, x: int): ... + @no_type_check + class D(C): + c = C + # verify that @no_type_check never affects bases + self.assertEqual(get_type_hints(C.meth), {'x': int}) + + def test_no_type_check_forward_ref_as_string(self): + class C: + foo: typing.ClassVar[int] = 7 + class D: + foo: ClassVar[int] = 7 + class E: + foo: 'typing.ClassVar[int]' = 7 + class F: + foo: 'ClassVar[int]' = 7 + + expected_result = {'foo': typing.ClassVar[int]} + for clazz in [C, D, E, F]: + self.assertEqual(get_type_hints(clazz), expected_result) + + def test_nested_classvar_fails_forward_ref_check(self): + class E: + foo: 'typing.ClassVar[typing.ClassVar[int]]' = 7 + class F: + foo: ClassVar['ClassVar[int]'] = 7 + + for clazz in [E, F]: + with self.assertRaises(TypeError): + get_type_hints(clazz) + + def test_meta_no_type_check(self): + + @no_type_check_decorator + def magic_decorator(func): + return func + + 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) + self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']}) + + def test_final_forward_ref(self): + self.assertEqual(gth(Loop, globals())['attr'], Final[Loop]) + self.assertNotEqual(gth(Loop, globals())['attr'], Final[int]) + self.assertNotEqual(gth(Loop, globals())['attr'], Final) + + +class OverloadTests(BaseTestCase): + + def test_overload_fails(self): + from typing import overload + + with self.assertRaises(RuntimeError): + + @overload + def blah(): + pass + + blah() + + def test_overload_succeeds(self): + from typing import overload + + @overload + def blah(): + pass + + def blah(): + pass + + blah() + + +ASYNCIO_TESTS = """ +import asyncio + +T_a = TypeVar('T_a') + +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 + + async def __anext__(self) -> T_a: + data = await self.value + if data: + return data + else: + raise StopAsyncIteration + +class ACM: + async def __aenter__(self) -> int: + return 42 + async def __aexit__(self, etype, eval, tb): + return None +""" + +try: + exec(ASYNCIO_TESTS) +except ImportError: + ASYNCIO = False # multithreading is not enabled +else: + ASYNCIO = True + +# Definitions needed for features introduced in Python 3.6 + +from test import ann_module, ann_module2, ann_module3 +from typing import AsyncContextManager + +class A: + y: float +class B(A): + x: ClassVar[Optional['B']] = None + y: int + b: int +class CSub(B): + z: ClassVar['CSub'] = B() +class G(Generic[T]): + lst: ClassVar[List[T]] = [] + +class Loop: + attr: Final['Loop'] + +class NoneAndForward: + parent: 'NoneAndForward' + meaning: None + +class CoolEmployee(NamedTuple): + name: str + cool: int + +class CoolEmployeeWithDefault(NamedTuple): + name: str + cool: int = 0 + +class XMeth(NamedTuple): + x: int + def double(self): + return 2 * self.x + +class XRepr(NamedTuple): + x: int + y: int = 1 + def __str__(self): + return f'{self.x} -> {self.y}' + def __add__(self, other): + return 0 + +Label = TypedDict('Label', [('label', str)]) + +class Point2D(TypedDict): + x: int + y: int + +class LabelPoint2D(Point2D, Label): ... + +class Options(TypedDict, total=False): + log_level: int + log_path: str + +class HasForeignBaseClass(mod_generics_cache.A): + some_xrepr: 'XRepr' + other_a: 'mod_generics_cache.A' + +async def g_with(am: AsyncContextManager[int]): + x: int + async with am as x: + return x + +try: + g_with(ACM()).send(None) +except StopIteration as e: + assert e.args[0] == 42 + +gth = get_type_hints + +class ForRefExample: + @ann_module.dec + def func(self: 'ForRefExample'): + pass + + @ann_module.dec + @ann_module.dec + def nested(self: 'ForRefExample'): + pass + + +class GetTypeHintTests(BaseTestCase): + def test_get_type_hints_from_various_objects(self): + # For invalid objects should fail with TypeError (not AttributeError etc). + with self.assertRaises(TypeError): + gth(123) + with self.assertRaises(TypeError): + gth('abc') + with self.assertRaises(TypeError): + gth(None) + + def test_get_type_hints_modules(self): + ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} + self.assertEqual(gth(ann_module), ann_module_type_hints) + self.assertEqual(gth(ann_module2), {}) + self.assertEqual(gth(ann_module3), {}) + + @skip("known bug") + def test_get_type_hints_modules_forwardref(self): + # FIXME: This currently exposes a bug in typing. Cached forward references + # don't account for the case where there are multiple types of the same + # name coming from different modules in the same program. + mgc_hints = {'default_a': Optional[mod_generics_cache.A], + 'default_b': Optional[mod_generics_cache.B]} + self.assertEqual(gth(mod_generics_cache), mgc_hints) + + def test_get_type_hints_classes(self): + self.assertEqual(gth(ann_module.C), # gth will find the right globalns + {'y': Optional[ann_module.C]}) + self.assertIsInstance(gth(ann_module.j_class), dict) + self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type}) + self.assertEqual(gth(ann_module.D), + {'j': str, 'k': str, 'y': Optional[ann_module.C]}) + self.assertEqual(gth(ann_module.Y), {'z': int}) + self.assertEqual(gth(ann_module.h_class), + {'y': Optional[ann_module.C]}) + self.assertEqual(gth(ann_module.S), {'x': str, 'y': str}) + self.assertEqual(gth(ann_module.foo), {'x': int}) + self.assertEqual(gth(NoneAndForward), + {'parent': NoneAndForward, 'meaning': type(None)}) + self.assertEqual(gth(HasForeignBaseClass), + {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, + 'some_b': mod_generics_cache.B}) + self.assertEqual(gth(XRepr.__new__), + {'x': int, 'y': int}) + self.assertEqual(gth(mod_generics_cache.B), + {'my_inner_a1': mod_generics_cache.B.A, + 'my_inner_a2': mod_generics_cache.B.A, + 'my_outer_a': mod_generics_cache.A}) + + 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_get_type_hints_for_builtins(self): + # Should not fail for built-in classes and functions. + self.assertEqual(gth(int), {}) + self.assertEqual(gth(type), {}) + self.assertEqual(gth(dir), {}) + self.assertEqual(gth(len), {}) + self.assertEqual(gth(object.__str__), {}) + self.assertEqual(gth(object().__str__), {}) + self.assertEqual(gth(str.join), {}) + + def test_previous_behavior(self): + def testf(x, y): ... + testf.__annotations__['x'] = 'int' + self.assertEqual(gth(testf), {'x': int}) + def testg(x: None): ... + self.assertEqual(gth(testg), {'x': type(None)}) + + def test_get_type_hints_for_object_with_annotations(self): + class A: ... + class B: ... + b = B() + b.__annotations__ = {'x': 'A'} + self.assertEqual(gth(b, locals()), {'x': A}) + + def test_get_type_hints_ClassVar(self): + self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), + {'var': typing.ClassVar[ann_module2.CV]}) + self.assertEqual(gth(B, globals()), + {'y': int, 'x': ClassVar[Optional[B]], 'b': int}) + self.assertEqual(gth(CSub, globals()), + {'z': ClassVar[CSub], 'y': int, 'b': int, + 'x': ClassVar[Optional[B]]}) + self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) + + def test_get_type_hints_wrapped_decoratored_func(self): + expects = {'self': ForRefExample} + self.assertEqual(gth(ForRefExample.func), expects) + self.assertEqual(gth(ForRefExample.nested), expects) + + +class GetUtilitiesTestCase(TestCase): + def test_get_origin(self): + T = TypeVar('T') + class C(Generic[T]): pass + self.assertIs(get_origin(C[int]), C) + self.assertIs(get_origin(C[T]), C) + self.assertIs(get_origin(int), None) + self.assertIs(get_origin(ClassVar[int]), ClassVar) + self.assertIs(get_origin(Union[int, str]), Union) + self.assertIs(get_origin(Literal[42, 43]), Literal) + self.assertIs(get_origin(Final[List[int]]), Final) + self.assertIs(get_origin(Generic), Generic) + self.assertIs(get_origin(Generic[T]), Generic) + self.assertIs(get_origin(List[Tuple[T, T]][int]), list) + self.assertIs(get_origin(List), list) + self.assertIs(get_origin(Tuple), tuple) + self.assertIs(get_origin(Callable), collections.abc.Callable) + + def test_get_args(self): + T = TypeVar('T') + class C(Generic[T]): pass + self.assertEqual(get_args(C[int]), (int,)) + self.assertEqual(get_args(C[T]), (T,)) + self.assertEqual(get_args(int), ()) + self.assertEqual(get_args(ClassVar[int]), (int,)) + self.assertEqual(get_args(Union[int, str]), (int, str)) + self.assertEqual(get_args(Literal[42, 43]), (42, 43)) + self.assertEqual(get_args(Final[List[int]]), (List[int],)) + self.assertEqual(get_args(Union[int, Tuple[T, int]][str]), + (int, Tuple[str, int])) + self.assertEqual(get_args(typing.Dict[int, Tuple[T, T]][Optional[int]]), + (int, Tuple[Optional[int], Optional[int]])) + self.assertEqual(get_args(Callable[[], T][int]), ([], int)) + self.assertEqual(get_args(Callable[..., int]), (..., int)) + self.assertEqual(get_args(Union[int, Callable[[Tuple[T, ...]], str]]), + (int, Callable[[Tuple[T, ...]], str])) + self.assertEqual(get_args(Tuple[int, ...]), (int, ...)) + self.assertEqual(get_args(Tuple[()]), ((),)) + self.assertEqual(get_args(List), ()) + self.assertEqual(get_args(Tuple), ()) + self.assertEqual(get_args(Callable), ()) + + +class CollectionsAbcTests(BaseTestCase): + + def test_hashable(self): + self.assertIsInstance(42, typing.Hashable) + self.assertNotIsInstance([], typing.Hashable) + + def test_iterable(self): + self.assertIsInstance([], typing.Iterable) + # Due to ABC caching, the second time takes a separate code + # path and could fail. So call this a few times. + self.assertIsInstance([], typing.Iterable) + self.assertIsInstance([], typing.Iterable) + self.assertNotIsInstance(42, typing.Iterable) + # Just in case, also test issubclass() a few times. + self.assertIsSubclass(list, typing.Iterable) + self.assertIsSubclass(list, typing.Iterable) + + def test_iterator(self): + it = iter([]) + self.assertIsInstance(it, typing.Iterator) + self.assertNotIsInstance(42, typing.Iterator) + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_awaitable(self): + ns = {} + exec( + "async def foo() -> typing.Awaitable[int]:\n" + " return await AwaitableWrapper(42)\n", + globals(), ns) + foo = ns['foo'] + g = foo() + self.assertIsInstance(g, typing.Awaitable) + self.assertNotIsInstance(foo, typing.Awaitable) + g.send(None) # Run foo() till completion, to avoid warning. + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_coroutine(self): + ns = {} + exec( + "async def foo():\n" + " return\n", + globals(), ns) + foo = ns['foo'] + g = foo() + self.assertIsInstance(g, typing.Coroutine) + with self.assertRaises(TypeError): + isinstance(g, typing.Coroutine[int]) + self.assertNotIsInstance(foo, typing.Coroutine) + try: + g.send(None) + except StopIteration: + pass + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_async_iterable(self): + base_it = range(10) # type: Iterator[int] + it = AsyncIteratorWrapper(base_it) + self.assertIsInstance(it, typing.AsyncIterable) + self.assertIsInstance(it, typing.AsyncIterable) + self.assertNotIsInstance(42, typing.AsyncIterable) + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_async_iterator(self): + base_it = range(10) # type: Iterator[int] + it = AsyncIteratorWrapper(base_it) + self.assertIsInstance(it, typing.AsyncIterator) + self.assertNotIsInstance(42, typing.AsyncIterator) + + def test_sized(self): + self.assertIsInstance([], typing.Sized) + self.assertNotIsInstance(42, typing.Sized) + + def test_container(self): + self.assertIsInstance([], typing.Container) + self.assertNotIsInstance(42, typing.Container) + + 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) + + def test_abstractset(self): + self.assertIsInstance(set(), typing.AbstractSet) + self.assertNotIsInstance(42, typing.AbstractSet) + + def test_mutableset(self): + self.assertIsInstance(set(), typing.MutableSet) + self.assertNotIsInstance(frozenset(), typing.MutableSet) + + def test_mapping(self): + self.assertIsInstance({}, typing.Mapping) + self.assertNotIsInstance(42, typing.Mapping) + + def test_mutablemapping(self): + self.assertIsInstance({}, typing.MutableMapping) + self.assertNotIsInstance(42, typing.MutableMapping) + + def test_sequence(self): + self.assertIsInstance([], typing.Sequence) + self.assertNotIsInstance(42, typing.Sequence) + + def test_mutablesequence(self): + self.assertIsInstance([], typing.MutableSequence) + self.assertNotIsInstance((), typing.MutableSequence) + + def test_bytestring(self): + self.assertIsInstance(b'', typing.ByteString) + self.assertIsInstance(bytearray(b''), typing.ByteString) + + def test_list(self): + self.assertIsSubclass(list, typing.List) + + def test_deque(self): + self.assertIsSubclass(collections.deque, typing.Deque) + class MyDeque(typing.Deque[int]): ... + self.assertIsInstance(MyDeque(), collections.deque) + + def test_counter(self): + self.assertIsSubclass(collections.Counter, typing.Counter) + + def test_set(self): + self.assertIsSubclass(set, typing.Set) + self.assertNotIsSubclass(frozenset, typing.Set) + + def test_frozenset(self): + self.assertIsSubclass(frozenset, typing.FrozenSet) + self.assertNotIsSubclass(set, typing.FrozenSet) + + def test_dict(self): + self.assertIsSubclass(dict, typing.Dict) + + 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]() + + def test_list_subclass(self): + + class MyList(typing.List[int]): + pass + + a = MyList() + self.assertIsInstance(a, MyList) + self.assertIsInstance(a, typing.Sequence) + + self.assertIsSubclass(MyList, list) + self.assertNotIsSubclass(list, MyList) + + 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]() + + def test_dict_subclass(self): + + class MyDict(typing.Dict[str, int]): + pass + + d = MyDict() + self.assertIsInstance(d, MyDict) + self.assertIsInstance(d, typing.MutableMapping) + + self.assertIsSubclass(MyDict, dict) + self.assertNotIsSubclass(dict, MyDict) + + def test_defaultdict_instantiation(self): + self.assertIs(type(typing.DefaultDict()), collections.defaultdict) + self.assertIs(type(typing.DefaultDict[KT, VT]()), collections.defaultdict) + self.assertIs(type(typing.DefaultDict[str, int]()), collections.defaultdict) + + def test_defaultdict_subclass(self): + + class MyDefDict(typing.DefaultDict[str, int]): + pass + + dd = MyDefDict() + self.assertIsInstance(dd, MyDefDict) + + self.assertIsSubclass(MyDefDict, collections.defaultdict) + self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + + def test_ordereddict_instantiation(self): + self.assertIs(type(typing.OrderedDict()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[KT, VT]()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[str, int]()), collections.OrderedDict) + + def test_ordereddict_subclass(self): + + class MyOrdDict(typing.OrderedDict[str, int]): + pass + + od = MyOrdDict() + self.assertIsInstance(od, MyOrdDict) + + self.assertIsSubclass(MyOrdDict, collections.OrderedDict) + self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) + + @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') + def test_chainmap_instantiation(self): + self.assertIs(type(typing.ChainMap()), collections.ChainMap) + self.assertIs(type(typing.ChainMap[KT, VT]()), collections.ChainMap) + self.assertIs(type(typing.ChainMap[str, int]()), collections.ChainMap) + class CM(typing.ChainMap[KT, VT]): ... + self.assertIs(type(CM[int, str]()), CM) + + @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') + def test_chainmap_subclass(self): + + class MyChainMap(typing.ChainMap[str, int]): + pass + + cm = MyChainMap() + self.assertIsInstance(cm, MyChainMap) + + self.assertIsSubclass(MyChainMap, collections.ChainMap) + self.assertNotIsSubclass(collections.ChainMap, MyChainMap) + + def test_deque_instantiation(self): + self.assertIs(type(typing.Deque()), collections.deque) + self.assertIs(type(typing.Deque[T]()), collections.deque) + self.assertIs(type(typing.Deque[int]()), collections.deque) + class D(typing.Deque[T]): ... + self.assertIs(type(D[int]()), D) + + def test_counter_instantiation(self): + self.assertIs(type(typing.Counter()), collections.Counter) + self.assertIs(type(typing.Counter[T]()), collections.Counter) + self.assertIs(type(typing.Counter[int]()), collections.Counter) + class C(typing.Counter[T]): ... + self.assertIs(type(C[int]()), C) + + def test_counter_subclass_instantiation(self): + + class MyCounter(typing.Counter[int]): + pass + + d = MyCounter() + self.assertIsInstance(d, MyCounter) + self.assertIsInstance(d, typing.Counter) + self.assertIsInstance(d, collections.Counter) + + 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() + self.assertIsInstance(d, MySet) + + 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() + self.assertIsInstance(d, MyFrozenSet) + + 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() + self.assertIsSubclass(type(g), typing.Generator) + + 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_async_generator(self): + ns = {} + exec("async def f():\n" + " yield 42\n", globals(), ns) + g = ns['f']() + self.assertIsSubclass(type(g), typing.AsyncGenerator) + + def test_no_async_generator_instantiation(self): + with self.assertRaises(TypeError): + typing.AsyncGenerator() + with self.assertRaises(TypeError): + typing.AsyncGenerator[T, T]() + with self.assertRaises(TypeError): + typing.AsyncGenerator[int, int]() + + def test_subclassing(self): + + class MMA(typing.MutableMapping): + pass + + with self.assertRaises(TypeError): # It's abstract + MMA() + + class MMC(MMA): + 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 + + self.assertEqual(len(MMC()), 0) + assert callable(MMC.update) + self.assertIsInstance(MMC(), typing.Mapping) + + class MMB(typing.MutableMapping[KT, VT]): + 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 + + self.assertEqual(len(MMB()), 0) + self.assertEqual(len(MMB[str, str]()), 0) + self.assertEqual(len(MMB[KT, VT]()), 0) + + self.assertNotIsSubclass(dict, MMA) + self.assertNotIsSubclass(dict, MMB) + + self.assertIsSubclass(MMA, typing.Mapping) + self.assertIsSubclass(MMB, typing.Mapping) + self.assertIsSubclass(MMC, typing.Mapping) + + self.assertIsInstance(MMB[KT, VT](), typing.Mapping) + self.assertIsInstance(MMB[KT, VT](), collections.abc.Mapping) + + self.assertIsSubclass(MMA, collections.abc.Mapping) + self.assertIsSubclass(MMB, collections.abc.Mapping) + self.assertIsSubclass(MMC, collections.abc.Mapping) + + with self.assertRaises(TypeError): + issubclass(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) + self.assertIsSubclass(G, collections.abc.Generator) + self.assertIsSubclass(G, collections.abc.Iterable) + self.assertNotIsSubclass(type(g), G) + + def test_subclassing_async_generator(self): + class G(typing.AsyncGenerator[int, int]): + def asend(self, value): + pass + def athrow(self, typ, val=None, tb=None): + pass + + ns = {} + exec('async def g(): yield 0', globals(), ns) + g = ns['g'] + self.assertIsSubclass(G, typing.AsyncGenerator) + self.assertIsSubclass(G, typing.AsyncIterable) + self.assertIsSubclass(G, collections.abc.AsyncGenerator) + self.assertIsSubclass(G, collections.abc.AsyncIterable) + self.assertNotIsSubclass(type(g), G) + + instance = G() + self.assertIsInstance(instance, typing.AsyncGenerator) + self.assertIsInstance(instance, typing.AsyncIterable) + self.assertIsInstance(instance, collections.abc.AsyncGenerator) + self.assertIsInstance(instance, collections.abc.AsyncIterable) + self.assertNotIsInstance(type(g), G) + self.assertNotIsInstance(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.abc.MutableMapping.register(M) + self.assertIsSubclass(M, typing.Mapping) + + def test_collections_as_base(self): + + class M(collections.abc.Mapping): ... + self.assertIsSubclass(M, typing.Mapping) + self.assertIsSubclass(M, typing.Iterable) + + class S(collections.abc.MutableSequence): ... + self.assertIsSubclass(S, typing.MutableSequence) + self.assertIsSubclass(S, typing.Iterable) + + class I(collections.abc.Iterable): ... + self.assertIsSubclass(I, typing.Iterable) + + class A(collections.abc.Mapping, metaclass=abc.ABCMeta): ... + class B: ... + A.register(B) + self.assertIsSubclass(B, typing.Mapping) + + +class OtherABCTests(BaseTestCase): + + def test_contextmanager(self): + @contextlib.contextmanager + def manager(): + yield 42 + + cm = manager() + self.assertIsInstance(cm, typing.ContextManager) + self.assertNotIsInstance(42, typing.ContextManager) + + @skipUnless(ASYNCIO, 'Python 3.5 required') + def test_async_contextmanager(self): + class NotACM: + pass + self.assertIsInstance(ACM(), typing.AsyncContextManager) + self.assertNotIsInstance(NotACM(), typing.AsyncContextManager) + @contextlib.contextmanager + def manager(): + yield 42 + + cm = manager() + self.assertNotIsInstance(cm, typing.AsyncContextManager) + self.assertEqual(typing.AsyncContextManager[int].__args__, (int,)) + with self.assertRaises(TypeError): + isinstance(42, typing.AsyncContextManager[int]) + with self.assertRaises(TypeError): + typing.AsyncContextManager[int, str] + + +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() + + 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() + + new_user(BasicUser) + + 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 + + +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 + + +class NamedTupleTests(BaseTestCase): + class NestedEmployee(NamedTuple): + name: str + cool: int + + def test_basics(self): + Emp = NamedTuple('Emp', [('name', str), ('id', int)]) + self.assertIsSubclass(Emp, tuple) + joe = Emp('Joe', 42) + jim = Emp(name='Jim', id=1) + 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.__annotations__, + collections.OrderedDict([('name', str), ('id', int)])) + self.assertIs(Emp._field_types, Emp.__annotations__) + + def test_namedtuple_pyversion(self): + if sys.version_info[:2] < (3, 6): + with self.assertRaises(TypeError): + NamedTuple('Name', one=int, other=str) + with self.assertRaises(TypeError): + class NotYet(NamedTuple): + whatever = 0 + + 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.__annotations__, + collections.OrderedDict(name=str, cool=int)) + self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__) + + def test_annotation_usage_with_default(self): + jelle = CoolEmployeeWithDefault('Jelle') + self.assertIsInstance(jelle, CoolEmployeeWithDefault) + self.assertIsInstance(jelle, tuple) + self.assertEqual(jelle.name, 'Jelle') + self.assertEqual(jelle.cool, 0) + cooler_employee = CoolEmployeeWithDefault('Sjoerd', 1) + self.assertEqual(cooler_employee.cool, 1) + + self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault') + self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) + self.assertEqual(CoolEmployeeWithDefault._field_types, dict(name=str, cool=int)) + self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) + + with self.assertRaises(TypeError): + exec(""" +class NonDefaultAfterDefault(NamedTuple): + x: int = 3 + y: int +""") + + def test_annotation_usage_with_methods(self): + self.assertEqual(XMeth(1).double(), 2) + self.assertEqual(XMeth(42).x, XMeth(42)[0]) + self.assertEqual(str(XRepr(42)), '42 -> 1') + self.assertEqual(XRepr(1, 2) + XRepr(3), 0) + + with self.assertRaises(AttributeError): + exec(""" +class XMethBad(NamedTuple): + x: int + def _fields(self): + return 'no chance for this' +""") + + with self.assertRaises(AttributeError): + exec(""" +class XMethBad2(NamedTuple): + x: int + def _source(self): + return 'no chance for this as well' +""") + + def test_namedtuple_keyword_usage(self): + LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int) + nick = LocalEmployee('Nick', 25) + self.assertIsInstance(nick, tuple) + self.assertEqual(nick.name, 'Nick') + self.assertEqual(LocalEmployee.__name__, 'LocalEmployee') + self.assertEqual(LocalEmployee._fields, ('name', 'age')) + self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int)) + self.assertIs(LocalEmployee._field_types, LocalEmployee.__annotations__) + with self.assertRaises(TypeError): + NamedTuple('Name', [('x', int)], y=str) + with self.assertRaises(TypeError): + NamedTuple('Name', x=1, y='a') + + def test_namedtuple_special_keyword_names(self): + NT = NamedTuple("NT", cls=type, self=object, typename=str, fields=list) + self.assertEqual(NT.__name__, 'NT') + self.assertEqual(NT._fields, ('cls', 'self', 'typename', 'fields')) + a = NT(cls=str, self=42, typename='foo', fields=[('bar', tuple)]) + self.assertEqual(a.cls, str) + self.assertEqual(a.self, 42) + self.assertEqual(a.typename, 'foo') + self.assertEqual(a.fields, [('bar', tuple)]) + + def test_namedtuple_errors(self): + with self.assertRaises(TypeError): + NamedTuple.__new__() + with self.assertRaises(TypeError): + NamedTuple() + with self.assertRaises(TypeError): + NamedTuple('Emp', [('name', str)], None) + with self.assertRaises(ValueError): + NamedTuple('Emp', [('_name', str)]) + + with self.assertWarns(DeprecationWarning): + Emp = NamedTuple(typename='Emp', name=str, id=int) + self.assertEqual(Emp.__name__, 'Emp') + self.assertEqual(Emp._fields, ('name', 'id')) + + with self.assertWarns(DeprecationWarning): + Emp = NamedTuple('Emp', fields=[('name', str), ('id', int)]) + self.assertEqual(Emp.__name__, 'Emp') + self.assertEqual(Emp._fields, ('name', 'id')) + + def test_copy_and_pickle(self): + global Emp # pickle wants to reference the class by name + Emp = NamedTuple('Emp', [('name', str), ('cool', int)]) + for cls in Emp, CoolEmployee, self.NestedEmployee: + with self.subTest(cls=cls): + jane = cls('jane', 37) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) + + jane2 = copy(jane) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) + + jane2 = deepcopy(jane) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) + + +class TypedDictTests(BaseTestCase): + def test_basics_functional_syntax(self): + Emp = TypedDict('Emp', {'name': str, 'id': int}) + self.assertIsSubclass(Emp, dict) + self.assertIsSubclass(Emp, typing.MutableMapping) + self.assertNotIsSubclass(Emp, collections.abc.Sequence) + jim = Emp(name='Jim', id=1) + self.assertIs(type(jim), dict) + self.assertEqual(jim['name'], 'Jim') + self.assertEqual(jim['id'], 1) + self.assertEqual(Emp.__name__, 'Emp') + self.assertEqual(Emp.__module__, __name__) + self.assertEqual(Emp.__bases__, (dict,)) + self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) + self.assertEqual(Emp.__total__, True) + + def test_basics_keywords_syntax(self): + Emp = TypedDict('Emp', name=str, id=int) + self.assertIsSubclass(Emp, dict) + self.assertIsSubclass(Emp, typing.MutableMapping) + self.assertNotIsSubclass(Emp, collections.abc.Sequence) + jim = Emp(name='Jim', id=1) + self.assertIs(type(jim), dict) + self.assertEqual(jim['name'], 'Jim') + self.assertEqual(jim['id'], 1) + self.assertEqual(Emp.__name__, 'Emp') + self.assertEqual(Emp.__module__, __name__) + self.assertEqual(Emp.__bases__, (dict,)) + self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) + self.assertEqual(Emp.__total__, True) + + def test_typeddict_special_keyword_names(self): + TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, fields=list, _fields=dict) + self.assertEqual(TD.__name__, 'TD') + self.assertEqual(TD.__annotations__, {'cls': type, 'self': object, 'typename': str, '_typename': int, 'fields': list, '_fields': dict}) + a = TD(cls=str, self=42, typename='foo', _typename=53, fields=[('bar', tuple)], _fields={'baz', set}) + self.assertEqual(a['cls'], str) + self.assertEqual(a['self'], 42) + self.assertEqual(a['typename'], 'foo') + self.assertEqual(a['_typename'], 53) + self.assertEqual(a['fields'], [('bar', tuple)]) + self.assertEqual(a['_fields'], {'baz', set}) + + def test_typeddict_create_errors(self): + with self.assertRaises(TypeError): + TypedDict.__new__() + with self.assertRaises(TypeError): + TypedDict() + with self.assertRaises(TypeError): + TypedDict('Emp', [('name', str)], None) + with self.assertRaises(TypeError): + TypedDict(_typename='Emp', name=str, id=int) + with self.assertRaises(TypeError): + TypedDict('Emp', _fields={'name': str, 'id': int}) + + def test_typeddict_errors(self): + Emp = TypedDict('Emp', {'name': str, 'id': int}) + self.assertEqual(TypedDict.__module__, 'typing') + jim = Emp(name='Jim', id=1) + with self.assertRaises(TypeError): + isinstance({}, Emp) + with self.assertRaises(TypeError): + isinstance(jim, Emp) + with self.assertRaises(TypeError): + issubclass(dict, Emp) + with self.assertRaises(TypeError): + TypedDict('Hi', x=1) + with self.assertRaises(TypeError): + TypedDict('Hi', [('x', int), ('y', 1)]) + with self.assertRaises(TypeError): + TypedDict('Hi', [('x', int)], y=int) + + def test_py36_class_syntax_usage(self): + self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') + self.assertEqual(LabelPoint2D.__module__, __name__) + self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) + self.assertEqual(LabelPoint2D.__bases__, (dict,)) + self.assertEqual(LabelPoint2D.__total__, True) + self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) + not_origin = Point2D(x=0, y=1) + self.assertEqual(not_origin['x'], 0) + self.assertEqual(not_origin['y'], 1) + other = LabelPoint2D(x=0, y=1, label='hi') + self.assertEqual(other['label'], 'hi') + + def test_pickle(self): + global EmpD # pickle wants to reference the class by name + EmpD = TypedDict('EmpD', name=str, id=int) + jane = EmpD({'name': 'jane', 'id': 37}) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertEqual(jane2, {'name': 'jane', 'id': 37}) + ZZ = pickle.dumps(EmpD, proto) + EmpDnew = pickle.loads(ZZ) + self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) + + def test_optional(self): + EmpD = TypedDict('EmpD', name=str, id=int) + + self.assertEqual(typing.Optional[EmpD], typing.Union[None, EmpD]) + self.assertNotEqual(typing.List[EmpD], typing.Tuple[EmpD]) + + def test_total(self): + D = TypedDict('D', {'x': int}, total=False) + self.assertEqual(D(), {}) + self.assertEqual(D(x=1), {'x': 1}) + self.assertEqual(D.__total__, False) + + self.assertEqual(Options(), {}) + self.assertEqual(Options(log_level=2), {'log_level': 2}) + self.assertEqual(Options.__total__, False) + + +class IOTests(BaseTestCase): + + def test_io(self): + + def stuff(a: IO) -> AnyStr: + return a.readline() + + a = stuff.__annotations__['a'] + self.assertEqual(a.__parameters__, (AnyStr,)) + + def test_textio(self): + + def stuff(a: TextIO) -> str: + return a.readline() + + a = stuff.__annotations__['a'] + self.assertEqual(a.__parameters__, ()) + + def test_binaryio(self): + + def stuff(a: BinaryIO) -> bytes: + return a.readline() + + a = stuff.__annotations__['a'] + self.assertEqual(a.__parameters__, ()) + + def test_io_submodule(self): + from typing.io import IO, TextIO, BinaryIO, __all__, __name__ + 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') + + +class RETests(BaseTestCase): + # Much of this is really testing _TypeAlias. + + def test_basics(self): + pat = re.compile('[a-z]+', re.I) + self.assertIsSubclass(pat.__class__, Pattern) + self.assertIsSubclass(type(pat), Pattern) + self.assertIsInstance(pat, Pattern) + + mat = pat.search('12345abcde.....') + self.assertIsSubclass(mat.__class__, Match) + self.assertIsSubclass(type(mat), Match) + self.assertIsInstance(mat, Match) + + # these should just work + Pattern[Union[str, bytes]] + Match[Union[bytes, str]] + + def test_alias_equality(self): + self.assertEqual(Pattern[str], Pattern[str]) + self.assertNotEqual(Pattern[str], Pattern[bytes]) + self.assertNotEqual(Pattern[str], Match[str]) + self.assertNotEqual(Pattern[str], str) + + def test_errors(self): + m = Match[Union[str, bytes]] + with self.assertRaises(TypeError): + m[str] + with self.assertRaises(TypeError): + # We don't support isinstance(). + isinstance(42, Pattern[str]) + with self.assertRaises(TypeError): + # We don't support issubclass(). + issubclass(Pattern[bytes], Pattern[str]) + + def test_repr(self): + self.assertEqual(repr(Pattern), 'typing.Pattern') + self.assertEqual(repr(Pattern[str]), 'typing.Pattern[str]') + self.assertEqual(repr(Pattern[bytes]), 'typing.Pattern[bytes]') + self.assertEqual(repr(Match), 'typing.Match') + self.assertEqual(repr(Match[str]), 'typing.Match[str]') + self.assertEqual(repr(Match[bytes]), 'typing.Match[bytes]') + + def test_re_submodule(self): + from typing.re import Match, Pattern, __all__, __name__ + self.assertIs(Match, typing.Match) + self.assertIs(Pattern, typing.Pattern) + self.assertEqual(set(__all__), set(['Match', 'Pattern'])) + self.assertEqual(__name__, 'typing.re') + + def test_cannot_subclass(self): + with self.assertRaises(TypeError) as ex: + + class A(typing.Match): + pass + + self.assertEqual(str(ex.exception), + "type 're.Match' is not an acceptable base type") + + +class AllTests(BaseTestCase): + """Tests for __all__.""" + + def test_all(self): + from typing import __all__ as a + # Just spot-check the first and last of every category. + self.assertIn('AbstractSet', a) + self.assertIn('ValuesView', a) + self.assertIn('cast', a) + self.assertIn('overload', a) + if hasattr(contextlib, 'AbstractContextManager'): + self.assertIn('ContextManager', a) + # Check that io and re are not exported. + self.assertNotIn('io', a) + self.assertNotIn('re', a) + # Spot-check that stdlib modules aren't exported. + self.assertNotIn('os', a) + self.assertNotIn('sys', a) + # Check that Text is defined. + self.assertIn('Text', a) + # Check previously missing classes. + self.assertIn('SupportsBytes', a) + self.assertIn('SupportsComplex', a) + + def test_all_exported_names(self): + import typing + + actual_all = set(typing.__all__) + computed_all = { + k for k, v in vars(typing).items() + # explicitly exported, not a thing with __module__ + if k in actual_all or ( + # avoid private names + not k.startswith('_') and + # avoid things in the io / re typing submodules + k not in typing.io.__all__ and + k not in typing.re.__all__ and + k not in {'io', 're'} and + # there's a few types and metaclasses that aren't exported + not k.endswith(('Meta', '_contra', '_co')) and + not k.upper() == k and + # but export all things that have __module__ == 'typing' + getattr(v, '__module__', None) == typing.__name__ + ) + } + self.assertSetEqual(computed_all, actual_all) + + + +if __name__ == '__main__': + main()