| import sys |
| import os |
| import abc |
| import contextlib |
| import collections |
| import collections.abc |
| from functools import lru_cache |
| import inspect |
| import pickle |
| import subprocess |
| import types |
| from unittest import TestCase, main, skipUnless, skipIf |
| from test import ann_module, ann_module2, ann_module3 |
| import typing |
| from typing import TypeVar, Optional, Union, Any, AnyStr |
| from typing import T, KT, VT # Not in __all__. |
| from typing import Tuple, List, Dict, Iterable, Iterator, Callable |
| from typing import Generic, NamedTuple |
| from typing import no_type_check |
| import typing_extensions |
| from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self |
| from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard |
| from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired |
| from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict |
| from typing_extensions import TypeVarTuple, Unpack, dataclass_transform, reveal_type, Never, assert_never, LiteralString |
| from typing_extensions import assert_type, get_type_hints, get_origin, get_args |
| |
| # Flags used to mark tests that only apply after a specific |
| # version of the typing module. |
| TYPING_3_8_0 = sys.version_info[:3] >= (3, 8, 0) |
| TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0) |
| TYPING_3_11_0 = sys.version_info[:3] >= (3, 11, 0) |
| |
| |
| class BaseTestCase(TestCase): |
| def assertIsSubclass(self, cls, class_or_tuple, msg=None): |
| if not issubclass(cls, class_or_tuple): |
| message = f'{cls!r} is not a subclass of {repr(class_or_tuple)}' |
| if msg is not None: |
| message += f' : {msg}' |
| raise self.failureException(message) |
| |
| def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): |
| if issubclass(cls, class_or_tuple): |
| message = f'{cls!r} is a subclass of {repr(class_or_tuple)}' |
| if msg is not None: |
| message += f' : {msg}' |
| raise self.failureException(message) |
| |
| |
| class Employee: |
| pass |
| |
| |
| class BottomTypeTestsMixin: |
| bottom_type: ClassVar[Any] |
| |
| def test_equality(self): |
| self.assertEqual(self.bottom_type, self.bottom_type) |
| self.assertIs(self.bottom_type, self.bottom_type) |
| self.assertNotEqual(self.bottom_type, None) |
| |
| def test_get_origin(self): |
| self.assertIs(get_origin(self.bottom_type), None) |
| |
| def test_instance_type_error(self): |
| with self.assertRaises(TypeError): |
| isinstance(42, self.bottom_type) |
| |
| def test_subclass_type_error(self): |
| with self.assertRaises(TypeError): |
| issubclass(Employee, self.bottom_type) |
| with self.assertRaises(TypeError): |
| issubclass(NoReturn, self.bottom_type) |
| |
| def test_not_generic(self): |
| with self.assertRaises(TypeError): |
| self.bottom_type[int] |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class A(self.bottom_type): |
| pass |
| with self.assertRaises(TypeError): |
| class A(type(self.bottom_type)): |
| pass |
| |
| def test_cannot_instantiate(self): |
| with self.assertRaises(TypeError): |
| self.bottom_type() |
| with self.assertRaises(TypeError): |
| type(self.bottom_type)() |
| |
| def test_pickle(self): |
| for proto in range(pickle.HIGHEST_PROTOCOL): |
| pickled = pickle.dumps(self.bottom_type, protocol=proto) |
| self.assertIs(self.bottom_type, pickle.loads(pickled)) |
| |
| |
| class NoReturnTests(BottomTypeTestsMixin, BaseTestCase): |
| bottom_type = NoReturn |
| |
| def test_repr(self): |
| if hasattr(typing, 'NoReturn'): |
| self.assertEqual(repr(NoReturn), 'typing.NoReturn') |
| else: |
| self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') |
| |
| def test_get_type_hints(self): |
| def some(arg: NoReturn) -> NoReturn: ... |
| def some_str(arg: 'NoReturn') -> 'typing.NoReturn': ... |
| |
| expected = {'arg': NoReturn, 'return': NoReturn} |
| targets = [some] |
| |
| # On 3.7.0 and 3.7.1, https://github.com/python/cpython/pull/10772 |
| # wasn't applied yet and NoReturn fails _type_check. |
| if not ((3, 7, 0) <= sys.version_info < (3, 7, 2)): |
| targets.append(some_str) |
| for target in targets: |
| with self.subTest(target=target): |
| self.assertEqual(gth(target), expected) |
| |
| def test_not_equality(self): |
| self.assertNotEqual(NoReturn, Never) |
| self.assertNotEqual(Never, NoReturn) |
| |
| |
| class NeverTests(BottomTypeTestsMixin, BaseTestCase): |
| bottom_type = Never |
| |
| def test_repr(self): |
| if hasattr(typing, 'Never'): |
| self.assertEqual(repr(Never), 'typing.Never') |
| else: |
| self.assertEqual(repr(Never), 'typing_extensions.Never') |
| |
| def test_get_type_hints(self): |
| def some(arg: Never) -> Never: ... |
| def some_str(arg: 'Never') -> 'typing_extensions.Never': ... |
| |
| expected = {'arg': Never, 'return': Never} |
| for target in [some, some_str]: |
| with self.subTest(target=target): |
| self.assertEqual(gth(target), expected) |
| |
| |
| class AssertNeverTests(BaseTestCase): |
| def test_exception(self): |
| with self.assertRaises(AssertionError): |
| assert_never(None) |
| |
| |
| 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): |
| if hasattr(typing, 'ClassVar'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(ClassVar), mod_name + '.ClassVar') |
| cv = ClassVar[int] |
| self.assertEqual(repr(cv), mod_name + '.ClassVar[int]') |
| cv = ClassVar[Employee] |
| self.assertEqual(repr(cv), mod_name + f'.ClassVar[{__name__}.Employee]') |
| |
| 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): |
| with self.assertRaises(TypeError): |
| Final[1] |
| with self.assertRaises(TypeError): |
| Final[int, str] |
| with self.assertRaises(TypeError): |
| Final[int][str] |
| |
| def test_repr(self): |
| if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(Final), mod_name + '.Final') |
| cv = Final[int] |
| self.assertEqual(repr(cv), mod_name + '.Final[int]') |
| cv = Final[Employee] |
| self.assertEqual(repr(cv), mod_name + f'.Final[{__name__}.Employee]') |
| |
| 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) |
| |
| |
| class RequiredTests(BaseTestCase): |
| |
| def test_basics(self): |
| with self.assertRaises(TypeError): |
| Required[1] |
| with self.assertRaises(TypeError): |
| Required[int, str] |
| with self.assertRaises(TypeError): |
| Required[int][str] |
| |
| def test_repr(self): |
| if hasattr(typing, 'Required'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(Required), mod_name + '.Required') |
| cv = Required[int] |
| self.assertEqual(repr(cv), mod_name + '.Required[int]') |
| cv = Required[Employee] |
| self.assertEqual(repr(cv), mod_name + '.Required[%s.Employee]' % __name__) |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(type(Required)): |
| pass |
| with self.assertRaises(TypeError): |
| class C(type(Required[int])): |
| pass |
| |
| def test_cannot_init(self): |
| with self.assertRaises(TypeError): |
| Required() |
| with self.assertRaises(TypeError): |
| type(Required)() |
| with self.assertRaises(TypeError): |
| type(Required[Optional[int]])() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(1, Required[int]) |
| with self.assertRaises(TypeError): |
| issubclass(int, Required) |
| |
| |
| class NotRequiredTests(BaseTestCase): |
| |
| def test_basics(self): |
| with self.assertRaises(TypeError): |
| NotRequired[1] |
| with self.assertRaises(TypeError): |
| NotRequired[int, str] |
| with self.assertRaises(TypeError): |
| NotRequired[int][str] |
| |
| def test_repr(self): |
| if hasattr(typing, 'NotRequired'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(NotRequired), mod_name + '.NotRequired') |
| cv = NotRequired[int] |
| self.assertEqual(repr(cv), mod_name + '.NotRequired[int]') |
| cv = NotRequired[Employee] |
| self.assertEqual(repr(cv), mod_name + '.NotRequired[%s.Employee]' % __name__) |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(type(NotRequired)): |
| pass |
| with self.assertRaises(TypeError): |
| class C(type(NotRequired[int])): |
| pass |
| |
| def test_cannot_init(self): |
| with self.assertRaises(TypeError): |
| NotRequired() |
| with self.assertRaises(TypeError): |
| type(NotRequired)() |
| with self.assertRaises(TypeError): |
| type(NotRequired[Optional[int]])() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(1, NotRequired[int]) |
| with self.assertRaises(TypeError): |
| issubclass(int, NotRequired) |
| |
| |
| class IntVarTests(BaseTestCase): |
| def test_valid(self): |
| T_ints = IntVar("T_ints") # noqa |
| |
| def test_invalid(self): |
| with self.assertRaises(TypeError): |
| T_ints = IntVar("T_ints", int) |
| with self.assertRaises(TypeError): |
| T_ints = IntVar("T_ints", bound=int) |
| with self.assertRaises(TypeError): |
| T_ints = IntVar("T_ints", covariant=True) # noqa |
| |
| |
| class LiteralTests(BaseTestCase): |
| def test_basics(self): |
| Literal[1] |
| Literal[1, 2, 3] |
| Literal["x", "y", "z"] |
| Literal[None] |
| |
| 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 maximum flexibility |
| Literal[int] |
| Literal[Literal[1, 2], Literal[4, 5]] |
| Literal[3j + 2, ..., ()] |
| Literal[b"foo", u"bar"] |
| 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): |
| if hasattr(typing, 'Literal'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(Literal[1]), mod_name + ".Literal[1]") |
| self.assertEqual(repr(Literal[1, True, "foo"]), mod_name + ".Literal[1, True, 'foo']") |
| self.assertEqual(repr(Literal[int]), mod_name + ".Literal[int]") |
| self.assertEqual(repr(Literal), mod_name + ".Literal") |
| self.assertEqual(repr(Literal[None]), mod_name + ".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] |
| |
| |
| class OverloadTests(BaseTestCase): |
| |
| def test_overload_fails(self): |
| with self.assertRaises(RuntimeError): |
| |
| @overload |
| def blah(): |
| pass |
| |
| blah() |
| |
| def test_overload_succeeds(self): |
| @overload |
| def blah(): |
| pass |
| |
| def blah(): |
| pass |
| |
| blah() |
| |
| |
| class AssertTypeTests(BaseTestCase): |
| |
| def test_basics(self): |
| arg = 42 |
| self.assertIs(assert_type(arg, int), arg) |
| self.assertIs(assert_type(arg, Union[str, float]), arg) |
| self.assertIs(assert_type(arg, AnyStr), arg) |
| self.assertIs(assert_type(arg, None), arg) |
| |
| def test_errors(self): |
| # Bogus calls are not expected to fail. |
| arg = 42 |
| self.assertIs(assert_type(arg, 42), arg) |
| self.assertIs(assert_type(arg, 'hello'), arg) |
| |
| |
| T_a = TypeVar('T_a') |
| |
| class AwaitableWrapper(Awaitable[T_a]): |
| |
| def __init__(self, value): |
| self.value = value |
| |
| def __await__(self) -> typing.Iterator[T_a]: |
| yield |
| return self.value |
| |
| class AsyncIteratorWrapper(AsyncIterator[T_a]): |
| |
| def __init__(self, value: Iterable[T_a]): |
| self.value = value |
| |
| def __aiter__(self) -> 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 |
| |
| |
| |
| 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 XRepr(NamedTuple): |
| x: int |
| y: int = 1 |
| |
| def __str__(self): |
| return f'{self.x} -> {self.y}' |
| |
| def __add__(self, other): |
| return 0 |
| |
| @runtime |
| class HasCallProtocol(Protocol): |
| __call__: typing.Callable |
| |
| |
| 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 |
| |
| Label = TypedDict('Label', [('label', str)]) |
| |
| class Point2D(TypedDict): |
| x: int |
| y: int |
| |
| class Point2Dor3D(Point2D, total=False): |
| z: int |
| |
| class LabelPoint2D(Point2D, Label): ... |
| |
| class Options(TypedDict, total=False): |
| log_level: int |
| log_path: str |
| |
| class BaseAnimal(TypedDict): |
| name: str |
| |
| class Animal(BaseAnimal, total=False): |
| voice: str |
| tail: bool |
| |
| class Cat(Animal): |
| fur_color: str |
| |
| class TotalMovie(TypedDict): |
| title: str |
| year: NotRequired[int] |
| |
| class NontotalMovie(TypedDict, total=False): |
| title: Required[str] |
| year: int |
| |
| class AnnotatedMovie(TypedDict): |
| title: Annotated[Required[str], "foobar"] |
| year: NotRequired[Annotated[int, 2000]] |
| |
| |
| gth = get_type_hints |
| |
| |
| class GetTypeHintTests(BaseTestCase): |
| def test_get_type_hints_modules(self): |
| ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} |
| if (TYPING_3_11_0 |
| or (TYPING_3_10_0 and sys.version_info.releaselevel in {'candidate', 'final'})): |
| # More tests were added in 3.10rc1. |
| ann_module_type_hints['u'] = int | float |
| self.assertEqual(gth(ann_module), ann_module_type_hints) |
| self.assertEqual(gth(ann_module2), {}) |
| self.assertEqual(gth(ann_module3), {}) |
| |
| def test_get_type_hints_classes(self): |
| self.assertEqual(gth(ann_module.C, ann_module.__dict__), |
| {'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, globals()), |
| {'parent': NoneAndForward, 'meaning': type(None)}) |
| |
| def test_respect_no_type_check(self): |
| @no_type_check |
| class NoTpCheck: |
| class Inn: |
| def __init__(self, x: 'not a type'): ... # noqa |
| 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_ClassVar(self): |
| self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), |
| {'var': 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_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 GetUtilitiesTestCase(TestCase): |
| def test_get_origin(self): |
| T = TypeVar('T') |
| P = ParamSpec('P') |
| Ts = TypeVarTuple('Ts') |
| 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(Annotated[T, 'thing']), Annotated) |
| self.assertIs(get_origin(List), list) |
| self.assertIs(get_origin(Tuple), tuple) |
| self.assertIs(get_origin(Callable), collections.abc.Callable) |
| if sys.version_info >= (3, 9): |
| self.assertIs(get_origin(list[int]), list) |
| self.assertIs(get_origin(list), None) |
| self.assertIs(get_origin(P.args), P) |
| self.assertIs(get_origin(P.kwargs), P) |
| self.assertIs(get_origin(Required[int]), Required) |
| self.assertIs(get_origin(NotRequired[int]), NotRequired) |
| self.assertIs(get_origin(Unpack[Ts]), Unpack) |
| self.assertIs(get_origin(Unpack), None) |
| |
| def test_get_args(self): |
| T = TypeVar('T') |
| Ts = TypeVarTuple('Ts') |
| 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(Annotated[T, 'one', 2, ['three']]), (T, 'one', 2, ['three'])) |
| self.assertEqual(get_args(List), ()) |
| self.assertEqual(get_args(Tuple), ()) |
| self.assertEqual(get_args(Callable), ()) |
| if sys.version_info >= (3, 9): |
| self.assertEqual(get_args(list[int]), (int,)) |
| self.assertEqual(get_args(list), ()) |
| if sys.version_info >= (3, 9): |
| # Support Python versions with and without the fix for |
| # https://bugs.python.org/issue42195 |
| # The first variant is for 3.9.2+, the second for 3.9.0 and 1 |
| self.assertIn(get_args(collections.abc.Callable[[int], str]), |
| (([int], str), ([[int]], str))) |
| self.assertIn(get_args(collections.abc.Callable[[], str]), |
| (([], str), ([[]], str))) |
| self.assertEqual(get_args(collections.abc.Callable[..., str]), (..., str)) |
| P = ParamSpec('P') |
| # In 3.9 and lower we use typing_extensions's hacky implementation |
| # of ParamSpec, which gets incorrectly wrapped in a list |
| self.assertIn(get_args(Callable[P, int]), [(P, int), ([P], int)]) |
| self.assertEqual(get_args(Callable[Concatenate[int, P], int]), |
| (Concatenate[int, P], int)) |
| self.assertEqual(get_args(Required[int]), (int,)) |
| self.assertEqual(get_args(NotRequired[int]), (int,)) |
| self.assertEqual(get_args(Unpack[Ts]), (Ts,)) |
| self.assertEqual(get_args(Unpack), ()) |
| |
| |
| class CollectionsAbcTests(BaseTestCase): |
| |
| def test_isinstance_collections(self): |
| self.assertNotIsInstance(1, collections.abc.Mapping) |
| self.assertNotIsInstance(1, collections.abc.Iterable) |
| self.assertNotIsInstance(1, collections.abc.Container) |
| self.assertNotIsInstance(1, collections.abc.Sized) |
| with self.assertRaises(TypeError): |
| isinstance(collections.deque(), typing_extensions.Deque[int]) |
| with self.assertRaises(TypeError): |
| issubclass(collections.Counter, typing_extensions.Counter[str]) |
| |
| def test_awaitable(self): |
| ns = {} |
| exec( |
| "async def foo() -> typing_extensions.Awaitable[int]:\n" |
| " return await AwaitableWrapper(42)\n", |
| globals(), ns) |
| foo = ns['foo'] |
| g = foo() |
| self.assertIsInstance(g, typing_extensions.Awaitable) |
| self.assertNotIsInstance(foo, typing_extensions.Awaitable) |
| g.send(None) # Run foo() till completion, to avoid warning. |
| |
| def test_coroutine(self): |
| ns = {} |
| exec( |
| "async def foo():\n" |
| " return\n", |
| globals(), ns) |
| foo = ns['foo'] |
| g = foo() |
| self.assertIsInstance(g, typing_extensions.Coroutine) |
| with self.assertRaises(TypeError): |
| isinstance(g, typing_extensions.Coroutine[int]) |
| self.assertNotIsInstance(foo, typing_extensions.Coroutine) |
| try: |
| g.send(None) |
| except StopIteration: |
| pass |
| |
| def test_async_iterable(self): |
| base_it = range(10) # type: Iterator[int] |
| it = AsyncIteratorWrapper(base_it) |
| self.assertIsInstance(it, typing_extensions.AsyncIterable) |
| self.assertIsInstance(it, typing_extensions.AsyncIterable) |
| self.assertNotIsInstance(42, typing_extensions.AsyncIterable) |
| |
| def test_async_iterator(self): |
| base_it = range(10) # type: Iterator[int] |
| it = AsyncIteratorWrapper(base_it) |
| self.assertIsInstance(it, typing_extensions.AsyncIterator) |
| self.assertNotIsInstance(42, typing_extensions.AsyncIterator) |
| |
| def test_deque(self): |
| self.assertIsSubclass(collections.deque, typing_extensions.Deque) |
| class MyDeque(typing_extensions.Deque[int]): ... |
| self.assertIsInstance(MyDeque(), collections.deque) |
| |
| def test_counter(self): |
| self.assertIsSubclass(collections.Counter, typing_extensions.Counter) |
| |
| def test_defaultdict_instantiation(self): |
| self.assertIs( |
| type(typing_extensions.DefaultDict()), |
| collections.defaultdict) |
| self.assertIs( |
| type(typing_extensions.DefaultDict[KT, VT]()), |
| collections.defaultdict) |
| self.assertIs( |
| type(typing_extensions.DefaultDict[str, int]()), |
| collections.defaultdict) |
| |
| def test_defaultdict_subclass(self): |
| |
| class MyDefDict(typing_extensions.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_extensions.OrderedDict()), |
| collections.OrderedDict) |
| self.assertIs( |
| type(typing_extensions.OrderedDict[KT, VT]()), |
| collections.OrderedDict) |
| self.assertIs( |
| type(typing_extensions.OrderedDict[str, int]()), |
| collections.OrderedDict) |
| |
| def test_ordereddict_subclass(self): |
| |
| class MyOrdDict(typing_extensions.OrderedDict[str, int]): |
| pass |
| |
| od = MyOrdDict() |
| self.assertIsInstance(od, MyOrdDict) |
| |
| self.assertIsSubclass(MyOrdDict, collections.OrderedDict) |
| self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) |
| |
| def test_chainmap_instantiation(self): |
| self.assertIs(type(typing_extensions.ChainMap()), collections.ChainMap) |
| self.assertIs(type(typing_extensions.ChainMap[KT, VT]()), collections.ChainMap) |
| self.assertIs(type(typing_extensions.ChainMap[str, int]()), collections.ChainMap) |
| class CM(typing_extensions.ChainMap[KT, VT]): ... |
| self.assertIs(type(CM[int, str]()), CM) |
| |
| def test_chainmap_subclass(self): |
| |
| class MyChainMap(typing_extensions.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_extensions.Deque()), collections.deque) |
| self.assertIs(type(typing_extensions.Deque[T]()), collections.deque) |
| self.assertIs(type(typing_extensions.Deque[int]()), collections.deque) |
| class D(typing_extensions.Deque[T]): ... |
| self.assertIs(type(D[int]()), D) |
| |
| def test_counter_instantiation(self): |
| self.assertIs(type(typing_extensions.Counter()), collections.Counter) |
| self.assertIs(type(typing_extensions.Counter[T]()), collections.Counter) |
| self.assertIs(type(typing_extensions.Counter[int]()), collections.Counter) |
| class C(typing_extensions.Counter[T]): ... |
| self.assertIs(type(C[int]()), C) |
| self.assertEqual(C.__bases__, (collections.Counter, typing.Generic)) |
| |
| def test_counter_subclass_instantiation(self): |
| |
| class MyCounter(typing_extensions.Counter[int]): |
| pass |
| |
| d = MyCounter() |
| self.assertIsInstance(d, MyCounter) |
| self.assertIsInstance(d, collections.Counter) |
| self.assertIsInstance(d, typing_extensions.Counter) |
| |
| def test_async_generator(self): |
| ns = {} |
| exec("async def f():\n" |
| " yield 42\n", globals(), ns) |
| g = ns['f']() |
| self.assertIsSubclass(type(g), typing_extensions.AsyncGenerator) |
| |
| def test_no_async_generator_instantiation(self): |
| with self.assertRaises(TypeError): |
| typing_extensions.AsyncGenerator() |
| with self.assertRaises(TypeError): |
| typing_extensions.AsyncGenerator[T, T]() |
| with self.assertRaises(TypeError): |
| typing_extensions.AsyncGenerator[int, int]() |
| |
| def test_subclassing_async_generator(self): |
| class G(typing_extensions.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_extensions.AsyncGenerator) |
| self.assertIsSubclass(G, typing_extensions.AsyncIterable) |
| self.assertIsSubclass(G, collections.abc.AsyncGenerator) |
| self.assertIsSubclass(G, collections.abc.AsyncIterable) |
| self.assertNotIsSubclass(type(g), G) |
| |
| instance = G() |
| self.assertIsInstance(instance, typing_extensions.AsyncGenerator) |
| self.assertIsInstance(instance, typing_extensions.AsyncIterable) |
| self.assertIsInstance(instance, collections.abc.AsyncGenerator) |
| self.assertIsInstance(instance, collections.abc.AsyncIterable) |
| self.assertNotIsInstance(type(g), G) |
| self.assertNotIsInstance(g, G) |
| |
| |
| class OtherABCTests(BaseTestCase): |
| |
| def test_contextmanager(self): |
| @contextlib.contextmanager |
| def manager(): |
| yield 42 |
| |
| cm = manager() |
| self.assertIsInstance(cm, typing_extensions.ContextManager) |
| self.assertNotIsInstance(42, typing_extensions.ContextManager) |
| |
| def test_async_contextmanager(self): |
| class NotACM: |
| pass |
| self.assertIsInstance(ACM(), typing_extensions.AsyncContextManager) |
| self.assertNotIsInstance(NotACM(), typing_extensions.AsyncContextManager) |
| @contextlib.contextmanager |
| def manager(): |
| yield 42 |
| |
| cm = manager() |
| self.assertNotIsInstance(cm, typing_extensions.AsyncContextManager) |
| self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,)) |
| with self.assertRaises(TypeError): |
| isinstance(42, typing_extensions.AsyncContextManager[int]) |
| with self.assertRaises(TypeError): |
| typing_extensions.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 Coordinate(Protocol): |
| x: int |
| y: int |
| |
| @runtime |
| 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 |
| class Position(XAxis, YAxis, Protocol): |
| pass |
| |
| @runtime |
| 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 |
| |
| |
| class ProtocolTests(BaseTestCase): |
| |
| def test_basic_protocol(self): |
| @runtime |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| class P(Protocol): |
| def x(self): ... |
| @runtime |
| 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 |
| 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 |
| class P(Protocol): |
| def meth(x): ... |
| @runtime |
| 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 |
| class P(Protocol): |
| x = 1 |
| @runtime |
| 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_protocols_support_register(self): |
| @runtime |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| class PR(Protocol[T, S]): |
| def meth(self): pass |
| class P(PR[int, T], Protocol[T]): |
| y = 1 |
| with self.assertRaises(TypeError): |
| issubclass(PR[int, T], PR) |
| with self.assertRaises(TypeError): |
| issubclass(P[str], PR) |
| with self.assertRaises(TypeError): |
| PR[int] |
| with self.assertRaises(TypeError): |
| P[int, str] |
| if not TYPING_3_10_0: |
| 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 |
| class PR(Protocol, Generic[T, S]): |
| def meth(self): pass |
| class P(PR[int, str], Protocol): |
| y = 1 |
| with self.assertRaises(TypeError): |
| self.assertIsSubclass(PR[int, str], PR) |
| self.assertIsSubclass(P, PR) |
| with self.assertRaises(TypeError): |
| PR[int] |
| if not TYPING_3_10_0: |
| 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 |
| class PSub(P1[str], Protocol): |
| x = 1 |
| class Test: |
| x = 1 |
| def bar(self, x: str) -> str: |
| return x |
| self.assertIsInstance(Test(), PSub) |
| if not TYPING_3_10_0: |
| 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 |
| 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) |
| if hasattr(typing, 'Protocol'): |
| self.assertFalse(P._is_runtime_protocol) |
| else: |
| with self.assertRaises(AttributeError): |
| self.assertFalse(P._is_runtime_protocol) |
| self.assertTrue(PR._is_runtime_protocol) |
| self.assertTrue(PG[int]._is_protocol) |
| self.assertEqual(typing_extensions._get_protocol_attrs(P), {'meth'}) |
| self.assertEqual(typing_extensions._get_protocol_attrs(PR), {'x'}) |
| self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG)), |
| frozenset({'x', 'meth'})) |
| |
| def test_no_runtime_deco_on_nominal(self): |
| with self.assertRaises(TypeError): |
| @runtime |
| class C: pass |
| class Proto(Protocol): |
| x = 1 |
| with self.assertRaises(TypeError): |
| @runtime |
| class Concrete(Proto): |
| pass |
| |
| def test_none_treated_correctly(self): |
| @runtime |
| 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 |
| 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_collections_protocols_allowed(self): |
| @runtime_checkable |
| class Custom(collections.abc.Iterable, Protocol): |
| def close(self): pass |
| |
| class A: ... |
| class B: |
| def __iter__(self): |
| return [] |
| def close(self): |
| return 0 |
| |
| self.assertIsSubclass(B, Custom) |
| self.assertNotIsSubclass(A, Custom) |
| |
| def test_no_init_same_for_different_protocol_implementations(self): |
| class CustomProtocolWithoutInitA(Protocol): |
| pass |
| |
| class CustomProtocolWithoutInitB(Protocol): |
| pass |
| |
| self.assertEqual(CustomProtocolWithoutInitA.__init__, CustomProtocolWithoutInitB.__init__) |
| |
| |
| class TypedDictTests(BaseTestCase): |
| |
| def test_basics_iterable_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}) |
| |
| @skipIf(hasattr(typing, 'TypedDict'), "Should be tested by upstream") |
| 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.assertWarns(DeprecationWarning): |
| Emp = TypedDict(_typename='Emp', name=str, id=int) |
| self.assertEqual(Emp.__name__, 'Emp') |
| self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) |
| |
| with self.assertWarns(DeprecationWarning): |
| Emp = TypedDict('Emp', _fields={'name': str, 'id': int}) |
| self.assertEqual(Emp.__name__, 'Emp') |
| self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) |
| |
| def test_typeddict_errors(self): |
| Emp = TypedDict('Emp', {'name': str, 'id': int}) |
| if hasattr(typing, "Required"): |
| self.assertEqual(TypedDict.__module__, 'typing') |
| else: |
| self.assertEqual(TypedDict.__module__, 'typing_extensions') |
| 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(get_type_hints(LabelPoint2D), {'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(D.__required_keys__, frozenset()) |
| self.assertEqual(D.__optional_keys__, {'x'}) |
| |
| self.assertEqual(Options(), {}) |
| self.assertEqual(Options(log_level=2), {'log_level': 2}) |
| self.assertEqual(Options.__total__, False) |
| self.assertEqual(Options.__required_keys__, frozenset()) |
| self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'}) |
| |
| def test_optional_keys(self): |
| assert Point2Dor3D.__required_keys__ == frozenset(['x', 'y']) |
| assert Point2Dor3D.__optional_keys__ == frozenset(['z']) |
| |
| def test_required_notrequired_keys(self): |
| assert NontotalMovie.__required_keys__ == frozenset({'title'}) |
| assert NontotalMovie.__optional_keys__ == frozenset({'year'}) |
| |
| assert TotalMovie.__required_keys__ == frozenset({'title'}) |
| assert TotalMovie.__optional_keys__ == frozenset({'year'}) |
| |
| |
| def test_keys_inheritance(self): |
| assert BaseAnimal.__required_keys__ == frozenset(['name']) |
| assert BaseAnimal.__optional_keys__ == frozenset([]) |
| assert get_type_hints(BaseAnimal) == {'name': str} |
| |
| assert Animal.__required_keys__ == frozenset(['name']) |
| assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) |
| assert get_type_hints(Animal) == { |
| 'name': str, |
| 'tail': bool, |
| 'voice': str, |
| } |
| |
| assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) |
| assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) |
| assert get_type_hints(Cat) == { |
| 'fur_color': str, |
| 'name': str, |
| 'tail': bool, |
| 'voice': str, |
| } |
| |
| def test_is_typeddict(self): |
| assert is_typeddict(Point2D) is True |
| assert is_typeddict(Point2Dor3D) is True |
| assert is_typeddict(Union[str, int]) is False |
| # classes, not instances |
| assert is_typeddict(Point2D()) is False |
| |
| @skipUnless(TYPING_3_8_0, "Python 3.8+ required") |
| def test_is_typeddict_against_typeddict_from_typing(self): |
| Point = typing.TypedDict('Point', {'x': int, 'y': int}) |
| |
| class PointDict2D(typing.TypedDict): |
| x: int |
| y: int |
| |
| class PointDict3D(PointDict2D, total=False): |
| z: int |
| |
| assert is_typeddict(Point) is True |
| assert is_typeddict(PointDict2D) is True |
| assert is_typeddict(PointDict3D) is True |
| |
| |
| class AnnotatedTests(BaseTestCase): |
| |
| def test_repr(self): |
| if hasattr(typing, 'Annotated'): |
| mod_name = 'typing' |
| else: |
| mod_name = "typing_extensions" |
| self.assertEqual( |
| repr(Annotated[int, 4, 5]), |
| mod_name + ".Annotated[int, 4, 5]" |
| ) |
| self.assertEqual( |
| repr(Annotated[List[int], 4, 5]), |
| mod_name + ".Annotated[typing.List[int], 4, 5]" |
| ) |
| |
| def test_flatten(self): |
| A = Annotated[Annotated[int, 4], 5] |
| self.assertEqual(A, Annotated[int, 4, 5]) |
| self.assertEqual(A.__metadata__, (4, 5)) |
| self.assertEqual(A.__origin__, int) |
| |
| def test_specialize(self): |
| L = Annotated[List[T], "my decoration"] |
| LI = Annotated[List[int], "my decoration"] |
| self.assertEqual(L[int], Annotated[List[int], "my decoration"]) |
| self.assertEqual(L[int].__metadata__, ("my decoration",)) |
| self.assertEqual(L[int].__origin__, List[int]) |
| with self.assertRaises(TypeError): |
| LI[int] |
| with self.assertRaises(TypeError): |
| L[int, float] |
| |
| def test_hash_eq(self): |
| self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) |
| self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) |
| self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) |
| self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) |
| self.assertEqual( |
| {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, |
| {Annotated[int, 4, 5], Annotated[T, 4, 5]} |
| ) |
| |
| def test_instantiate(self): |
| class C: |
| classvar = 4 |
| |
| def __init__(self, x): |
| self.x = x |
| |
| def __eq__(self, other): |
| if not isinstance(other, C): |
| return NotImplemented |
| return other.x == self.x |
| |
| A = Annotated[C, "a decoration"] |
| a = A(5) |
| c = C(5) |
| self.assertEqual(a, c) |
| self.assertEqual(a.x, c.x) |
| self.assertEqual(a.classvar, c.classvar) |
| |
| def test_instantiate_generic(self): |
| MyCount = Annotated[typing_extensions.Counter[T], "my decoration"] |
| self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1}) |
| self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1}) |
| |
| def test_cannot_instantiate_forward(self): |
| A = Annotated["int", (5, 6)] |
| with self.assertRaises(TypeError): |
| A(5) |
| |
| def test_cannot_instantiate_type_var(self): |
| A = Annotated[T, (5, 6)] |
| with self.assertRaises(TypeError): |
| A(5) |
| |
| def test_cannot_getattr_typevar(self): |
| with self.assertRaises(AttributeError): |
| Annotated[T, (5, 7)].x |
| |
| def test_attr_passthrough(self): |
| class C: |
| classvar = 4 |
| |
| A = Annotated[C, "a decoration"] |
| self.assertEqual(A.classvar, 4) |
| A.x = 5 |
| self.assertEqual(C.x, 5) |
| |
| @skipIf(sys.version_info[:2] in ((3, 9), (3, 10)), "Waiting for bpo-46491 bugfix.") |
| def test_special_form_containment(self): |
| class C: |
| classvar: Annotated[ClassVar[int], "a decoration"] = 4 |
| const: Annotated[Final[int], "Const"] = 4 |
| |
| if sys.version_info[:2] >= (3, 7): |
| self.assertEqual(get_type_hints(C, globals())["classvar"], ClassVar[int]) |
| self.assertEqual(get_type_hints(C, globals())["const"], Final[int]) |
| else: |
| self.assertEqual( |
| get_type_hints(C, globals())["classvar"], |
| Annotated[ClassVar[int], "a decoration"] |
| ) |
| self.assertEqual( |
| get_type_hints(C, globals())["const"], Annotated[Final[int], "Const"] |
| ) |
| |
| def test_hash_eq(self): |
| self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) |
| self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) |
| self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) |
| self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) |
| self.assertEqual( |
| {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, |
| {Annotated[int, 4, 5], Annotated[T, 4, 5]} |
| ) |
| |
| def test_cannot_subclass(self): |
| with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"): |
| class C(Annotated): |
| pass |
| |
| def test_cannot_check_instance(self): |
| with self.assertRaises(TypeError): |
| isinstance(5, Annotated[int, "positive"]) |
| |
| def test_cannot_check_subclass(self): |
| with self.assertRaises(TypeError): |
| issubclass(int, Annotated[int, "positive"]) |
| |
| def test_pickle(self): |
| samples = [typing.Any, typing.Union[int, str], |
| typing.Optional[str], Tuple[int, ...], |
| typing.Callable[[str], bytes], |
| Self, LiteralString, Never] |
| |
| for t in samples: |
| x = Annotated[t, "a"] |
| |
| for prot in range(pickle.HIGHEST_PROTOCOL + 1): |
| with self.subTest(protocol=prot, type=t): |
| pickled = pickle.dumps(x, prot) |
| restored = pickle.loads(pickled) |
| self.assertEqual(x, restored) |
| |
| global _Annotated_test_G |
| |
| class _Annotated_test_G(Generic[T]): |
| x = 1 |
| |
| G = Annotated[_Annotated_test_G[int], "A decoration"] |
| G.foo = 42 |
| G.bar = 'abc' |
| |
| for proto in range(pickle.HIGHEST_PROTOCOL + 1): |
| z = pickle.dumps(G, proto) |
| x = pickle.loads(z) |
| self.assertEqual(x.foo, 42) |
| self.assertEqual(x.bar, 'abc') |
| self.assertEqual(x.x, 1) |
| |
| def test_subst(self): |
| dec = "a decoration" |
| dec2 = "another decoration" |
| |
| S = Annotated[T, dec2] |
| self.assertEqual(S[int], Annotated[int, dec2]) |
| |
| self.assertEqual(S[Annotated[int, dec]], Annotated[int, dec, dec2]) |
| L = Annotated[List[T], dec] |
| |
| self.assertEqual(L[int], Annotated[List[int], dec]) |
| with self.assertRaises(TypeError): |
| L[int, int] |
| |
| self.assertEqual(S[L[int]], Annotated[List[int], dec, dec2]) |
| |
| D = Annotated[Dict[KT, VT], dec] |
| self.assertEqual(D[str, int], Annotated[Dict[str, int], dec]) |
| with self.assertRaises(TypeError): |
| D[int] |
| |
| It = Annotated[int, dec] |
| with self.assertRaises(TypeError): |
| It[None] |
| |
| LI = L[int] |
| with self.assertRaises(TypeError): |
| LI[None] |
| |
| def test_annotated_in_other_types(self): |
| X = List[Annotated[T, 5]] |
| self.assertEqual(X[int], List[Annotated[int, 5]]) |
| |
| |
| class GetTypeHintsTests(BaseTestCase): |
| def test_get_type_hints(self): |
| def foobar(x: List['X']): ... |
| X = Annotated[int, (1, 10)] |
| self.assertEqual( |
| get_type_hints(foobar, globals(), locals()), |
| {'x': List[int]} |
| ) |
| self.assertEqual( |
| get_type_hints(foobar, globals(), locals(), include_extras=True), |
| {'x': List[Annotated[int, (1, 10)]]} |
| ) |
| BA = Tuple[Annotated[T, (1, 0)], ...] |
| def barfoo(x: BA): ... |
| self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...]) |
| self.assertIs( |
| get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'], |
| BA |
| ) |
| def barfoo2(x: typing.Callable[..., Annotated[List[T], "const"]], |
| y: typing.Union[int, Annotated[T, "mutable"]]): ... |
| self.assertEqual( |
| get_type_hints(barfoo2, globals(), locals()), |
| {'x': typing.Callable[..., List[T]], 'y': typing.Union[int, T]} |
| ) |
| BA2 = typing.Callable[..., List[T]] |
| def barfoo3(x: BA2): ... |
| self.assertIs( |
| get_type_hints(barfoo3, globals(), locals(), include_extras=True)["x"], |
| BA2 |
| ) |
| |
| def test_get_type_hints_refs(self): |
| |
| Const = Annotated[T, "Const"] |
| |
| class MySet(Generic[T]): |
| |
| def __ior__(self, other: "Const[MySet[T]]") -> "MySet[T]": |
| ... |
| |
| def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]": |
| ... |
| |
| self.assertEqual( |
| get_type_hints(MySet.__iand__, globals(), locals()), |
| {'other': MySet[T], 'return': MySet[T]} |
| ) |
| |
| self.assertEqual( |
| get_type_hints(MySet.__iand__, globals(), locals(), include_extras=True), |
| {'other': Const[MySet[T]], 'return': MySet[T]} |
| ) |
| |
| self.assertEqual( |
| get_type_hints(MySet.__ior__, globals(), locals()), |
| {'other': MySet[T], 'return': MySet[T]} |
| ) |
| |
| def test_get_type_hints_typeddict(self): |
| assert get_type_hints(TotalMovie) == {'title': str, 'year': int} |
| assert get_type_hints(TotalMovie, include_extras=True) == { |
| 'title': str, |
| 'year': NotRequired[int], |
| } |
| |
| assert get_type_hints(AnnotatedMovie) == {'title': str, 'year': int} |
| assert get_type_hints(AnnotatedMovie, include_extras=True) == { |
| 'title': Annotated[Required[str], "foobar"], |
| 'year': NotRequired[Annotated[int, 2000]], |
| } |
| |
| |
| class TypeAliasTests(BaseTestCase): |
| def test_canonical_usage_with_variable_annotation(self): |
| ns = {} |
| exec('Alias: TypeAlias = Employee', globals(), ns) |
| |
| def test_canonical_usage_with_type_comment(self): |
| Alias = Employee # type: TypeAlias |
| |
| def test_cannot_instantiate(self): |
| with self.assertRaises(TypeError): |
| TypeAlias() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(42, TypeAlias) |
| |
| def test_no_issubclass(self): |
| with self.assertRaises(TypeError): |
| issubclass(Employee, TypeAlias) |
| |
| with self.assertRaises(TypeError): |
| issubclass(TypeAlias, Employee) |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(TypeAlias): |
| pass |
| |
| with self.assertRaises(TypeError): |
| class C(type(TypeAlias)): |
| pass |
| |
| def test_repr(self): |
| if hasattr(typing, 'TypeAlias'): |
| self.assertEqual(repr(TypeAlias), 'typing.TypeAlias') |
| else: |
| self.assertEqual(repr(TypeAlias), 'typing_extensions.TypeAlias') |
| |
| def test_cannot_subscript(self): |
| with self.assertRaises(TypeError): |
| TypeAlias[int] |
| |
| class ParamSpecTests(BaseTestCase): |
| |
| def test_basic_plain(self): |
| P = ParamSpec('P') |
| self.assertEqual(P, P) |
| self.assertIsInstance(P, ParamSpec) |
| # Should be hashable |
| hash(P) |
| |
| def test_repr(self): |
| P = ParamSpec('P') |
| P_co = ParamSpec('P_co', covariant=True) |
| P_contra = ParamSpec('P_contra', contravariant=True) |
| P_2 = ParamSpec('P_2') |
| self.assertEqual(repr(P), '~P') |
| self.assertEqual(repr(P_2), '~P_2') |
| |
| # Note: PEP 612 doesn't require these to be repr-ed correctly, but |
| # just follow CPython. |
| self.assertEqual(repr(P_co), '+P_co') |
| self.assertEqual(repr(P_contra), '-P_contra') |
| |
| def test_valid_uses(self): |
| P = ParamSpec('P') |
| T = TypeVar('T') |
| C1 = typing.Callable[P, int] |
| self.assertEqual(C1.__args__, (P, int)) |
| self.assertEqual(C1.__parameters__, (P,)) |
| C2 = typing.Callable[P, T] |
| self.assertEqual(C2.__args__, (P, T)) |
| self.assertEqual(C2.__parameters__, (P, T)) |
| |
| |
| # Test collections.abc.Callable too. |
| if sys.version_info[:2] >= (3, 9): |
| # Note: no tests for Callable.__parameters__ here |
| # because types.GenericAlias Callable is hardcoded to search |
| # for tp_name "TypeVar" in C. This was changed in 3.10. |
| C3 = collections.abc.Callable[P, int] |
| self.assertEqual(C3.__args__, (P, int)) |
| C4 = collections.abc.Callable[P, T] |
| self.assertEqual(C4.__args__, (P, T)) |
| |
| # ParamSpec instances should also have args and kwargs attributes. |
| # Note: not in dir(P) because of __class__ hacks |
| self.assertTrue(hasattr(P, 'args')) |
| self.assertTrue(hasattr(P, 'kwargs')) |
| |
| @skipIf((3, 10, 0) <= sys.version_info[:3] <= (3, 10, 2), "Needs bpo-46676.") |
| def test_args_kwargs(self): |
| P = ParamSpec('P') |
| P_2 = ParamSpec('P_2') |
| # Note: not in dir(P) because of __class__ hacks |
| self.assertTrue(hasattr(P, 'args')) |
| self.assertTrue(hasattr(P, 'kwargs')) |
| self.assertIsInstance(P.args, ParamSpecArgs) |
| self.assertIsInstance(P.kwargs, ParamSpecKwargs) |
| self.assertIs(P.args.__origin__, P) |
| self.assertIs(P.kwargs.__origin__, P) |
| self.assertEqual(P.args, P.args) |
| self.assertEqual(P.kwargs, P.kwargs) |
| self.assertNotEqual(P.args, P_2.args) |
| self.assertNotEqual(P.kwargs, P_2.kwargs) |
| self.assertNotEqual(P.args, P.kwargs) |
| self.assertNotEqual(P.kwargs, P.args) |
| self.assertNotEqual(P.args, P_2.kwargs) |
| self.assertEqual(repr(P.args), "P.args") |
| self.assertEqual(repr(P.kwargs), "P.kwargs") |
| |
| def test_user_generics(self): |
| T = TypeVar("T") |
| P = ParamSpec("P") |
| P_2 = ParamSpec("P_2") |
| |
| class X(Generic[T, P]): |
| pass |
| |
| G1 = X[int, P_2] |
| self.assertEqual(G1.__args__, (int, P_2)) |
| self.assertEqual(G1.__parameters__, (P_2,)) |
| |
| G2 = X[int, Concatenate[int, P_2]] |
| self.assertEqual(G2.__args__, (int, Concatenate[int, P_2])) |
| self.assertEqual(G2.__parameters__, (P_2,)) |
| |
| # The following are some valid uses cases in PEP 612 that don't work: |
| # These do not work in 3.9, _type_check blocks the list and ellipsis. |
| # G3 = X[int, [int, bool]] |
| # G4 = X[int, ...] |
| # G5 = Z[[int, str, bool]] |
| # Not working because this is special-cased in 3.10. |
| # G6 = Z[int, str, bool] |
| |
| class Z(Generic[P]): |
| pass |
| |
| def test_pickle(self): |
| global P, P_co, P_contra |
| P = ParamSpec('P') |
| P_co = ParamSpec('P_co', covariant=True) |
| P_contra = ParamSpec('P_contra', contravariant=True) |
| for proto in range(pickle.HIGHEST_PROTOCOL): |
| with self.subTest(f'Pickle protocol {proto}'): |
| for paramspec in (P, P_co, P_contra): |
| z = pickle.loads(pickle.dumps(paramspec, proto)) |
| self.assertEqual(z.__name__, paramspec.__name__) |
| self.assertEqual(z.__covariant__, paramspec.__covariant__) |
| self.assertEqual(z.__contravariant__, paramspec.__contravariant__) |
| self.assertEqual(z.__bound__, paramspec.__bound__) |
| |
| def test_eq(self): |
| P = ParamSpec('P') |
| self.assertEqual(P, P) |
| self.assertEqual(hash(P), hash(P)) |
| # ParamSpec should compare by id similar to TypeVar in CPython |
| self.assertNotEqual(ParamSpec('P'), P) |
| self.assertIsNot(ParamSpec('P'), P) |
| # Note: normally you don't test this as it breaks when there's |
| # a hash collision. However, ParamSpec *must* guarantee that |
| # as long as two objects don't have the same ID, their hashes |
| # won't be the same. |
| self.assertNotEqual(hash(ParamSpec('P')), hash(P)) |
| |
| |
| class ConcatenateTests(BaseTestCase): |
| def test_basics(self): |
| P = ParamSpec('P') |
| |
| class MyClass: ... |
| |
| c = Concatenate[MyClass, P] |
| self.assertNotEqual(c, Concatenate) |
| |
| def test_valid_uses(self): |
| P = ParamSpec('P') |
| T = TypeVar('T') |
| |
| C1 = Callable[Concatenate[int, P], int] |
| C2 = Callable[Concatenate[int, T, P], T] |
| |
| # Test collections.abc.Callable too. |
| if sys.version_info[:2] >= (3, 9): |
| C3 = collections.abc.Callable[Concatenate[int, P], int] |
| C4 = collections.abc.Callable[Concatenate[int, T, P], T] |
| |
| def test_invalid_uses(self): |
| P = ParamSpec('P') |
| T = TypeVar('T') |
| |
| with self.assertRaisesRegex( |
| TypeError, |
| 'Cannot take a Concatenate of no types', |
| ): |
| Concatenate[()] |
| |
| with self.assertRaisesRegex( |
| TypeError, |
| 'The last parameter to Concatenate should be a ParamSpec variable', |
| ): |
| Concatenate[P, T] |
| |
| with self.assertRaisesRegex( |
| TypeError, |
| 'each arg must be a type', |
| ): |
| Concatenate[1, P] |
| |
| def test_basic_introspection(self): |
| P = ParamSpec('P') |
| C1 = Concatenate[int, P] |
| C2 = Concatenate[int, T, P] |
| self.assertEqual(C1.__origin__, Concatenate) |
| self.assertEqual(C1.__args__, (int, P)) |
| self.assertEqual(C2.__origin__, Concatenate) |
| self.assertEqual(C2.__args__, (int, T, P)) |
| |
| def test_eq(self): |
| P = ParamSpec('P') |
| C1 = Concatenate[int, P] |
| C2 = Concatenate[int, P] |
| C3 = Concatenate[int, T, P] |
| self.assertEqual(C1, C2) |
| self.assertEqual(hash(C1), hash(C2)) |
| self.assertNotEqual(C1, C3) |
| |
| |
| class TypeGuardTests(BaseTestCase): |
| def test_basics(self): |
| TypeGuard[int] # OK |
| self.assertEqual(TypeGuard[int], TypeGuard[int]) |
| |
| def foo(arg) -> TypeGuard[int]: ... |
| self.assertEqual(gth(foo), {'return': TypeGuard[int]}) |
| |
| def test_repr(self): |
| if hasattr(typing, 'TypeGuard'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(TypeGuard), f'{mod_name}.TypeGuard') |
| cv = TypeGuard[int] |
| self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[int]') |
| cv = TypeGuard[Employee] |
| self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[{__name__}.Employee]') |
| cv = TypeGuard[Tuple[int]] |
| self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[typing.Tuple[int]]') |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(type(TypeGuard)): |
| pass |
| with self.assertRaises(TypeError): |
| class C(type(TypeGuard[int])): |
| pass |
| |
| def test_cannot_init(self): |
| with self.assertRaises(TypeError): |
| TypeGuard() |
| with self.assertRaises(TypeError): |
| type(TypeGuard)() |
| with self.assertRaises(TypeError): |
| type(TypeGuard[Optional[int]])() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(1, TypeGuard[int]) |
| with self.assertRaises(TypeError): |
| issubclass(int, TypeGuard) |
| |
| |
| class LiteralStringTests(BaseTestCase): |
| def test_basics(self): |
| class Foo: |
| def bar(self) -> LiteralString: ... |
| def baz(self) -> "LiteralString": ... |
| |
| self.assertEqual(gth(Foo.bar), {'return': LiteralString}) |
| self.assertEqual(gth(Foo.baz), {'return': LiteralString}) |
| |
| def test_get_origin(self): |
| self.assertIsNone(get_origin(LiteralString)) |
| |
| def test_repr(self): |
| if hasattr(typing, 'LiteralString'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(LiteralString), '{}.LiteralString'.format(mod_name)) |
| |
| def test_cannot_subscript(self): |
| with self.assertRaises(TypeError): |
| LiteralString[int] |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(type(LiteralString)): |
| pass |
| with self.assertRaises(TypeError): |
| class C(LiteralString): |
| pass |
| |
| def test_cannot_init(self): |
| with self.assertRaises(TypeError): |
| LiteralString() |
| with self.assertRaises(TypeError): |
| type(LiteralString)() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(1, LiteralString) |
| with self.assertRaises(TypeError): |
| issubclass(int, LiteralString) |
| |
| def test_alias(self): |
| StringTuple = Tuple[LiteralString, LiteralString] |
| class Alias: |
| def return_tuple(self) -> StringTuple: |
| return ("foo", "pep" + "675") |
| |
| def test_typevar(self): |
| StrT = TypeVar("StrT", bound=LiteralString) |
| self.assertIs(StrT.__bound__, LiteralString) |
| |
| def test_pickle(self): |
| for proto in range(pickle.HIGHEST_PROTOCOL): |
| pickled = pickle.dumps(LiteralString, protocol=proto) |
| self.assertIs(LiteralString, pickle.loads(pickled)) |
| |
| |
| class SelfTests(BaseTestCase): |
| def test_basics(self): |
| class Foo: |
| def bar(self) -> Self: ... |
| |
| self.assertEqual(gth(Foo.bar), {'return': Self}) |
| |
| def test_repr(self): |
| if hasattr(typing, 'Self'): |
| mod_name = 'typing' |
| else: |
| mod_name = 'typing_extensions' |
| self.assertEqual(repr(Self), '{}.Self'.format(mod_name)) |
| |
| def test_cannot_subscript(self): |
| with self.assertRaises(TypeError): |
| Self[int] |
| |
| def test_cannot_subclass(self): |
| with self.assertRaises(TypeError): |
| class C(type(Self)): |
| pass |
| |
| def test_cannot_init(self): |
| with self.assertRaises(TypeError): |
| Self() |
| with self.assertRaises(TypeError): |
| type(Self)() |
| |
| def test_no_isinstance(self): |
| with self.assertRaises(TypeError): |
| isinstance(1, Self) |
| with self.assertRaises(TypeError): |
| issubclass(int, Self) |
| |
| def test_alias(self): |
| TupleSelf = Tuple[Self, Self] |
| class Alias: |
| def return_tuple(self) -> TupleSelf: |
| return (self, self) |
| |
| def test_pickle(self): |
| for proto in range(pickle.HIGHEST_PROTOCOL): |
| pickled = pickle.dumps(Self, protocol=proto) |
| self.assertIs(Self, pickle.loads(pickled)) |
| |
| |
| class UnpackTests(BaseTestCase): |
| def test_basic_plain(self): |
| Ts = TypeVarTuple('Ts') |
| self.assertEqual(Unpack[Ts], Unpack[Ts]) |
| with self.assertRaises(TypeError): |
| Unpack() |
| |
| def test_repr(self): |
| Ts = TypeVarTuple('Ts') |
| self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]') |
| |
| def test_cannot_subclass_vars(self): |
| with self.assertRaises(TypeError): |
| class V(Unpack[TypeVarTuple('Ts')]): |
| pass |
| |
| def test_tuple(self): |
| Ts = TypeVarTuple('Ts') |
| Tuple[Unpack[Ts]] |
| |
| def test_union(self): |
| Xs = TypeVarTuple('Xs') |
| Ys = TypeVarTuple('Ys') |
| self.assertEqual( |
| Union[Unpack[Xs]], |
| Unpack[Xs] |
| ) |
| self.assertNotEqual( |
| Union[Unpack[Xs]], |
| Union[Unpack[Xs], Unpack[Ys]] |
| ) |
| self.assertEqual( |
| Union[Unpack[Xs], Unpack[Xs]], |
| Unpack[Xs] |
| ) |
| self.assertNotEqual( |
| Union[Unpack[Xs], int], |
| Union[Unpack[Xs]] |
| ) |
| self.assertNotEqual( |
| Union[Unpack[Xs], int], |
| Union[int] |
| ) |
| self.assertEqual( |
| Union[Unpack[Xs], int].__args__, |
| (Unpack[Xs], int) |
| ) |
| self.assertEqual( |
| Union[Unpack[Xs], int].__parameters__, |
| (Xs,) |
| ) |
| self.assertIs( |
| Union[Unpack[Xs], int].__origin__, |
| Union |
| ) |
| |
| def test_concatenation(self): |
| Xs = TypeVarTuple('Xs') |
| self.assertEqual(Tuple[int, Unpack[Xs]].__args__, (int, Unpack[Xs])) |
| self.assertEqual(Tuple[Unpack[Xs], int].__args__, (Unpack[Xs], int)) |
| self.assertEqual(Tuple[int, Unpack[Xs], str].__args__, |
| (int, Unpack[Xs], str)) |
| class C(Generic[Unpack[Xs]]): pass |
| self.assertEqual(C[int, Unpack[Xs]].__args__, (int, Unpack[Xs])) |
| self.assertEqual(C[Unpack[Xs], int].__args__, (Unpack[Xs], int)) |
| self.assertEqual(C[int, Unpack[Xs], str].__args__, |
| (int, Unpack[Xs], str)) |
| |
| def test_class(self): |
| Ts = TypeVarTuple('Ts') |
| |
| class C(Generic[Unpack[Ts]]): pass |
| self.assertEqual(C[int].__args__, (int,)) |
| self.assertEqual(C[int, str].__args__, (int, str)) |
| |
| with self.assertRaises(TypeError): |
| class C(Generic[Unpack[Ts], int]): pass |
| |
| T1 = TypeVar('T') |
| T2 = TypeVar('T') |
| class C(Generic[T1, T2, Unpack[Ts]]): pass |
| self.assertEqual(C[int, str].__args__, (int, str)) |
| self.assertEqual(C[int, str, float].__args__, (int, str, float)) |
| self.assertEqual(C[int, str, float, bool].__args__, (int, str, float, bool)) |
| with self.assertRaises(TypeError): |
| C[int] |
| |
| |
| class TypeVarTupleTests(BaseTestCase): |
| |
| def test_basic_plain(self): |
| Ts = TypeVarTuple('Ts') |
| self.assertEqual(Ts, Ts) |
| self.assertIsInstance(Ts, TypeVarTuple) |
| Xs = TypeVarTuple('Xs') |
| Ys = TypeVarTuple('Ys') |
| self.assertNotEqual(Xs, Ys) |
| |
| def test_repr(self): |
| Ts = TypeVarTuple('Ts') |
| self.assertEqual(repr(Ts), 'Ts') |
| |
| def test_no_redefinition(self): |
| self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts')) |
| |
| def test_cannot_subclass_vars(self): |
| with self.assertRaises(TypeError): |
| class V(TypeVarTuple('Ts')): |
| pass |
| |
| def test_cannot_subclass_var_itself(self): |
| with self.assertRaises(TypeError): |
| class V(TypeVarTuple): |
| pass |
| |
| def test_cannot_instantiate_vars(self): |
| Ts = TypeVarTuple('Ts') |
| with self.assertRaises(TypeError): |
| Ts() |
| |
| def test_tuple(self): |
| Ts = TypeVarTuple('Ts') |
| # Not legal at type checking time but we can't really check against it. |
| Tuple[Ts] |
| |
| def test_args_and_parameters(self): |
| Ts = TypeVarTuple('Ts') |
| |
| t = Tuple[tuple(Ts)] |
| self.assertEqual(t.__args__, (Ts.__unpacked__,)) |
| self.assertEqual(t.__parameters__, (Ts,)) |
| |
| |
| class FinalDecoratorTests(BaseTestCase): |
| def test_final_unmodified(self): |
| def func(x): ... |
| self.assertIs(func, final(func)) |
| |
| def test_dunder_final(self): |
| @final |
| def func(): ... |
| @final |
| class Cls: ... |
| self.assertIs(True, func.__final__) |
| self.assertIs(True, Cls.__final__) |
| |
| class Wrapper: |
| __slots__ = ("func",) |
| def __init__(self, func): |
| self.func = func |
| def __call__(self, *args, **kwargs): |
| return self.func(*args, **kwargs) |
| |
| # Check that no error is thrown if the attribute |
| # is not writable. |
| @final |
| @Wrapper |
| def wrapped(): ... |
| self.assertIsInstance(wrapped, Wrapper) |
| self.assertIs(False, hasattr(wrapped, "__final__")) |
| |
| class Meta(type): |
| @property |
| def __final__(self): return "can't set me" |
| @final |
| class WithMeta(metaclass=Meta): ... |
| self.assertEqual(WithMeta.__final__, "can't set me") |
| |
| # Builtin classes throw TypeError if you try to set an |
| # attribute. |
| final(int) |
| self.assertIs(False, hasattr(int, "__final__")) |
| |
| # Make sure it works with common builtin decorators |
| class Methods: |
| @final |
| @classmethod |
| def clsmethod(cls): ... |
| |
| @final |
| @staticmethod |
| def stmethod(): ... |
| |
| # The other order doesn't work because property objects |
| # don't allow attribute assignment. |
| @property |
| @final |
| def prop(self): ... |
| |
| @final |
| @lru_cache() # noqa: B019 |
| def cached(self): ... |
| |
| # Use getattr_static because the descriptor returns the |
| # underlying function, which doesn't have __final__. |
| self.assertIs( |
| True, |
| inspect.getattr_static(Methods, "clsmethod").__final__ |
| ) |
| self.assertIs( |
| True, |
| inspect.getattr_static(Methods, "stmethod").__final__ |
| ) |
| self.assertIs(True, Methods.prop.fget.__final__) |
| self.assertIs(True, Methods.cached.__final__) |
| |
| |
| class RevealTypeTests(BaseTestCase): |
| def test_reveal_type(self): |
| obj = object() |
| self.assertIs(obj, reveal_type(obj)) |
| |
| |
| class DataclassTransformTests(BaseTestCase): |
| def test_decorator(self): |
| def create_model(*, frozen: bool = False, kw_only: bool = True): |
| return lambda cls: cls |
| |
| decorated = dataclass_transform(kw_only_default=True, order_default=False)(create_model) |
| |
| class CustomerModel: |
| id: int |
| |
| self.assertIs(decorated, create_model) |
| self.assertEqual( |
| decorated.__dataclass_transform__, |
| { |
| "eq_default": True, |
| "order_default": False, |
| "kw_only_default": True, |
| "field_descriptors": (), |
| } |
| ) |
| self.assertIs( |
| decorated(frozen=True, kw_only=False)(CustomerModel), |
| CustomerModel |
| ) |
| |
| def test_base_class(self): |
| class ModelBase: |
| def __init_subclass__(cls, *, frozen: bool = False): ... |
| |
| Decorated = dataclass_transform(eq_default=True, order_default=True)(ModelBase) |
| |
| class CustomerModel(Decorated, frozen=True): |
| id: int |
| |
| self.assertIs(Decorated, ModelBase) |
| self.assertEqual( |
| Decorated.__dataclass_transform__, |
| { |
| "eq_default": True, |
| "order_default": True, |
| "kw_only_default": False, |
| "field_descriptors": (), |
| } |
| ) |
| self.assertIsSubclass(CustomerModel, Decorated) |
| |
| def test_metaclass(self): |
| class Field: ... |
| |
| class ModelMeta(type): |
| def __new__( |
| cls, name, bases, namespace, *, init: bool = True, |
| ): |
| return super().__new__(cls, name, bases, namespace) |
| |
| Decorated = dataclass_transform( |
| order_default=True, field_descriptors=(Field,) |
| )(ModelMeta) |
| |
| class ModelBase(metaclass=Decorated): ... |
| |
| class CustomerModel(ModelBase, init=False): |
| id: int |
| |
| self.assertIs(Decorated, ModelMeta) |
| self.assertEqual( |
| Decorated.__dataclass_transform__, |
| { |
| "eq_default": True, |
| "order_default": True, |
| "kw_only_default": False, |
| "field_descriptors": (Field,), |
| } |
| ) |
| self.assertIsInstance(CustomerModel, Decorated) |
| |
| |
| class AllTests(BaseTestCase): |
| |
| def test_typing_extensions_includes_standard(self): |
| a = typing_extensions.__all__ |
| self.assertIn('ClassVar', a) |
| self.assertIn('Type', a) |
| self.assertIn('ChainMap', a) |
| self.assertIn('ContextManager', a) |
| self.assertIn('Counter', a) |
| self.assertIn('DefaultDict', a) |
| self.assertIn('Deque', a) |
| self.assertIn('NewType', a) |
| self.assertIn('overload', a) |
| self.assertIn('Text', a) |
| self.assertIn('TYPE_CHECKING', a) |
| self.assertIn('TypeAlias', a) |
| self.assertIn('ParamSpec', a) |
| self.assertIn("Concatenate", a) |
| |
| self.assertIn('Annotated', a) |
| self.assertIn('get_type_hints', a) |
| |
| self.assertIn('Awaitable', a) |
| self.assertIn('AsyncIterator', a) |
| self.assertIn('AsyncIterable', a) |
| self.assertIn('Coroutine', a) |
| self.assertIn('AsyncContextManager', a) |
| |
| self.assertIn('AsyncGenerator', a) |
| |
| self.assertIn('Protocol', a) |
| self.assertIn('runtime', a) |
| |
| # Check that all objects in `__all__` are present in the module |
| for name in a: |
| self.assertTrue(hasattr(typing_extensions, name)) |
| |
| def test_typing_extensions_defers_when_possible(self): |
| exclude = { |
| 'overload', |
| 'Text', |
| 'TypedDict', |
| 'TYPE_CHECKING', |
| 'Final', |
| 'get_type_hints', |
| 'is_typeddict', |
| } |
| if sys.version_info < (3, 10): |
| exclude |= {'get_args', 'get_origin'} |
| if sys.version_info < (3, 11): |
| exclude.add('final') |
| for item in typing_extensions.__all__: |
| if item not in exclude and hasattr(typing, item): |
| self.assertIs( |
| getattr(typing_extensions, item), |
| getattr(typing, item)) |
| |
| def test_typing_extensions_compiles_with_opt(self): |
| file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), |
| 'typing_extensions.py') |
| try: |
| subprocess.check_output(f'{sys.executable} -OO {file_path}', |
| stderr=subprocess.STDOUT, |
| shell=True) |
| except subprocess.CalledProcessError: |
| self.fail('Module does not compile with optimize=2 (-OO flag).') |
| |
| |
| |
| if __name__ == '__main__': |
| main() |