diff --git a/LICENSE b/LICENSE index 208ecb46a4fa..74302470069d 100644 --- a/LICENSE +++ b/LICENSE @@ -4,7 +4,7 @@ Mypy is licensed under the terms of the MIT license, reproduced below. The MIT License -Copyright (c) 2012 Jukka Lehtosalo and contributors +Copyright (c) 2015 Jukka Lehtosalo and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,8 +26,9 @@ DEALINGS IN THE SOFTWARE. = = = = = -Portions of mypy are licensed under different licenses. The files under -python-lib are licensed under the PSF 2 License, reproduced below. +Portions of mypy are licensed under different licenses. The files +under lib-python and lib-typing/3.2 are licensed under the PSF 2 +License, reproduced below. = = = = = diff --git a/lib-python/3.2/pprint.py b/lib-python/3.2/pprint.py index b68339f49575..650c1a3b5afe 100644 --- a/lib-python/3.2/pprint.py +++ b/lib-python/3.2/pprint.py @@ -203,7 +203,7 @@ def _format(self, object: object, stream: TextIO, indent: int, (issubclass(typ, set) and r is set.__repr__) or (issubclass(typ, frozenset) and r is frozenset.__repr__) ): - anyobj = Any(object) # TODO Collection? + anyobj = cast(Any, object) # TODO Collection? length = _len(anyobj) if issubclass(typ, list): write('[') @@ -322,7 +322,7 @@ def _safe_repr(object: object, context: Dict[int, int], if (issubclass(typ, list) and r is list.__repr__) or \ (issubclass(typ, tuple) and r is tuple.__repr__): - anyobj = Any(object) # TODO Sequence? + anyobj = cast(Any, object) # TODO Sequence? if issubclass(typ, list): if not object: return "[]", True, False diff --git a/lib-python/3.2/subprocess.py b/lib-python/3.2/subprocess.py index edbf22b60cd0..05218668d235 100644 --- a/lib-python/3.2/subprocess.py +++ b/lib-python/3.2/subprocess.py @@ -373,9 +373,9 @@ def __str__(self) -> str: import _subprocess class STARTUPINFO: dwFlags = 0 - hStdInput = Any(None) - hStdOutput = Any(None) - hStdError = Any(None) + hStdInput = cast(Any, None) + hStdOutput = cast(Any, None) + hStdError = cast(Any, None) wShowWindow = 0 class pywintypes: error = IOError @@ -1048,8 +1048,8 @@ def _readerthread(self, fh: IO[AnyStr], buffer: List[AnyStr]) -> None: def _communicate(self, input: Any) -> Tuple[Any, Any]: - stdout = Any(None) # Return - stderr = Any(None) # Return + stdout = cast(Any, None) # Return + stderr = cast(Any, None) # Return if self.stdout: stdout = [] @@ -1483,8 +1483,8 @@ def _communicate(self, input: Any) -> Tuple[Any, Any]: # Translate newlines, if requested. # This also turns bytes into strings. - stdout3 = Any(stdout2) - stderr3 = Any(stderr2) + stdout3 = cast(Any, stdout2) + stderr3 = cast(Any, stderr2) if self.universal_newlines: if stdout is not None: stdout3 = self._translate_newlines( diff --git a/lib-python/3.2/tempfile.py b/lib-python/3.2/tempfile.py index 2078a43b05c5..dcc6a8f1cf93 100644 --- a/lib-python/3.2/tempfile.py +++ b/lib-python/3.2/tempfile.py @@ -379,7 +379,7 @@ def __getattr__(self, name: str) -> _Any: # Attribute lookups are delegated to the underlying file # and cached for non-numeric results # (i.e. methods are cached, closed and friends are not) - file = _Any(self).__dict__['file'] # type: _IO[_Any] + file = _cast(_Any, self).__dict__['file'] # type: _IO[_Any] a = getattr(file, name) if not isinstance(a, int): setattr(self, name, a) diff --git a/lib-python/3.2/test/support.py b/lib-python/3.2/test/support.py index b7af14aeeb2d..a36ba28f2341 100644 --- a/lib-python/3.2/test/support.py +++ b/lib-python/3.2/test/support.py @@ -25,7 +25,7 @@ import logging.handlers import _thread, threading -from typing import Any, Dict +from typing import Any, Dict, cast #try: # import multiprocessing.process #except ImportError: @@ -441,7 +441,7 @@ def fcmp(x, y): # fuzzy comparison function # decorator for skipping tests on non-IEEE 754 platforms requires_IEEE_754 = unittest.skipUnless( - (Any(float)).__getformat__("double").startswith("IEEE"), + cast(Any, float).__getformat__("double").startswith("IEEE"), "test requires IEEE 754 doubles") is_jython = sys.platform.startswith('java') diff --git a/lib-python/3.2/test/test_getopt.py b/lib-python/3.2/test/test_getopt.py index 3a92aa82cac6..33205521ebd2 100644 --- a/lib-python/3.2/test/test_getopt.py +++ b/lib-python/3.2/test/test_getopt.py @@ -6,7 +6,7 @@ import getopt -from typing import Any +from typing import cast, Any sentinel = object() @@ -22,7 +22,7 @@ def tearDown(self) -> None: def assertError(self, *args: Any, **kwargs: Any) -> None: # JLe: work around mypy bug #229 - Any(self.assertRaises)(getopt.GetoptError, *args, **kwargs) + cast(Any, self.assertRaises)(getopt.GetoptError, *args, **kwargs) def test_short_has_arg(self) -> None: self.assertTrue(getopt.short_has_arg('a', 'a:')) diff --git a/lib-python/3.2/test/test_posixpath.py b/lib-python/3.2/test/test_posixpath.py index 76d7e16c8b06..de98975ad92e 100644 --- a/lib-python/3.2/test/test_posixpath.py +++ b/lib-python/3.2/test/test_posixpath.py @@ -13,7 +13,7 @@ from posixpath import realpath, abspath, dirname, basename import posix -from typing import Any, TypeVar, Callable +from typing import cast, Any, TypeVar, Callable T = TypeVar('T') @@ -225,7 +225,7 @@ def test_samestat_on_links(self) -> None: test_fn2 = support.TESTFN + "2" self._create_file(test_fn1) test_fns = [test_fn1, test_fn2] - Any(os.symlink)(*test_fns) + cast(Any, os.symlink)(*test_fns) stats = map(os.stat, test_fns) self.assertTrue(posixpath.samestat(*stats)) os.remove(test_fn2) diff --git a/lib-python/3.2/test/test_pprint.py b/lib-python/3.2/test/test_pprint.py index 23a1ed756170..9dce3c56a5ee 100644 --- a/lib-python/3.2/test/test_pprint.py +++ b/lib-python/3.2/test/test_pprint.py @@ -122,8 +122,9 @@ def test_same_as_repr(self) -> None: -6, -6, complex(-6.,-6.), -1.5, "x", b"x", (3,), [3], {3: 6}, (1,2), [3,4], {5: 6}, tuple2((1,2)), tuple3((1,2)), tuple3(range(100)), - [3,4], list2(Any([3,4])), list3(Any([3,4])), list3(Any(range(100))), - dict2(Any({5: 6})), dict3(Any({5: 6})), # JLe: work around mypy issue #233 + [3,4], list2(cast(Any, [3,4])), list3(cast(Any, [3,4])), + list3(cast(Any, range(100))), dict2(cast(Any, {5: 6})), + dict3(cast(Any, {5: 6})), # JLe: work around mypy issue #233 range(10, -11, -1) ): native = repr(simple) diff --git a/lib-python/3.2/test/test_random.py b/lib-python/3.2/test/test_random.py index 6200eb6012e7..61dd489ba4fd 100644 --- a/lib-python/3.2/test/test_random.py +++ b/lib-python/3.2/test/test_random.py @@ -8,7 +8,7 @@ from math import log, exp, pi, fsum, sin from test import support -from typing import Undefined, Any, Dict, List, Callable, Generic, TypeVar +from typing import Undefined, Any, Dict, List, Callable, Generic, TypeVar, cast RT = TypeVar('RT', random.Random, random.SystemRandom) @@ -267,7 +267,7 @@ def test_setstate_middle_arg(self) -> None: # Wrong type, s/b tuple of 625 ints self.assertRaises(TypeError, self.gen.setstate, (2, tuple(['a',]*625), None)) # Last element s/b an int also - self.assertRaises(TypeError, self.gen.setstate, (2, Any((0,))*624+('a',), None)) + self.assertRaises(TypeError, self.gen.setstate, (2, cast(Any, (0,))*624+('a',), None)) def test_referenceImplementation(self) -> None: # Compare the python implementation with results from the original diff --git a/lib-python/3.2/test/test_shutil.py b/lib-python/3.2/test/test_shutil.py index 01b81aa68f3f..015783bfa548 100644 --- a/lib-python/3.2/test/test_shutil.py +++ b/lib-python/3.2/test/test_shutil.py @@ -50,10 +50,10 @@ def mock_rename(func: Any) -> Any: def wrap(*args: Any, **kwargs: Any) -> Any: try: builtin_rename = shutil.rename - shutil.rename = Any(_fake_rename) + shutil.rename = cast(Any, _fake_rename) return func(*args, **kwargs) finally: - shutil.rename = Any(builtin_rename) + shutil.rename = cast(Any, builtin_rename) return wrap class TestShutil(unittest.TestCase): diff --git a/lib-python/3.2/test/test_subprocess.py b/lib-python/3.2/test/test_subprocess.py index 2f752df9abc9..772d8cc416e4 100644 --- a/lib-python/3.2/test/test_subprocess.py +++ b/lib-python/3.2/test/test_subprocess.py @@ -17,7 +17,7 @@ import resource -from typing import Any, Dict, Callable, Iterable, List, Set, Tuple +from typing import Any, Dict, Callable, Iterable, List, Set, Tuple, cast mswindows = (sys.platform == "win32") @@ -40,7 +40,7 @@ def _mkstemp() -> Tuple[int, str]: """Replacement for mkstemp, calling mktemp.""" fname = tempfile.mktemp() return os.open(fname, os.O_RDWR|os.O_CREAT), fname - mkstemp = Any(_mkstemp) + mkstemp = cast(Any, _mkstemp) class BaseTestCase(unittest.TestCase): @@ -89,7 +89,7 @@ def test_check_output(self) -> None: # check_output() function with zero return code output = subprocess.check_output( [sys.executable, "-c", "print('BDFL')"]) - self.assertIn(b'BDFL', Any(output)) # see #39 + self.assertIn(b'BDFL', cast(Any, output)) # see #39 def test_check_output_nonzero(self) -> None: # check_call() function with non-zero return code @@ -103,7 +103,7 @@ def test_check_output_stderr(self) -> None: output = subprocess.check_output( [sys.executable, "-c", "import sys; sys.stderr.write('BDFL')"], stderr=subprocess.STDOUT) - self.assertIn(b'BDFL', Any(output)) # see #39 + self.assertIn(b'BDFL', cast(Any, output)) # see #39 def test_check_output_stdout_arg(self) -> None: # check_output() function stderr redirected to stdout @@ -633,7 +633,7 @@ def test_invalid_bufsize(self) -> None: # an invalid type of the bufsize argument should raise # TypeError. with self.assertRaises(TypeError): - subprocess.Popen([sys.executable, "-c", "pass"], Any("orange")) + subprocess.Popen([sys.executable, "-c", "pass"], cast(Any, "orange")) def test_bufsize_is_none(self) -> None: # bufsize=None should be the same as bufsize=0. @@ -897,7 +897,7 @@ def test_args_string(self) -> None: # args is a string fd, fname = mkstemp() # reopen in text mode - with open(fd, "w", errors=Any("surrogateescape")) as fobj: # see #260 + with open(fd, "w", errors=cast(Any, "surrogateescape")) as fobj: # see #260 fobj.write("#!/bin/sh\n") fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) @@ -942,7 +942,7 @@ def test_call_string(self) -> None: # call() function with string argument on UNIX fd, fname = mkstemp() # reopen in text mode - with open(fd, "w", errors=Any("surrogateescape")) as fobj: # see #260 + with open(fd, "w", errors=cast(Any, "surrogateescape")) as fobj: # see #260 fobj.write("#!/bin/sh\n") fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) diff --git a/lib-typing/3.2/test_typing.py b/lib-typing/3.2/test_typing.py index 8240f4bd0d29..35630102d29c 100644 --- a/lib-typing/3.2/test_typing.py +++ b/lib-typing/3.2/test_typing.py @@ -1,453 +1,1151 @@ -from abc import abstractmethod, ABCMeta -import unittest - -from typing import ( - List, Dict, Set, Tuple, Pattern, Match, Any, Callable, Generic, - _Protocol, Sized, Iterable, Iterator, Sequence, Union, Optional, - AbstractSet, Mapping, BinaryIO, TextIO, SupportsInt, SupportsFloat, - SupportsAbs, SupportsRound, Reversible, Undefined, AnyStr, - cast, overload, TypeVar -) - - -class TestTyping(unittest.TestCase): - def test_List(self): - self.assertIs(List[int], list) - self.assertIs(List[str], list) - - def test_Dict(self): - self.assertIs(Dict[int, str], dict) - self.assertIs(Dict[str, int], dict) - - def test_Set(self): - self.assertIs(Set[int], set) - self.assertIs(Set[str], set) - - def test_Tuple(self): - self.assertIs(Tuple[int], tuple) - self.assertIs(Tuple[str, int], tuple) - self.assertIs(Tuple[str, int, bool], tuple) - - def test_Pattern(self): - import re - self.assertIs(type(re.compile('')), Pattern.target_type) - self.assertIs(type(re.compile(b'')), Pattern.target_type) - - def test_Match(self): - import re - self.assertIs(type(re.match('', '')), Match.target_type) - self.assertIs(type(re.match(b'', b'')), Match.target_type) - - def test_Any(self): - o = object() - self.assertIs(Any(o), o) - s = 'x' - self.assertIs(Any(s), s) - - def test_Callable(self): - # Just check that we can call Callable. Don't care about return value. - Callable[[], int] - Callable[[int], None] - Callable[[int, str], bool] - - def test_cast(self): - o = object() - # cast performs no runtime checks! - self.assertIs(cast(int, o), o) - s = 'x' - self.assertIs(cast(str, s), s) - self.assertIs(cast('xyz', s), s) - # cast does not check type validity; anything goes. - self.assertIs(cast(o, s), s) - - def test_annotations(self): - def f(x: int) -> str: return 'string' - - self.assertEqual(f.__annotations__, {'x': int, 'return': str}) - - def test_typevar(self): - t = TypeVar('t') - self.assertEqual(t.name, 't') - self.assertIsNone(t.values) - - def test_typevar_values(self): - t = TypeVar('t', int, str) - self.assertEqual(t.name, 't') - self.assertEqual(t.values, (int, str)) - - def test_predefined_typevars(self): - self.assertEqual(AnyStr.name, 'AnyStr') - self.assertEqual(AnyStr.values, (str, bytes)) - - def test_simple_generic_class(self): - t = TypeVar('t') - - class C(Generic[t]): - pass +from collections import namedtuple +import re +import sys +from unittest import TestCase, main +try: + from unittest import mock +except ImportError: + import mock # 3rd party install, for PY3.2. + +from typing import Any +from typing import TypeVar, T, KT, VT, AnyStr +from typing import Union, Optional +from typing import Tuple +from typing import Callable +from typing import Generic +from typing import Undefined +from typing import cast +from typing import get_type_hints +from typing import no_type_check, no_type_check_decorator +from typing import NamedTuple +from typing import IO, TextIO, BinaryIO +from typing import Pattern, Match +import typing + + +class ConstantsTests(TestCase): + + def test_py23(self): + assert isinstance(typing.PY2, bool) + assert isinstance(typing.PY3, bool) + assert typing.PY3 == (not typing.PY2) + + def test_poswin(self): + assert isinstance(typing.POSIX, bool) + assert isinstance(typing.WINDOWS, bool) + assert typing.POSIX == (not typing.WINDOWS) + + +class Employee: + pass + + +class Manager(Employee): + pass + + +class Founder(Employee): + pass + + +class ManagingFounder(Manager, Founder): + pass + + +class AnyTests(TestCase): + + def test_any_instance(self): + self.assertIsInstance(Employee(), Any) + self.assertIsInstance(42, Any) + self.assertIsInstance(None, Any) + self.assertIsInstance(object(), Any) + + def test_any_subclass(self): + self.assertTrue(issubclass(Employee, Any)) + self.assertTrue(issubclass(int, Any)) + self.assertTrue(issubclass(type(None), Any)) + self.assertTrue(issubclass(object, Any)) + + def test_others_any(self): + self.assertFalse(issubclass(Any, Employee)) + self.assertFalse(issubclass(Any, int)) + self.assertFalse(issubclass(Any, type(None))) + # However, Any is a subclass of object (this can't be helped). + self.assertTrue(issubclass(Any, object)) + + 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 + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + Any() + + def test_cannot_subscript(self): + with self.assertRaises(TypeError): + Any[int] + + def test_any_is_subclass(self): + # Any should be considered a subclass of everything. + assert issubclass(Any, Any) + assert issubclass(Any, typing.List) + assert issubclass(Any, typing.List[int]) + assert issubclass(Any, typing.List[T]) + assert issubclass(Any, typing.Mapping) + assert issubclass(Any, typing.Mapping[str, int]) + assert issubclass(Any, typing.Mapping[KT, VT]) + assert issubclass(Any, Generic) + assert issubclass(Any, Generic[T]) + assert issubclass(Any, Generic[KT, VT]) + assert issubclass(Any, AnyStr) + assert issubclass(Any, Union) + assert issubclass(Any, Union[int, str]) + assert issubclass(Any, typing.Match) + assert issubclass(Any, typing.Match[str]) + # These expressions must simply not fail. + typing.Match[Any] + typing.Pattern[Any] + typing.IO[Any] + + +class TypeVarTests(TestCase): + + def test_isinstance(self): + self.assertNotIsInstance(42, T) + self.assertIsInstance(b'b', AnyStr) + self.assertIsInstance('s', AnyStr) + self.assertNotIsInstance(42, AnyStr) + + def test_issubclass(self): + self.assertTrue(issubclass(T, Any)) + self.assertFalse(issubclass(int, T)) + self.assertTrue(issubclass(bytes, AnyStr)) + self.assertTrue(issubclass(str, AnyStr)) + self.assertTrue(issubclass(T, T)) + self.assertTrue(issubclass(AnyStr, AnyStr)) + + def test_repr(self): + self.assertEqual(repr(T), '~T') + self.assertEqual(repr(KT), '~KT') + self.assertEqual(repr(VT), '~VT') + self.assertEqual(repr(AnyStr), '~AnyStr') + + def test_no_redefinition(self): + self.assertNotEqual(TypeVar('T'), TypeVar('T')) + self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str)) + + def test_subclass_as_unions(self): + self.assertTrue(issubclass(TypeVar('T', int, str), + TypeVar('T', int, str))) + self.assertTrue(issubclass(TypeVar('T', int), TypeVar('T', int, str))) + self.assertTrue(issubclass(TypeVar('T', int, str), + TypeVar('T', str, int))) + A = TypeVar('A', int, str) + B = TypeVar('B', int, str, float) + self.assertTrue(issubclass(A, B)) + self.assertFalse(issubclass(B, A)) + + 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 - self.assertIs(C[int], C) - self.assertIsInstance(C(), C) - self.assertIsInstance(C[int](), C) + def test_cannot_instantiate_vars(self): + with self.assertRaises(TypeError): + TypeVar('A')() + + def test_bind(self): + self.assertNotIsInstance(42, T) # Baseline. + with T.bind(int): + self.assertIsInstance(42, T) + self.assertNotIsInstance(3.14, T) + self.assertTrue(issubclass(int, T)) + self.assertFalse(issubclass(T, int)) + self.assertFalse(issubclass(float, T)) + self.assertNotIsInstance(42, T) # Baseline restored. + + def test_bind_reuse(self): + self.assertNotIsInstance(42, T) # Baseline. + bv = T.bind(int) + with bv: + self.assertIsInstance(42, T) # Bound. + self.assertNotIsInstance(3.14, T) + self.assertNotIsInstance(42, T) # Baseline restored. + # Reusing bv will work. + with bv: + self.assertIsInstance(42, T) # Bound. + self.assertNotIsInstance(3.14, T) + # Reusing bv recursively won't work. + with self.assertRaises(TypeError): + with bv: + self.assertFalse("Should not get here") + # Rebinding T explicitly will work. + with T.bind(float): + self.assertIsInstance(3.14, T) + self.assertNotIsInstance(42, T) + # Now the previous binding should be restored. + self.assertIsInstance(42, T) + self.assertNotIsInstance(3.14, T) + self.assertNotIsInstance(42, T) # Baseline restored. + + def test_bind_fail(self): + # This essentially tests what happens when __enter__() raises + # an exception. __exit__() won't be called, but the + # VarBinding and the TypeVar are still in consistent states. + bv = T.bind(int) + with mock.patch('typing.TypeVar._bind', side_effect=RuntimeError): + with self.assertRaises(RuntimeError): + with bv: + self.assertFalse("Should not get here") + self.assertNotIsInstance(42, T) + with bv: + self.assertIsInstance(42, T) + self.assertNotIsInstance(42, T) + + +class UnionTests(TestCase): + + def test_basics(self): + u = Union[int, float] + self.assertNotEqual(u, Union) + self.assertIsInstance(42, u) + self.assertIsInstance(3.14, u) + self.assertTrue(issubclass(int, u)) + self.assertTrue(issubclass(float, u)) + + def test_union_any(self): + u = Union[Any] + self.assertEqual(u, Any) + u = Union[int, Any] + self.assertEqual(u, Any) + u = Union[Any, int] + self.assertEqual(u, Any) + + def test_union_object(self): + u = Union[object] + self.assertEqual(u, object) + u = Union[int, object] + self.assertEqual(u, object) + u = Union[object, int] + self.assertEqual(u, object) + + def test_union_any_object(self): + u = Union[object, Any] + self.assertEqual(u, Any) + u = Union[Any, object] + self.assertEqual(u, Any) + + def test_unordered(self): + u1 = Union[int, float] + u2 = Union[float, int] + self.assertEqual(u1, u2) + + def test_subclass(self): + u = Union[int, Employee] + self.assertIsInstance(Manager(), u) + self.assertTrue(issubclass(Manager, u)) + + def test_self_subclass(self): + self.assertTrue(issubclass(Union[KT, VT], Union)) + self.assertFalse(issubclass(Union, Union[KT, VT])) + + def test_multiple_inheritance(self): + u = Union[int, Employee] + self.assertIsInstance(ManagingFounder(), u) + self.assertTrue(issubclass(ManagingFounder, u)) + + def test_single_class_disappears(self): + t = Union[Employee] + self.assertIs(t, Employee) + + def test_base_class_disappears(self): + u = Union[Employee, Manager, int] + self.assertEqual(u, Union[int, Employee]) + u = Union[Manager, int, Employee] + self.assertEqual(u, Union[int, Employee]) + u = Union[Employee, Manager] + self.assertIs(u, Employee) + + def test_weird_subclasses(self): + u = Union[Employee, int, float] + v = Union[int, float] + self.assertTrue(issubclass(v, u)) + w = Union[int, Manager] + self.assertTrue(issubclass(w, u)) + + def test_union_union(self): + u = Union[int, float] + v = Union[u, Employee] + self.assertEqual(v, Union[int, float, Employee]) + + def test_repr(self): + self.assertEqual(repr(Union), 'typing.Union') + u = Union[Employee, int] + self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__) + u = Union[int, Employee] + self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(Union): + pass + with self.assertRaises(TypeError): + class C(Union[int, str]): + pass - def test_generic_class_with_two_typeargs(self): - t = TypeVar('t') - u = TypeVar('u') + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + Union() + u = Union[int, float] + with self.assertRaises(TypeError): + u() - class C(Generic[t, u]): + def test_optional(self): + o = Optional[int] + u = Union[int, None] + self.assertEqual(o, u) + self.assertIsInstance(42, o) + self.assertIsInstance(None, o) + self.assertNotIsInstance(3.14, o) + + def test_empty(self): + with self.assertRaises(TypeError): + Union[()] + + +class TypeVarUnionTests(TestCase): + + def test_simpler(self): + A = TypeVar('A', int, str, float) + B = TypeVar('B', int, str) + assert issubclass(A, A) + assert issubclass(B, B) + assert issubclass(B, A) + assert issubclass(A, Union[int, str, float]) + assert issubclass(Union[int, str, float], A) + assert issubclass(Union[int, str], B) + assert issubclass(B, Union[int, str]) + assert not issubclass(A, B) + assert not issubclass(Union[int, str, float], B) + assert not issubclass(A, Union[int, str]) + + def test_var_union_subclass(self): + self.assertTrue(issubclass(T, Union[int, T])) + self.assertTrue(issubclass(KT, Union[KT, VT])) + + def test_var_union(self): + TU = TypeVar('TU', Union[int, float]) + self.assertIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + with TU.bind(int): + # The effective binding is the union. + self.assertIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + with self.assertRaises(TypeError): + with TU.bind(str): + self.assertFalse("Should not get here") + + def test_var_union_and_more_precise(self): + TU = TypeVar('TU', Union[int, float], int) + with TU.bind(int): + # The binding is ambiguous, but the second alternative + # is strictly more precise. Choose the more precise match. + # The effective binding is int. + self.assertIsInstance(42, TU) + self.assertNotIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + with TU.bind(float): + # The effective binding is the union. + self.assertIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + + def test_var_union_overlapping(self): + TU = TypeVar('TU', Union[int, float], Union[float, str]) + with TU.bind(int): + # The effective binding is the first union. + self.assertIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + with TU.bind(float): + # The binding is ambiguous, but neither constraint is a + # subclass of the other. Choose the first match. + # The effective binding is the first union. + self.assertIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertNotIsInstance('', TU) + with TU.bind(str): + # The effective binding is the second union. + self.assertNotIsInstance(42, TU) + self.assertIsInstance(3.14, TU) + self.assertIsInstance('', TU) + + +class TupleTests(TestCase): + + def test_basics(self): + self.assertIsInstance((42, 3.14, ''), Tuple) + self.assertIsInstance((42, 3.14, ''), Tuple[int, float, str]) + self.assertIsInstance((42,), Tuple[int]) + self.assertNotIsInstance((3.14,), Tuple[int]) + self.assertNotIsInstance((42, 3.14), Tuple[int, float, str]) + self.assertNotIsInstance((42, 3.14, 100), Tuple[int, float, str]) + self.assertNotIsInstance((42, 3.14, 100), Tuple[int, float]) + self.assertTrue(issubclass(Tuple[int, str], Tuple)) + self.assertTrue(issubclass(Tuple[int, str], Tuple[int, str])) + self.assertFalse(issubclass(int, Tuple)) + self.assertFalse(issubclass(Tuple[float, str], Tuple[int, str])) + self.assertFalse(issubclass(Tuple[int, str, int], Tuple[int, str])) + self.assertFalse(issubclass(Tuple[int, str], Tuple[int, str, int])) + self.assertTrue(issubclass(tuple, Tuple)) + self.assertFalse(issubclass(Tuple, tuple)) # Can't have it both ways. + + def test_tuple_subclass(self): + class MyTuple(tuple): pass + self.assertTrue(issubclass(MyTuple, 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]') + + def test_errors(self): + with self.assertRaises(TypeError): + issubclass(42, Tuple) + with self.assertRaises(TypeError): + issubclass(42, Tuple[int]) + + +class CallableTests(TestCase): + + def test_basics(self): + c = Callable[[int, float], str] + + def flub(a: int, b: float) -> str: + return str(a * b) + + def flob(a: int, b: int) -> str: + return str(a * b) - self.assertIs(C[int, str], C) - self.assertIsInstance(C(), C) - self.assertIsInstance(C[int, str](), C) + self.assertIsInstance(flub, c) + self.assertNotIsInstance(flob, c) - def test_abstract_generic_class(self): - t = TypeVar('t') - class C(Generic[t]): + def test_self_subclass(self): + self.assertTrue(issubclass(Callable[[int], int], Callable)) + self.assertFalse(issubclass(Callable, Callable[[int], int])) + self.assertTrue(issubclass(Callable[[int], int], Callable[[int], int])) + self.assertFalse(issubclass(Callable[[Employee], int], + Callable[[Manager], int])) + self.assertFalse(issubclass(Callable[[Manager], int], + Callable[[Employee], int])) + self.assertFalse(issubclass(Callable[[int], Employee], + Callable[[int], Manager])) + self.assertFalse(issubclass(Callable[[int], Manager], + Callable[[int], Employee])) + + 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_with_none(self): + c = Callable[[None], None] + + def flub(self: None) -> None: pass - class D: + + def flab(self: Any) -> None: pass - self.assertNotIsInstance(D(), C) - C.register(D) - self.assertIsInstance(D(), C) - def test_sequence(self): - self.assertIs(Sequence[int], Sequence) + def flob(self: None) -> Any: + pass - self.assertIsInstance([], Sequence) - self.assertIsInstance((), Sequence) - self.assertIsInstance('', Sequence) - self.assertIsInstance(b'', Sequence) - self.assertIsInstance(range(5), Sequence) - self.assertNotIsInstance({}, Sequence) + self.assertIsInstance(flub, c) + self.assertIsInstance(flab, c) + self.assertNotIsInstance(flob, c) # Test contravariance. - def test_abstract_set(self): - self.assertIs(AbstractSet[int], AbstractSet) + def test_with_subclasses(self): + c = Callable[[Employee, Manager], Employee] - self.assertIsInstance(set(), AbstractSet) - self.assertIsInstance(frozenset(), AbstractSet) - self.assertIsInstance({}.keys(), AbstractSet) - self.assertIsInstance({}.items(), AbstractSet) - # This is consistent with collections.Set. - self.assertNotIsInstance({}.values(), AbstractSet) + def flub(a: Employee, b: Employee) -> Manager: + return Manager() - def test_mapping(self): - self.assertIs(Mapping[int, str], Mapping) - self.assertIsInstance({}, Mapping) + def flob(a: Manager, b: Manager) -> Employee: + return Employee() - def test_io_types(self): - self.assertIsInstance(BinaryIO, type) - self.assertIsInstance(TextIO, type) + self.assertIsInstance(flub, c) + self.assertNotIsInstance(flob, c) + + def test_with_default_args(self): + c = Callable[[int], int] + + def flub(a: int, b: float = 3.14) -> int: + return a + + def flab(a: int, *, b: float = 3.14) -> int: + return a + + def flob(a: int = 42) -> int: + return a + + self.assertIsInstance(flub, c) + self.assertIsInstance(flab, c) + self.assertIsInstance(flob, c) + + def test_with_varargs(self): + c = Callable[[int], int] + + def flub(*args) -> int: + return 42 + + def flab(*args: int) -> int: + return 42 + + def flob(*args: float) -> int: + return 42 + + self.assertIsInstance(flub, c) + self.assertIsInstance(flab, c) + self.assertNotIsInstance(flob, c) + + def test_with_method(self): + + class C: + + def imethod(self, arg: int) -> int: + self.last_arg = arg + return arg + 1 + + @classmethod + def cmethod(cls, arg: int) -> int: + cls.last_cls_arg = arg + return arg + 1 + + @staticmethod + def smethod(arg: int) -> int: + return arg + 1 + + ct = Callable[[int], int] + self.assertIsInstance(C().imethod, ct) + self.assertIsInstance(C().cmethod, ct) + self.assertIsInstance(C.cmethod, ct) + self.assertIsInstance(C().smethod, ct) + self.assertIsInstance(C.smethod, ct) + self.assertIsInstance(C.imethod, Callable[[Any, int], int]) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + + class C(Callable): + pass + + with self.assertRaises(TypeError): + + class C(Callable[[int], int]): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + Callable() + c = Callable[[int], str] + with self.assertRaises(TypeError): + c() + + def test_callable_varargs(self): + ct = Callable[..., int] + + def foo(a, b) -> int: + return 42 + + def bar(a=42) -> int: + return a + + def baz(*, x, y, z) -> int: + return 100 + + self.assertIsInstance(foo, ct) + self.assertIsInstance(bar, ct) + self.assertIsInstance(baz, ct) + + +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): + + 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 ProtocolTests(TestCase): def test_supports_int(self): - self.assertIsInstance(1, SupportsInt) - self.assertIsInstance(1.1, SupportsInt) - self.assertNotIsInstance('', SupportsInt) - self.assertNotIsInstance(b'', SupportsInt) - self.assertNotIsInstance((), SupportsInt) + assert issubclass(int, typing.SupportsInt) + assert not issubclass(str, typing.SupportsInt) def test_supports_float(self): - self.assertIsInstance(1.1, SupportsFloat) - self.assertIsInstance(1, SupportsFloat) - self.assertNotIsInstance('', SupportsFloat) - self.assertNotIsInstance(b'', SupportsFloat) - self.assertNotIsInstance((), SupportsFloat) + assert issubclass(float, typing.SupportsFloat) + assert not issubclass(str, typing.SupportsFloat) def test_supports_abs(self): - self.assertIsInstance(1.1, SupportsAbs) - self.assertIsInstance(1, SupportsAbs) - self.assertNotIsInstance('', SupportsAbs) - self.assertNotIsInstance((), SupportsAbs) + assert issubclass(float, typing.SupportsAbs) + assert issubclass(int, typing.SupportsAbs) + assert not issubclass(str, typing.SupportsAbs) def test_supports_round(self): - self.assertIsInstance(1.1, SupportsRound) - self.assertIsInstance(1, SupportsRound) - self.assertNotIsInstance('', SupportsRound) - self.assertNotIsInstance((), SupportsRound) + assert issubclass(float, typing.SupportsRound) + assert issubclass(int, typing.SupportsRound) + assert not issubclass(str, typing.SupportsRound) def test_reversible(self): - self.assertIsInstance([], Reversible) - self.assertIsInstance(range(1), Reversible) - self.assertNotIsInstance((), Reversible) - self.assertNotIsInstance('', Reversible) - - def test_simple_protocol(self): - class P(_Protocol): - def f(self): pass - - class A: - # Conforms to P - def f(self): pass - def g(self): pass - - class B: - # Does not conform to P - def g(self): pass - - self.assertIsInstance(A(), P) - self.assertNotIsInstance(B(), P) - - self.assertTrue(issubclass(A, P)) - self.assertFalse(issubclass(B, P)) - self.assertTrue(issubclass(P, _Protocol)) - self.assertTrue(issubclass(_Protocol, _Protocol)) - self.assertTrue(issubclass(A, _Protocol)) - - def test_issubclass_of_protocol(self): - class A: pass - self.assertTrue(issubclass(A, _Protocol)) - - def test_protocol_with_two_attrs(self): - class P(_Protocol): - def __int__(self): pass - x = 0 - - class A: - # Conforms to P; attribute values don't need to be similar - __int__ = 0 - def x(self): pass - def f(self): pass # Extra method - - class B: - # Does not conform to P - __int__ = 0 - class C: - # Does not conform to P - x = 0 - - self.assertIsInstance(A(), P) - self.assertNotIsInstance(B(), P) - self.assertNotIsInstance(C(), P) - - def test_protocol_inheritance(self): - class P(_Protocol): - def f(self): pass - class PP(P, _Protocol): - def g(self): pass - - class A: - # Conforms to P but not PP - def f(self): pass - class B: - # Conforms to P and PP - def f(self): pass - def g(self): pass - class C: - # Conforms to neither P nor PP - def g(self): pass - - self.assertIsInstance(A(), P) - self.assertIsInstance(B(), P) - self.assertIsInstance(B(), PP) - self.assertNotIsInstance(A(), PP) - self.assertNotIsInstance(C(), PP) - - class AA(_Protocol): - def f(self): return 1 - class BB(AA): pass - - self.assertEqual(BB().f(), 1) - - class CC(AA): pass - # BB is not a protocol since it doesn't explicitly subclass _Protocol. - self.assertNotIsInstance(CC(), BB) - - def test_builtin_class_and_protocol(self): - class P(_Protocol): - def __add__(self): pass - - self.assertIsInstance('', P) - self.assertIsInstance([], P) - self.assertIsInstance(1, P) - self.assertNotIsInstance({}, P) - - self.assertTrue(issubclass(str, P)) - self.assertFalse(issubclass(dict, P)) - - def test_generic_protocol(self): - t = TypeVar('t') - class P(_Protocol[t]): - x = 1 - class A: - x = 2 - self.assertIsInstance(A(), P) - - def test_indexing_in_protocol(self): - class P(_Protocol): - def __getitem__(self): pass - class A: - def __getitem__(self): pass - class B: + assert issubclass(list, typing.Reversible) + assert not issubclass(int, typing.Reversible) + + +class GenericTests(TestCase): + + def test_basics(self): + X = SimpleMapping[str, Any] + Y = SimpleMapping[AnyStr, str] + X[str, str] + Y[str, str] + with self.assertRaises(TypeError): + X[int, str] + with self.assertRaises(TypeError): + Y[str, bytes] + + def test_repr(self): + self.assertEqual(repr(SimpleMapping), + __name__ + '.' + 'SimpleMapping[~XK, ~XV]') + self.assertEqual(repr(MySimpleMapping), + __name__ + '.' + 'MySimpleMapping[~XK, ~XV]') + A = TypeVar('A', str) # Must be a subclass of XK. + B = TypeVar('B') + + class X(SimpleMapping[A, B]): pass - self.assertIsInstance(A(), P) - self.assertNotIsInstance(B(), P) - def test_sized(self): - self.assertIsInstance([], Sized) - self.assertIsInstance((), Sized) - self.assertIsInstance('', Sized) - self.assertIsInstance(b'', Sized) - self.assertIsInstance({}, Sized) - self.assertIsInstance(set(), Sized) - self.assertIsInstance(range(5), Sized) - self.assertNotIsInstance(1, Sized) + self.assertEqual(repr(X).split('.')[-1], 'X[~A, ~B]') - class A: - def __len__(self): pass + def test_errors(self): + with self.assertRaises(TypeError): + B = SimpleMapping[XK, Any] - self.assertIsInstance(A(), Sized) + class C(Generic[B]): + pass - def test_iterable(self): - self.assertIsInstance([], Iterable) - class A: - def __iter__(self): pass - def g(self): pass - self.assertIsInstance(A(), Iterable) - self.assertNotIsInstance(1, Iterable) + def test_repr_2(self): + PY32 = sys.version_info[:2] < (3, 3) - def test_iterator(self): - self.assertIsInstance(iter(''), Iterator) - self.assertIsInstance(iter([]), Iterator) - self.assertIsInstance(iter({}), Iterator) - self.assertNotIsInstance([], Iterator) + class C(Generic[T]): + pass - class A: - def __iter__(self): pass - def __next__(self): pass - self.assertIsInstance(A(), Iterator) + assert C.__module__ == __name__ + if not PY32: + assert C.__qualname__ == 'GenericTests.test_repr_2..C' + assert repr(C).split('.')[-1] == 'C[~T]' + X = C[int] + assert X.__module__ == __name__ + if not PY32: + assert X.__qualname__ == 'C' + assert repr(X).split('.')[-1] == 'C[int]' + + class Y(C[int]): + pass - class B: - def __iter__(self): pass - self.assertNotIsInstance(B(), Iterator) + assert Y.__module__ == __name__ + if not PY32: + assert Y.__qualname__ == 'GenericTests.test_repr_2..Y' + assert repr(Y).split('.')[-1] == 'Y[int]' - class C: - def __next__(self): pass - self.assertNotIsInstance(C(), Iterator) - - def test_class_inheritance_and_protocols(self): - class A: - def __iter__(self): pass - class B(A): - def __next__(self): pass - self.assertIsInstance(B(), Iterator) - self.assertNotIsInstance(A(), Iterator) - - def test_class_multiple_inheritance_and_protocols(self): - class A: - def __iter__(self): pass - class B: - def __next__(self): pass - class C(A, B): pass - self.assertIsInstance(C(), Iterator) - self.assertNotIsInstance(A(), Iterator) - self.assertNotIsInstance(B(), Iterator) - - def test_multiple_protocol_inheritance(self): - class P(_Protocol): - x = 1 - class P2(_Protocol): - y = 1 - class P3(P, P2, _Protocol): pass - - class A: - x = 1 - y = 1 - class B: - x = 1 - class C: - y = 1 - - self.assertIsInstance(A(), P3) - self.assertNotIsInstance(B(), P3) - self.assertNotIsInstance(C(), P3) - - def test_protocol_docstrings(self): - class P(_Protocol): - """blah""" - def f(self): pass - class A: - def f(self): pass - self.assertIsInstance(A(), P) - - def test_string_literal_in_annotation(self): - def f(a:'str') -> 'str': - return a + 'x' - def f(a:'Iterable[int]') -> 'List[int]': - return list(a) - - def test_undefined(self): - self.assertEqual(str(Undefined), '') - with self.assertRaises(AttributeError): - Undefined.x = 1 - with self.assertRaises(AttributeError): - Undefined.x - with self.assertRaises(TypeError): - if Undefined == 0: pass - with self.assertRaises(TypeError): - if Undefined != 0: pass - with self.assertRaises(TypeError): - hash(Undefined) + def test_eq_1(self): + assert Generic == Generic + assert Generic[T] == Generic[T] + assert Generic[KT] != Generic[VT] + + def test_eq_2(self): + + class A(Generic[T]): + pass + + class B(Generic[T]): + pass + + assert A == A + assert A != B + assert A[T] == A[T] + assert A[T] != B[T] + + def test_multiple_inheritance(self): + + class A(Generic[T, VT]): + pass + + class B(Generic[KT, T]): + pass + + class C(A, Generic[KT, VT], B): + pass + + assert C.__parameters__ == (T, VT, KT) + + def test_nested(self): + + class G(Generic): + pass + + 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) + assert a.get() == [1, 42] + + +class UndefinedTest(TestCase): + + def test_basics(self): + x = Undefined(int) + x = Undefined(Any) + x = Undefined(Union[int, str]) + x = Undefined(None) + + def test_errors(self): with self.assertRaises(TypeError): - if Undefined: pass + x = Undefined(42) + u = Undefined(int) with self.assertRaises(TypeError): - if not Undefined: pass + {u: 42} - def test_construct_class_with_abstract_method(self): - t = TypeVar('t') + def test_repr(self): + self.assertEqual(repr(Undefined(Any)), 'typing.Undefined(typing.Any)') - class A(Generic[t]): - @abstractmethod - def f(self): pass + def test_type_alias(self): + # These simply must not fail. + Undefined(typing.re.Pattern) + Undefined(typing.re.Pattern[str]) + Undefined(typing.re.Pattern[bytes]) + Undefined(typing.re.Pattern[Any]) - class B(A): - def f(self): pass - with self.assertRaises(TypeError): - A() - B() +class CastTest(TestCase): - def test_protocol_with_abstract_method(self): - class A(_Protocol): - @abstractmethod - def f(self): pass + def test_basics(self): + assert cast(int, 42) == 42 + assert cast(float, 42) == 42 + assert type(cast(float, 42)) is int + assert cast(Any, 42) == 42 + assert cast(list, 42) == 42 + assert cast(Union[str, float], 42) == 42 + assert cast(AnyStr, 42) == 42 + assert cast(None, 42) == 42 - with self.assertRaises(TypeError): - A() # No implementation for abstract method. + def test_errors(self): + # Bogus calls are not expected to fail. + cast(42, 42) + cast('hello', 42) + + +class ForwardRefTest(TestCase): + + 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 test_protocol_inheritance_with_abstract_method(self): - class A(_Protocol): - @abstractmethod - def f(self): pass - class B(A): + 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) + assert both_hints['left'] == both_hints['right'] == Optional[Node[T]] + assert both_hints['stuff'] == Optional[int] + assert 'blah' not in both_hints + + left_hints = get_type_hints(t.add_left) + assert left_hints['node'] == Optional[Node[T]] + + right_hints = get_type_hints(t.add_right) + assert right_hints['node'] == Optional[Node[T]] + + def test_union_forward(self): + + def foo(a: Union['T']): pass - with self.assertRaises(TypeError): - B() # No implementation for abstract method. - class C(A): - def f(self): pass - C() + self.assertEqual(get_type_hints(foo), {'a': Union[T]}) + + def test_tuple_forward(self): + + def foo(a: Tuple['T']): + pass + + self.assertEqual(get_type_hints(foo), {'a': Tuple[T]}) + + def test_callable_forward(self): + + def foo(a: Callable[['T'], 'T']): + pass + + self.assertEqual(get_type_hints(foo), {'a': Callable[[T], 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_name_error(self): + + def foo(a: 'Noode[T]'): + pass + + with self.assertRaises(NameError): + get_type_hints(foo) + + def test_no_type_check(self): + + @no_type_check + def foo(a: 'whatevers') -> {}: + pass + + th = get_type_hints(foo) + self.assertEqual(th, {}) + + def test_meta_no_type_check(self): + + @no_type_check_decorator + def magic_decorator(deco): + return deco + + self.assertEqual(magic_decorator.__name__, 'magic_decorator') + + @magic_decorator + def foo(a: 'whatevers') -> {}: + pass + + self.assertEqual(foo.__name__, 'foo') + th = get_type_hints(foo) + self.assertEqual(th, {}) + + +class OverloadTests(TestCase): + + def test_overload_exists(self): + from typing import overload + + def test_overload_fails(self): + from typing import overload - def test_overload(self): - with self.assertRaises(RuntimeError): - @overload - def f(): pass - with self.assertRaises(RuntimeError): - @overload - def g(x: int) -> None: pass with self.assertRaises(RuntimeError): @overload - def h(x: int) -> None: pass - @overload - def h(x: str) -> None: pass + def blah(): + pass - def test_optional(self): - # TODO: This test actually isn't very useful right now, but it will make sense - # once Union is modified to keep track of the given type arguments. - self.assertEqual(Optional[int], Union[int, None]) +class CollectionsAbcTests(TestCase): + + def test_hashable(self): + assert isinstance(42, typing.Hashable) + assert not isinstance([], typing.Hashable) -class Dummy: - """Dummy class defined in module scope""" + def test_iterable(self): + assert isinstance([], typing.Iterable) + assert isinstance([], typing.Iterable[int]) + assert not isinstance(42, typing.Iterable) + + def test_iterator(self): + it = iter([]) + assert isinstance(it, typing.Iterator) + assert isinstance(it, typing.Iterator[int]) + assert not isinstance(42, typing.Iterator) + + def test_sized(self): + assert isinstance([], typing.Sized) + assert not isinstance(42, typing.Sized) + + def test_container(self): + assert isinstance([], typing.Container) + assert not isinstance(42, typing.Container) + + def test_abstractset(self): + assert isinstance(set(), typing.AbstractSet) + assert not isinstance(42, typing.AbstractSet) + + def test_mutableset(self): + assert isinstance(set(), typing.MutableSet) + assert not isinstance(frozenset(), typing.MutableSet) + + def test_mapping(self): + assert isinstance({}, typing.Mapping) + assert not isinstance(42, typing.Mapping) + + def test_mutablemapping(self): + assert isinstance({}, typing.MutableMapping) + assert not isinstance(42, typing.MutableMapping) + + def test_sequence(self): + assert isinstance([], typing.Sequence) + assert not isinstance(42, typing.Sequence) + + def test_mutablesequence(self): + assert isinstance([], typing.MutableSequence) + assert not isinstance((), typing.MutableSequence) + + def test_bytestring(self): + assert isinstance(b'', typing.ByteString) + assert isinstance(bytearray(b''), typing.ByteString) + + def test_list(self): + assert issubclass(list, typing.List) + assert isinstance([], typing.List) + assert not isinstance((), typing.List) + t = typing.List[int] + assert isinstance([], t) + assert isinstance([42], t) + assert not isinstance([''], t) + + def test_set(self): + assert issubclass(set, typing.Set) + assert isinstance(set(), typing.Set) + assert not isinstance({}, typing.Set) + t = typing.Set[int] + assert isinstance(set(), t) + assert isinstance({42}, t) + assert not isinstance({''}, t) + + def test_mapping_views(self): + # TODO: These tests are kind of lame. + assert isinstance({}.keys(), typing.KeysView) + assert isinstance({}.items(), typing.ItemsView) + assert isinstance({}.values(), typing.ValuesView) + + def test_dict(self): + assert issubclass(dict, typing.Dict) + assert isinstance({}, typing.Dict) + assert not isinstance([], typing.Dict) + t = typing.Dict[int, str] + assert isinstance({}, t) + assert isinstance({42: ''}, t) + assert not isinstance({42: 42}, t) + assert not isinstance({'': 42}, t) + assert not isinstance({'': ''}, t) + + +class NamedTupleTests(TestCase): + + def test_basics(self): + Emp = NamedTuple('Emp', [('name', str), ('id', int)]) + assert issubclass(Emp, tuple) + joe = Emp('Joe', 42) + jim = Emp(name='Jim', id=1) + assert isinstance(joe, Emp) + assert isinstance(joe, tuple) + assert joe.name == 'Joe' + assert joe.id == 42 + assert jim.name == 'Jim' + assert jim.id == 1 + assert Emp.__name__ == 'Emp' + assert Emp._fields == ('name', 'id') + assert Emp._field_types == dict(name=str, id=int) + + +class IOTests(TestCase): + + def test_io(self): + + def stuff(a: IO) -> AnyStr: + return a.readline() + + a = stuff.__annotations__['a'] + assert a.__parameters__ == (AnyStr,) + + def test_textio(self): + + def stuff(a: TextIO) -> str: + return a.readline() + + a = stuff.__annotations__['a'] + assert a.__parameters__ == (str,) + + def test_binaryio(self): + + def stuff(a: BinaryIO) -> bytes: + return a.readline() + + a = stuff.__annotations__['a'] + assert a.__parameters__ == (bytes,) + + def test_io_submodule(self): + from typing.io import IO, TextIO, BinaryIO, __all__, __name__ + assert IO is typing.IO + assert TextIO is typing.TextIO + assert BinaryIO is typing.BinaryIO + assert set(__all__) == set(['IO', 'TextIO', 'BinaryIO']) + assert __name__ == 'typing.io' + + +class RETests(TestCase): + # Much of this is really testing _TypeAlias. + + def test_basics(self): + pat = re.compile('[a-z]+', re.I) + assert isinstance(pat, Pattern) + assert isinstance(pat, Pattern[str]) + assert not isinstance(pat, Pattern[bytes]) + assert issubclass(type(pat), Pattern) + assert issubclass(type(pat), Pattern[str]) + + mat = pat.search('12345abcde.....') + assert isinstance(mat, Match) + assert isinstance(mat, Match[str]) + assert not isinstance(mat, Match[bytes]) + assert issubclass(type(mat), Match) + assert issubclass(type(mat), Match[str]) + + p = Pattern[Union[str, bytes]] + assert isinstance(pat, p) + assert issubclass(Pattern[str], Pattern) + assert issubclass(Pattern[str], p) + + m = Match[Union[bytes, str]] + assert isinstance(mat, m) + assert issubclass(Match[bytes], Match) + assert issubclass(Match[bytes], m) + + def test_errors(self): + with self.assertRaises(TypeError): + # Doesn't fit AnyStr. + Pattern[int] + with self.assertRaises(TypeError): + # Can't change type vars? + Match[T] + m = Match[Union[str, bytes]] + with self.assertRaises(TypeError): + # Too complicated? + m[str] + + def test_repr(self): + assert repr(Pattern) == 'Pattern[~AnyStr]' + assert repr(Pattern[str]) == 'Pattern[str]' + assert repr(Pattern[bytes]) == 'Pattern[bytes]' + assert repr(Match) == 'Match[~AnyStr]' + assert repr(Match[str]) == 'Match[str]' + assert repr(Match[bytes]) == 'Match[bytes]' + + def test_re_submodule(self): + from typing.re import Match, Pattern, __all__, __name__ + assert Match is typing.Match + assert Pattern is typing.Pattern + assert set(__all__) == set(['Match', 'Pattern']) + assert __name__ == 'typing.re' + + +class AllTests(TestCase): + """Tests for __all__.""" + + def test_all(self): + from typing import __all__ as a + # Don't test everything, just spot-check the first and last of every category. + assert 'AbstractSet' in a + assert 'ValuesView' in a + assert 'POSIX' in a + assert 'WINDOWS' in a + assert 'cast' in a + assert 'overload' in a + assert 'io' in a + assert 're' in a + # Spot-check that stdlib modules aren't exported. + assert 'os' not in a + assert 'sys' not in a if __name__ == '__main__': - unittest.main() + main() diff --git a/lib-typing/3.2/typing.py b/lib-typing/3.2/typing.py index f5ccfe4a2ed7..d1ba5b542967 100644 --- a/lib-typing/3.2/typing.py +++ b/lib-typing/3.2/typing.py @@ -1,91 +1,1223 @@ -"""Static type checking helpers""" +# TODO: +# Support Python 3.2 -from abc import ABCMeta, abstractmethod, abstractproperty +# TODO nits: +# Get rid of asserts that are the caller's fault. +# Docstrings (e.g. ABCs). + +import abc +from abc import abstractmethod, abstractproperty import collections +import functools import inspect +import re as stdlib_re # Avoid confusion with the re we export. import sys -import re -import functools +import types +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc # Fallback for PY3.2. +# Please keep __all__ alphabetized within each category. __all__ = [ - # Type system related + # Generic classes and special types. + 'AbstractSet', 'Any', 'AnyStr', - 'Dict', + 'ByteString', 'Callable', + 'Container', + 'Dict', 'Generic', - 'GenericMeta', - 'IO', + 'Hashable', + 'ItemsView', + 'Iterable', + 'Iterator', + 'KeysView', 'List', - 'Match', + 'Mapping', + 'MappingView', + 'MutableMapping', + 'MutableSequence', + 'MutableSet', 'NamedTuple', 'Optional', - 'Pattern', + 'Reversible', + 'Sequence', 'Set', + 'Sized', + 'SupportsAbs', + 'SupportsFloat', + 'SupportsInt', + 'SupportsRound', 'Tuple', + 'TypeVar', 'Undefined', 'Union', + 'ValuesView', + # Compile-time constants. + 'POSIX', + 'PY2', + 'PY3', + 'WINDOWS', + # Functions and decorators. 'cast', + 'get_type_hints', + 'no_type_check', + 'no_type_check_decorator', 'overload', - 'TypeVar', - # Protocols and abstract base classes - 'Container', - 'Iterable', - 'Iterator', - 'Sequence', - 'Sized', - 'AbstractSet', - 'Mapping', - 'BinaryIO', - 'TextIO', -] + # Submodules. + 'io', + 're', + ] + + +# Simple constants defined in the PEP. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] >= 3 +WINDOWS = sys.platform == 'win32' +POSIX = not WINDOWS + + +def _qualname(x): + if sys.version_info[:2] >= (3, 3): + return x.__qualname__ + else: + # Fall back to just name. + return x.__name__ + + +class TypingMeta(type): + """Metaclass for every type defined below. + + This overrides __new__() to require an extra keyword parameter + '_root', which serves as a guard against naive subclassing of the + typing classes. Any legitimate class defined using a metaclass + derived from TypingMeta (including internal subclasses created by + e.g. Union[X, Y]) must pass _root=True. + + This also defines a dummy constructor (all the work is done in + __new__) and a nicer repr(). + """ + + _is_protocol = False + + def __new__(cls, name, bases, namespace, *, _root=False): + if not _root: + raise TypeError("Cannot subclass %s" % + (', '.join(map(_type_repr, bases)) or '()')) + return super().__new__(cls, name, bases, namespace) + + def __init__(self, *args, **kwds): + pass + + def _eval_type(self, globalns, localns): + """Override this in subclasses to interpret forward references. + + For example, Union['C'] is internally stored as + Union[_ForwardRef('C')], which should evaluate to _Union[C], + where C is an object found in globalns or localns (searching + localns first, of course). + """ + return self + + def _has_type_var(self): + return False + + def __repr__(self): + return '%s.%s' % (self.__module__, _qualname(self)) + + +class Final: + """Mix-in class to prevent instantiation.""" + + def __new__(self, *args, **kwds): + raise TypeError("Cannot instantiate %r" % self.__class__) + + +class _ForwardRef(TypingMeta): + """Wrapper to hold a forward reference.""" + + def __new__(cls, arg): + if not isinstance(arg, str): + raise TypeError('ForwardRef must be a string -- got %r' % (arg,)) + try: + code = compile(arg, '', 'eval') + except SyntaxError: + raise SyntaxError('ForwardRef must be an expression -- got %r' % + (arg,)) + self = super().__new__(cls, arg, (), {}, _root=True) + self.__forward_arg__ = arg + self.__forward_code__ = code + self.__forward_evaluated__ = False + self.__forward_value__ = None + return self + + def _eval_type(self, globalns, localns): + if not isinstance(localns, dict): + raise TypeError('ForwardRef localns must be a dict -- got %r' % + (localns,)) + if not isinstance(globalns, dict): + raise TypeError('ForwardRef globalns must be a dict -- got %r' % + (globalns,)) + if not self.__forward_evaluated__: + self.__forward_value__ = eval(self.__forward_code__, + globalns, localns) + self.__forward_evaluated__ = True + return self.__forward_value__ + + def __repr__(self): + return '_ForwardRef(%r)' % (self.__forward_arg__,) + + +class _TypeAlias: + """Internal helper class for defining generic variants of concrete types. + + Note that this is not a type; let's call it a pseudo-type. It can + be used in instance and subclass checks, e.g. isinstance(m, Match) + or issubclass(type(m), Match). However, it cannot be itself the + target of an issubclass() call; e.g. issubclass(Match, C) (for + some arbitrary class C) raises TypeError rather than returning + False. + """ + + def __init__(self, name, type_var, impl_type, type_checker): + """Constructor. + + Args: + name: The name, e.g. 'Pattern'. + type_var: The type parameter, e.g. AnyStr, or the + specific type, e.g. str. + impl_type: The implementation type. + type_checker: Function that takes an impl_type instance. + and returns a value that should be a type_var instance. + """ + assert isinstance(name, str), repr(name) + assert isinstance(type_var, type), repr(type_var) + assert isinstance(impl_type, type), repr(impl_type) + assert not isinstance(impl_type, TypingMeta), repr(impl_type) + self.name = name + self.type_var = type_var + self.impl_type = impl_type + self.type_checker = type_checker + + def __repr__(self): + return "%s[%s]" % (self.name, _type_repr(self.type_var)) + + def __getitem__(self, parameter): + assert isinstance(parameter, type), repr(parameter) + if not isinstance(self.type_var, TypeVar): + raise TypeError("%s cannot be further parameterized." % self) + if not issubclass(parameter, self.type_var): + raise TypeError("%s is not a valid substitution for %s." % + (parameter, self.type_var)) + return self.__class__(self.name, parameter, + self.impl_type, self.type_checker) + + def __instancecheck__(self, obj): + return (isinstance(obj, self.impl_type) and + isinstance(self.type_checker(obj), self.type_var)) + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if isinstance(cls, _TypeAlias): + # Covariance. For now, we compare by name. + return (cls.name == self.name and + issubclass(cls.type_var, self.type_var)) + else: + # Note that this is too lenient, because the + # implementation type doesn't carry information about + # whether it is about bytes or str (for example). + return issubclass(cls, self.impl_type) + + +def _has_type_var(t): + return t is not None and isinstance(t, TypingMeta) and t._has_type_var() + + +def _eval_type(t, globalns, localns): + if isinstance(t, TypingMeta): + return t._eval_type(globalns, localns) + else: + return t + + +def _type_check(arg, msg): + """Check that the argument is a type, and return it. + + As a special case, accept None and return type(None) instead. + Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable. + + The msg argument is a human-readable error message, e.g. + + "Union[arg, ...]: arg should be a type." + + We append the repr() of the actual value (truncated to 100 chars). + """ + if arg is None: + return type(None) + if isinstance(arg, str): + arg = _ForwardRef(arg) + if not isinstance(arg, (type, _TypeAlias)): + raise TypeError(msg + " Got %.100r." % (arg,)) + return arg + + +def _type_repr(obj): + """Return the repr() of an object, special-casing types. + + If obj is a type, we return a shorter version than the default + type.__repr__, based on the module and qualified name, which is + typically enough to uniquely identify a type. For everything + else, we fall back on repr(obj). + """ + if isinstance(obj, type) and not isinstance(obj, TypingMeta): + if obj.__module__ == 'builtins': + return _qualname(obj) + else: + return '%s.%s' % (obj.__module__, _qualname(obj)) + else: + return repr(obj) + + +class AnyMeta(TypingMeta): + """Metaclass for Any.""" + + def __new__(cls, name, bases, namespace, _root=False): + self = super().__new__(cls, name, bases, namespace, _root=_root) + return self + + def __instancecheck__(self, instance): + return True + + def __subclasscheck__(self, cls): + if not isinstance(cls, type): + return super().__subclasscheck__(cls) # To TypeError. + return True + + +class Any(Final, metaclass=AnyMeta, _root=True): + """Special type indicating an unconstrained type. + + - Any object is an instance of Any. + - Any class is a subclass of Any. + - As a special case, Any and object are subclasses of each other. + """ + + +class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True): + """Type variable. + + Usage:: + + T1 = TypeVar('T1') # Unconstrained + T2 = TypeVar('T2', t1, t2, ...) # Constrained to any of (t1, t2, ...) + + For an unconstrained type variable T, isinstance(x, T) is false + for all x, and similar for issubclass(cls, T). Example:: + T = TypeVar('T') + assert not isinstance(42, T) + assert not issubclass(int, T) -class GenericMeta(ABCMeta): - """Metaclass for (abstract) generic classes that support type indexing. + For a constrained type variable T, isinstance(x, T) is true for + any x that is an instance of at least one of T's constraints, + and similar for issubclass(cls, T). Example:: + + AnyStr = TypeVar('AnyStr', str, bytes) + # AnyStr behaves similar to Union[str, bytes] (but not exactly!) + assert not isinstance(42, AnyStr) + assert isinstance('', AnyStr) + assert isinstance(b'', AnyStr) + assert not issubclass(int, AnyStr) + assert issubclass(str, AnyStr) + assert issubclass(bytes, AnyStr) + + Type variables that are distinct objects are never equal (even if + created with the same parameters). + + You can temporarily *bind* a type variable to a specific type by + calling its bind() method and using the result as a context + manager (i.e., in a with-statement). Example:: + + with T.bind(int): + # In this block, T is nearly an alias for int. + assert isinstance(42, T) + assert issubclass(int, T) + + There is still a difference between T and int; issubclass(T, int) + is False. However, issubclass(int, T) is true. + + Binding a constrained type variable will replace the binding type + with the most derived of its constraints that matches. Example:: + + class MyStr(str): + pass + + with AnyStr.bind(MyStr): + # In this block, AnyStr is an alias for str, not for MyStr. + assert isinstance('', AnyStr) + assert issubclass(str, AnyStr) + assert not isinstance(b'', AnyStr) + assert not issubclass(bytes, AnyStr) - This is used for both ABCs and ordinary classes. """ - def __new__(mcls, name, bases, namespace): - cls = super().__new__(mcls, name, bases, namespace) - # 'Protocol' must be an explicit base class in order for a class to - # be a protocol. - cls._is_protocol = name == '_Protocol' or _Protocol in bases - return cls + def __new__(cls, name, *constraints): + self = super().__new__(cls, name, (Final,), {}, _root=True) + msg = "TypeVar(name, constraint, ...): constraints must be types." + self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) + self.__binding__ = None + return self + + def _has_type_var(self): + return True - def __getitem__(self, args): - # Just ignore args; they are for compile-time checks only. + def __repr__(self): + return '~' + self.__name__ + + def __instancecheck__(self, instance): + if self.__binding__ is not None: + return isinstance(instance, self.__binding__) + elif not self.__constraints__: + return False + else: + return isinstance(instance, Union[self.__constraints__]) + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if cls is self: + return True + elif self.__binding__ is not None: + return issubclass(cls, self.__binding__) + elif not self.__constraints__: + return False + else: + return issubclass(cls, Union[self.__constraints__]) + + def bind(self, binding): + binding = _type_check(binding, "TypeVar.bind(t): t must be a type.") + if self.__constraints__: + best = None + for t in self.__constraints__: + if (issubclass(binding, t) and + (best is None or issubclass(t, best))): + best = t + if best is None: + raise TypeError( + "TypeVar.bind(t): t must match one of the constraints.") + binding = best + return VarBinding(self, binding) + + def _bind(self, binding): + old_binding = self.__binding__ + self.__binding__ = binding + return old_binding + + def _unbind(self, binding, old_binding): + assert self.__binding__ is binding, (self.__binding__, + binding, old_binding) + self.__binding__ = old_binding + + +# Compatibility for for mypy's typevar(). +def typevar(name, values=()): + return TypeVar(name, *values) + + +class VarBinding: + """TypeVariable binding returned by TypeVar.bind().""" + + # TODO: This is not thread-safe. We could solve this in one of + # two ways: by using a lock or by using thread-local state. But + # either of these feels overly heavy, and still doesn't work + # e.g. in an asyncio Task. + + def __init__(self, var, binding): + assert isinstance(var, TypeVar), (var, binding) + assert isinstance(binding, type), (var, binding) + self._var = var + self._binding = binding + self._old_binding = None + self._entered = False + + def __enter__(self): + if self._entered: + # This checks for the following scenario: + # bv = T.bind() + # with bv: + # with bv: # Will raise here. + # ... + # However, the following scenario is OK (if somewhat odd): + # bv = T.bind() + # with bv: + # ... + # with bv: + # ... + # The following scenario is also fine: + # with T.bind(): + # with T.bind(): + # ... + raise TypeError("Cannot reuse variable binding recursively.") + self._old_binding = self._var._bind(self._binding) + self._entered = True + + def __exit__(self, *args): + try: + self._var._unbind(self._binding, self._old_binding) + finally: + self._entered = False + self._old_binding = None + + +# Some unconstrained type variables. These are used by the container types. +# TODO: Don't export these. +T = TypeVar('T') # Any type. +KT = TypeVar('KT') # Key type. +VT = TypeVar('VT') # Value type. + +# A useful type variable with constraints. This represents string types. +# TODO: What about bytearray, memoryview? +AnyStr = TypeVar('AnyStr', bytes, str) + + +class UnionMeta(TypingMeta): + """Metaclass for Union.""" + + def __new__(cls, name, bases, namespace, parameters=None, _root=False): + if parameters is None: + return super().__new__(cls, name, bases, namespace, _root=_root) + if not isinstance(parameters, tuple): + raise TypeError("Expected parameters=") + # Flatten out Union[Union[...], ...] and type-check non-Union args. + params = [] + msg = "Union[arg, ...]: each arg must be a type." + for p in parameters: + if isinstance(p, UnionMeta): + params.extend(p.__union_params__) + else: + params.append(_type_check(p, msg)) + # Weed out strict duplicates, preserving the first of each occurrence. + all_params = set(params) + if len(all_params) < len(params): + new_params = [] + for t in params: + if t in all_params: + new_params.append(t) + all_params.remove(t) + params = new_params + assert not all_params, all_params + # Weed out subclasses. + # E.g. Union[int, Employee, Manager] == Union[int, Employee]. + # If Any or object is present it will be the sole survivor. + # If both Any and object are present, Any wins. + all_params = set(params) + for t1 in params: + if t1 is Any: + return Any + if any(issubclass(t1, t2) for t2 in all_params - {t1}): + all_params.remove(t1) + # It's not a union if there's only one type left. + if len(all_params) == 1: + return all_params.pop() + # Create a new class with these params. + self = super().__new__(cls, name, bases, {}, _root=True) + self.__union_params__ = tuple(t for t in params if t in all_params) + self.__union_set_params__ = frozenset(self.__union_params__) return self + def _eval_type(self, globalns, localns): + p = tuple(_eval_type(t, globalns, localns) + for t in self.__union_params__) + if p == self.__union_params__: + return self + else: + return self.__class__(self.__name__, self.__bases__, {}, + p, _root=True) + + def _has_type_var(self): + if self.__union_params__: + for t in self.__union_params__: + if _has_type_var(t): + return True + return False + + def __repr__(self): + r = super().__repr__() + if self.__union_params__: + r += '[%s]' % (', '.join(_type_repr(t) + for t in self.__union_params__)) + return r + + def __getitem__(self, parameters): + if self.__union_params__ is not None: + raise TypeError( + "Cannot subscript an existing Union. Use Union[u, t] instead.") + if parameters == (): + raise TypeError("Cannot take a Union of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + return self.__class__(self.__name__, self.__bases__, + dict(self.__dict__), parameters, _root=True) + + def __eq__(self, other): + if not isinstance(other, UnionMeta): + return NotImplemented + return self.__union_set_params__ == other.__union_set_params__ + + def __hash__(self): + return hash(self.__union_set_params__) + + def __instancecheck__(self, instance): + return any(isinstance(instance, t) for t in self.__union_params__) + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if self.__union_params__ is None: + return isinstance(cls, UnionMeta) + elif isinstance(cls, UnionMeta): + if cls.__union_params__ is None: + return False + return all(issubclass(c, self) for c in (cls.__union_params__)) + elif isinstance(cls, TypeVar): + if cls in self.__union_params__: + return True + if cls.__constraints__: + return issubclass(Union[cls.__constraints__], self) + return False + else: + return any(issubclass(cls, t) for t in self.__union_params__) + + +class Union(Final, metaclass=UnionMeta, _root=True): + """Union type; Union[X, Y] means either X or Y. + + To define a union, use e.g. Union[int, str]. Details: + + - The arguments must be types and there must be at least one. + + - None as an argument is a special case and is replaced by + type(None). + + - Unions of unions are flattened, e.g.:: + + Union[Union[int, str], float] == Union[int, str, float] + + - Unions of a single argument vanish, e.g.:: + + Union[int] == int # The constructore actually returns int + + - Redundant arguments are skipped, e.g.:: + + Union[int, str, int] == Union[int, str] + + - When comparing unions, the argument order is ignored, e.g.:: + + Union[int, str] == Union[str, int] + + - When two arguments have a subclass relationship, the least + derived argument is kept, e.g.:: + + class Employee: pass + class Manager(Employee): pass + Union[int, Employee, Manager] == Union[int, Employee] + Union[Manager, int, Employee] == Union[int, Employee] + Union[Employee, Manager] == Employee + + - Corollary: if Any is present it is the sole survivor, e.g.:: + + Union[int, Any] == Any + + - Similar for object:: + + Union[int, object] == object + + - To cut a tie: Union[object, Any] == Union[Any, object] == Any. + + - You cannot subclass or instantiate a union. -class _Protocol(metaclass=GenericMeta): - """Internal base class for protocol classes (structural isinstance checks).""" + - You cannot write Union[X][Y] (what would it mean?). + + - You can use Optional[X] as a shorthand for Union[X, None]. + """ + + # Unsubscripted Union type has params set to None. + __union_params__ = None + __union_set_params__ = None + + +class OptionalMeta(TypingMeta): + """Metaclass for Optional.""" + + def __new__(cls, name, bases, namespace, _root=False): + return super().__new__(cls, name, bases, namespace, _root=_root) + + def __getitem__(self, arg): + arg = _type_check(arg, "Optional[t] requires a single type.") + return Union[arg, type(None)] + + +class Optional(Final, metaclass=OptionalMeta, _root=True): + """Optional type. + + Optional[X] is equivalent to Union[X, type(None)]. + """ + + +class TupleMeta(TypingMeta): + """Metaclass for Tuple.""" + + def __new__(cls, name, bases, namespace, parameters=None, _root=False): + self = super().__new__(cls, name, bases, namespace, _root=_root) + self.__tuple_params__ = parameters + return self + + def _has_type_var(self): + if self.__tuple_params__: + for t in self.__tuple_params__: + if _has_type_var(t): + return True + return False + + def _eval_type(self, globalns, localns): + tp = self.__tuple_params__ + if tp is None: + return self + p = tuple(_eval_type(t, globalns, localns) for t in tp) + if p == self.__tuple_params__: + return self + else: + return self.__class__(self.__name__, self.__bases__, {}, + p, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__tuple_params__ is not None: + r += '[%s]' % ( + ', '.join(_type_repr(p) for p in self.__tuple_params__)) + return r + + def __getitem__(self, parameters): + if self.__tuple_params__ is not None: + raise TypeError("Cannot re-parameterize %r" % (self,)) + if not isinstance(parameters, tuple): + parameters = (parameters,) + msg = "Class[arg, ...]: each arg must be a type." + parameters = tuple(_type_check(p, msg) for p in parameters) + return self.__class__(self.__name__, self.__bases__, + dict(self.__dict__), parameters, _root=True) + + def __eq__(self, other): + if not isinstance(other, TupleMeta): + return NotImplemented + return self.__tuple_params__ == other.__tuple_params__ + + def __hash__(self): + return hash(self.__tuple_params__) + + def __instancecheck__(self, t): + if not isinstance(t, tuple): + return False + if self.__tuple_params__ is None: + return True + return (len(t) == len(self.__tuple_params__) and + all(isinstance(x, p) + for x, p in zip(t, self.__tuple_params__))) + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if not isinstance(cls, type): + return super().__subclasscheck__(cls) # To TypeError. + if issubclass(cls, tuple): + return True # Special case. + if not isinstance(cls, TupleMeta): + return super().__subclasscheck__(cls) # False. + if self.__tuple_params__ is None: + return True + if cls.__tuple_params__ is None: + return False # ??? + # Covariance. + return (len(self.__tuple_params__) == len(cls.__tuple_params__) and + all(issubclass(x, p) + for x, p in zip(cls.__tuple_params__, + self.__tuple_params__))) + + +class Tuple(Final, metaclass=TupleMeta, _root=True): + """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. + + Example: Tuple[T1, T2] is a tuple of two elements corresponding + to type variables T1 and T2. Tuple[int, float, str] is a tuple + of an int, a float and a string. + + To specify a variable-length tuple of homogeneous type, use Sequence[T]. + """ + + +class CallableMeta(TypingMeta): + """Metaclass for Callable.""" + + def __new__(cls, name, bases, namespace, _root=False, + args=None, result=None): + if args is None and result is None: + pass # Must be 'class Callable'. + else: + if args is not Ellipsis: + if not isinstance(args, list): + raise TypeError("Callable[args, result]: " + "args must be a list." + " Got %.100r." % (args,)) + msg = "Callable[[arg, ...], result]: each arg must be a type." + args = tuple(_type_check(arg, msg) for arg in args) + msg = "Callable[args, result]: result must be a type." + result = _type_check(result, msg) + self = super().__new__(cls, name, bases, namespace, _root=_root) + self.__args__ = args + self.__result__ = result + return self + + def _has_type_var(self): + if self.__args__: + for t in self.__args__: + if _has_type_var(t): + return True + return _has_type_var(self.__result__) + + def _eval_type(self, globalns, localns): + if self.__args__ is None and self.__result__ is None: + return self + args = [_eval_type(t, globalns, localns) for t in self.__args__] + result = _eval_type(self.__result__, globalns, localns) + if args == self.__args__ and result == self.__result__: + return self + else: + return self.__class__(self.__name__, self.__bases__, {}, + args=args, result=result, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__args__ is not None or self.__result__ is not None: + r += '%s[[%s], %s]' % (_qualname(self), + ', '.join(_type_repr(t) + for t in self.__args__), + _type_repr(self.__result__)) + return r + + def __getitem__(self, parameters): + if self.__args__ is not None or self.__result__ is not None: + raise TypeError("This Callable type is already parameterized.") + if not isinstance(parameters, tuple) or len(parameters) != 2: + raise TypeError( + "Callable must be used as Callable[[arg, ...], result].") + args, result = parameters + return self.__class__(self.__name__, self.__bases__, + dict(self.__dict__), _root=True, + args=args, result=result) + + def __eq__(self, other): + if not isinstance(other, CallableMeta): + return NotImplemented + return (self.__args__ == other.__args__ and + self.__result__ == other.__result__) + + def __hash__(self): + return hash(self.__args__) ^ hash(self.__result__) + + def __instancecheck__(self, instance): + if not callable(instance): + return False + if self.__args__ is None and self.__result__ is None: + return True + assert self.__args__ is not None + assert self.__result__ is not None + my_args, my_result = self.__args__, self.__result__ + # Would it be better to use Signature objects? + try: + (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, + annotations) = inspect.getfullargspec(instance) + except TypeError: + return False # We can't find the signature. Give up. + msg = ("When testing isinstance(, Callable[...], " + "'s annotations must be types.") + if my_args is not Ellipsis: + if kwonlyargs and (not kwonlydefaults or + len(kwonlydefaults) < len(kwonlyargs)): + return False + if isinstance(instance, types.MethodType): + # For methods, getfullargspec() includes self/cls, + # but it's not part of the call signature, so drop it. + del args[0] + min_call_args = len(args) + if defaults: + min_call_args -= len(defaults) + if varargs: + max_call_args = 999999999 + if len(args) < len(my_args): + args += [varargs] * (len(my_args) - len(args)) + else: + max_call_args = len(args) + if not min_call_args <= len(my_args) <= max_call_args: + return False + for my_arg_type, name in zip(my_args, args): + if name in annotations: + annot_type = _type_check(annotations[name], msg) + else: + annot_type = Any + if not issubclass(my_arg_type, annot_type): + return False + # TODO: If mutable type, check invariance? + if 'return' in annotations: + annot_return_type = _type_check(annotations['return'], msg) + # Note contravariance here! + if not issubclass(annot_return_type, my_result): + return False + # Can't find anything wrong... + return True + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if not isinstance(cls, CallableMeta): + return super().__subclasscheck__(cls) + if self.__args__ is None and self.__result__ is None: + return True + # We're not doing covariance or contravariance -- this is *invariance*. + return self == cls + + +class Callable(Final, metaclass=CallableMeta, _root=True): + """Callable type; Callable[[int], str] is a function of (int) -> str. + + The subscription syntax must always be used with exactly two + values: the argument list and the return type. The argument list + must be a list of types; the return type must be a single type. + + There is no syntax to indicate optional or keyword arguments, + such function types are rarely used as callback types. + """ + + +class GenericMeta(TypingMeta, abc.ABCMeta): + """Metaclass for generic types.""" + + # TODO: Constrain more how Generic is used; only a few + # standard patterns should be allowed. + + __extra__ = None + + def __new__(cls, name, bases, namespace, parameters=None, extra=None): + if parameters is None: + # Extract parameters from direct base classes. Only + # direct bases are considered and only those that are + # themselves generic, and parameterized with type + # variables. Don't use bases like Any, Union, Tuple, + # Callable or type variables. + params = None + for base in bases: + if isinstance(base, TypingMeta): + if not isinstance(base, GenericMeta): + raise TypeError( + "You cannot inherit from magic class %s" % + repr(base)) + if base.__parameters__ is None: + continue # The base is unparameterized. + for bp in base.__parameters__: + if _has_type_var(bp) and not isinstance(bp, TypeVar): + raise TypeError( + "Cannot inherit from a generic class " + "parameterized with " + "non-type-variable %s" % bp) + if params is None: + params = [] + if bp not in params: + params.append(bp) + if params is not None: + parameters = tuple(params) + self = super().__new__(cls, name, bases, namespace, _root=True) + self.__parameters__ = parameters + if extra is not None: + self.__extra__ = extra + # Else __extra__ is inherited, eventually from the + # (meta-)class default above. + return self + + def _has_type_var(self): + if self.__parameters__: + for t in self.__parameters__: + if _has_type_var(t): + return True + return False + + def __repr__(self): + r = super().__repr__() + if self.__parameters__ is not None: + r += '[%s]' % ( + ', '.join(_type_repr(p) for p in self.__parameters__)) + return r + + def __eq__(self, other): + if not isinstance(other, GenericMeta): + return NotImplemented + return (self.__name__ == other.__name__ and + self.__parameters__ == other.__parameters__) + + def __hash__(self): + return hash((self.__name__, self.__parameters__)) + + def __getitem__(self, params): + if not isinstance(params, tuple): + params = (params,) + if not params: + raise TypeError("Cannot have empty parameter list") + msg = "Parameters to generic types must be types." + params = tuple(_type_check(p, msg) for p in params) + if self.__parameters__ is None: + for p in params: + if not isinstance(p, TypeVar): + raise TypeError("Initial parameters must be " + "type variables; got %s" % p) + else: + if len(params) != len(self.__parameters__): + raise TypeError("Cannot change parameter count from %d to %d" % + (len(self.__parameters__), len(params))) + for new, old in zip(params, self.__parameters__): + if isinstance(old, TypeVar) and not old.__constraints__: + # Substituting for an unconstrained TypeVar is always OK. + continue + if not issubclass(new, old): + raise TypeError( + "Cannot substitute %s for %s in %s" % + (_type_repr(new), _type_repr(old), self)) + + return self.__class__(self.__name__, self.__bases__, + dict(self.__dict__), + parameters=params, extra=self.__extra__) + + def __subclasscheck__(self, cls): + if cls is Any: + return True + if super().__subclasscheck__(cls): + return True + if self.__extra__ is None: + return False + return issubclass(cls, self.__extra__) + + def __instancecheck__(self, obj): + if super().__instancecheck__(obj): + return True + if self.__extra__ is None: + return False + return isinstance(obj, self.__extra__) + + +class Generic(metaclass=GenericMeta): + """Abstract base class for generic types. + + A generic type is typically declared by inheriting from an + instantiation of this class with one or more type variables. + For example, a generic mapping type might be defined as:: + + class Mapping(Generic[KT, VT]): + def __getitem__(self, key: KT) -> VT: + ... + # Etc. + + This class can then be used as follows:: + + def lookup_name(mapping: Mapping, key: KT, default: VT) -> VT: + try: + return mapping[key] + except KeyError: + return default + + For clarity the type variables may be redefined, e.g.:: + + X = TypeVar('X') + Y = TypeVar('Y') + def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y: + # Same body as above. + """ + + +class Undefined: + """An undefined value. + + Example:: + + x = Undefined(typ) + + This tells the type checker that x has the given type but its + value should be considered undefined. At runtime x is an instance + of Undefined. The actual type can be introspected by looking at + x.__type__ and its str() and repr() are defined, but any other + operations or attributes will raise an exception. + + An alternative syntax is also supported: + + x = Undefined # type: typ + + This has the same meaning to the static type checker but uses less + overhead at run-time, at the cost of not being introspectible. + + NOTE: Do not under any circumstances check for Undefined. We + don't want this to become something developers rely upon, like + JavaScript's undefined. Code that returns or uses an Undefined + value in any way should be considered broken. Static type + checkers should warn about using potentially Undefined values. + """ + + __slots__ = ['__type__'] + + def __new__(cls, typ): + typ = _type_check(typ, "Undefined(t): t must be a type.") + self = super().__new__(cls) + self.__type__ = typ + return self + + __hash__ = None + + def __repr__(self): + return '%s(%s)' % (_type_repr(self.__class__), + _type_repr(self.__type__)) + + +def cast(typ, val): + """Cast a value to a type. + + This returns the value unchanged. To the type checker this + signals that the return value has the designated type, but at + runtime we intentionally don't check anything (we want this + to be as fast as possible). + """ + return val + + +def _get_defaults(func): + """Internal helper to extract the default arguments, by name.""" + code = func.__code__ + pos_count = code.co_argcount + kw_count = code.co_kwonlyargcount + arg_names = code.co_varnames + kwarg_names = arg_names[pos_count:pos_count+kw_count] + arg_names = arg_names[:pos_count] + defaults = func.__defaults__ or () + kwdefaults = func.__kwdefaults__ + res = dict(kwdefaults) if kwdefaults else {} + pos_offset = pos_count - len(defaults) + for name, value in zip(arg_names[pos_offset:], defaults): + assert name not in res + res[name] = value + return res + + +def get_type_hints(obj, globalns=None, localns=None): + """Return type hints for a function or method object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, and if necessary + adds Optional[t] if a default value equal to None is set. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, the defaults are taken from the + globals and locals of the caller, respectively. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if getattr(obj, '__no_type_check__', None): + return {} + if globalns is None: + globalns = sys._getframe(1).f_globals + if localns is None: + localns = sys._getframe(1).f_locals + elif localns is None: + localns = globalns + defaults = _get_defaults(obj) + hints = dict(obj.__annotations__) + for name, value in hints.items(): + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + if name in defaults and defaults[name] is None: + value = Optional[value] + hints[name] = value + return hints + + +# TODO: Also support this as a class decorator. +def no_type_check(func): + """Decorator to indicate that annotations are not type hints. + + This mutates the function in place. + """ + func.__no_type_check__ = True + return func + + +def no_type_check_decorator(decorator): + """Decorator to give another decorator the @no_type_check effect. + + This wraps the decorator with something that wraps the decorated + function in @no_type_check. + """ + + @functools.wraps(decorator) + def wrapped_decorator(*args, **kwds): + func = decorator(*args, **kwds) + func = no_type_check(func) + return func + + return wrapped_decorator + + +def overload(func): + raise RuntimeError("Overloading is only supported in library stubs") + + +class _Protocol(Generic): + """Internal base class for protocol classes. + + This implements a simple-minded structural isinstance check + (similar but more general than the one-offs in collections.abc + such as Hashable). + """ + + _is_protocol = True @classmethod - def __subclasshook__(cls, c): - if not cls._is_protocol: + def __subclasshook__(self, cls): + if not self._is_protocol: # No structural checks since this isn't a protocol. return NotImplemented - if cls is _Protocol: + if self is _Protocol: # Every class is a subclass of the empty protocol. return True # Find all attributes defined in the protocol. - attrs = cls._get_protocol_attrs() + attrs = self._get_protocol_attrs() for attr in attrs: - if not any(attr in d.__dict__ for d in c.__mro__): + if not any(attr in d.__dict__ for d in cls.__mro__): return NotImplemented return True @classmethod - def _get_protocol_attrs(cls): + def _get_protocol_attrs(self): # Get all Protocol base classes. protocol_bases = [] - for c in cls.__mro__: + for c in self.__mro__: if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol': protocol_bases.append(c) @@ -94,7 +1226,7 @@ def _get_protocol_attrs(cls): for base in protocol_bases: for attr in base.__dict__.keys(): # Include attributes not defined in any non-protocol bases. - for c in cls.__mro__: + for c in self.__mro__: if (c is not base and attr in c.__dict__ and not getattr(c, '_is_protocol', False)): break @@ -110,320 +1242,343 @@ def _get_protocol_attrs(cls): return attrs -class Generic(metaclass=GenericMeta): - """Base class for (abstract) generic classes.""" +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. +Hashable = collections_abc.Hashable # Not generic. -class TypeAlias: - """Class for defining generic aliases for library types.""" - def __init__(self, target_type): - self.target_type = target_type +class Iterable(Generic[T], extra=collections_abc.Iterable): + pass - def __getitem__(self, typeargs): - return self.target_type +class Iterator(Iterable, extra=collections_abc.Iterator): + pass -Traceback = object() # TODO proper type object +class SupportsInt(_Protocol): -# Define aliases for built-in types that support indexing. -List = TypeAlias(list) -Dict = TypeAlias(dict) -Set = TypeAlias(set) -Tuple = TypeAlias(tuple) -Callable = TypeAlias(callable) -Pattern = TypeAlias(type(re.compile(''))) -Match = TypeAlias(type(re.match('', ''))) + @abstractmethod + def __int__(self) -> int: + pass -def union(x): return x +class SupportsFloat(_Protocol): + @abstractmethod + def __float__(self) -> float: + pass -Union = TypeAlias(union) +class SupportsAbs(_Protocol[T]): -class _Optional: - def __getitem__(self, typearg): - return Union[typearg, None] + @abstractmethod + def __abs__(self) -> T: + pass -Optional = _Optional() +class SupportsRound(_Protocol[T]): + @abstractmethod + def __round__(self, ndigits: int = 0) -> T: + pass -def NamedTuple(typename, fields): - return collections.namedtuple(typename, - (name for name, type in fields)) +class Reversible(_Protocol[T]): -class TypeVar: - def __init__(self, name, *values): - self.name = name - if not values: - values = None - self.values = values + @abstractmethod + def __reversed__(self) -> 'Iterator[T]': + pass -# Predefined type variables. -AnyStr = TypeVar('AnyStr', str, bytes) +Sized = collections_abc.Sized # Not generic. -def Any(x): - """The Any type; can also be used to cast a value to type Any.""" - return x +class Container(Generic[T], extra=collections_abc.Container): + pass -def cast(type, object): - """Cast a value to a type. - This only affects static checking; simply return object at runtime. - """ - return object +# Callable was defined earlier. -def overload(func): - raise RuntimeError("Overloading only supported in library stubs") +class AbstractSet(Sized, Iterable, Container, extra=collections_abc.Set): + pass -class Undefined: - """Class that represents an undefined value with a specified type. - - At runtime the name Undefined is bound to an instance of this - class. The intent is that any operation on an Undefined object - raises an exception, including use in a boolean context. Some - operations cannot be disallowed: Undefined can be used as an - operand of 'is', and it can be assigned to variables and stored in - containers. - - 'Undefined' makes it possible to declare the static type of a - variable even if there is no useful default value to initialize it - with: - - from typing import Undefined - x = Undefined(int) - y = Undefined # type: int - - The latter form can be used if efficiency is of utmost importance, - since it saves a call operation and potentially additional - operations needed to evaluate a type expression. Undefined(x) - just evaluates to Undefined, ignoring the argument value. - """ +class MutableSet(AbstractSet, extra=collections_abc.MutableSet): + pass - def __repr__(self): - return '' - def __setattr__(self, attr, value): - raise AttributeError("'Undefined' object has no attribute '%s'" % attr) +class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT], + extra=collections_abc.Mapping): + pass - def __eq__(self, other): - raise TypeError("'Undefined' object cannot be compared") - def __call__(self, type): - return self +class MutableMapping(Mapping, extra=collections_abc.MutableMapping): + pass - def __bool__(self): - raise TypeError("'Undefined' object is not valid as a boolean") +class Sequence(Sized, Iterable, Container, extra=collections_abc.Sequence): + pass -Undefined = Undefined() +class MutableSequence(Sequence, extra=collections_abc.MutableSequence): + pass -# Abstract classes +class ByteString(Sequence[int], extra=collections_abc.ByteString): + pass -T = TypeVar('T') -KT = TypeVar('KT') -VT = TypeVar('VT') +ByteString.register(type(memoryview(b''))) -class SupportsInt(_Protocol): - @abstractmethod - def __int__(self) -> int: pass +class _ListMeta(GenericMeta): -class SupportsFloat(_Protocol): - @abstractmethod - def __float__(self) -> float: pass + def __instancecheck__(self, obj): + if not super().__instancecheck__(obj): + return False + itemtype = self.__parameters__[0] + for x in obj: + if not isinstance(x, itemtype): + return False + return True -class SupportsAbs(_Protocol[T]): - @abstractmethod - def __abs__(self) -> T: pass +class List(list, MutableSequence, metaclass=_ListMeta): + pass -class SupportsRound(_Protocol[T]): - @abstractmethod - def __round__(self, ndigits: int = 0) -> T: pass +class _SetMeta(GenericMeta): + def __instancecheck__(self, obj): + if not super().__instancecheck__(obj): + return False + itemtype = self.__parameters__[0] + for x in obj: + if not isinstance(x, itemtype): + return False + return True -class Reversible(_Protocol[T]): - @abstractmethod - def __reversed__(self) -> 'Iterator[T]': pass +class Set(set, MutableSet, metaclass=_SetMeta): + pass -class Sized(_Protocol): - @abstractmethod - def __len__(self) -> int: pass +class MappingView(Sized, Iterable, extra=collections_abc.MappingView): + pass -class Container(_Protocol[T]): - @abstractmethod - def __contains__(self, x) -> bool: pass +class KeysView(MappingView, Set[KT], extra=collections_abc.KeysView): + pass -class Iterable(_Protocol[T]): - @abstractmethod - def __iter__(self) -> 'Iterator[T]': pass +# TODO: Enable Set[Tuple[KT, VT]] instead of Generic[KT, VT]. +class ItemsView(MappingView, Generic[KT, VT], extra=collections_abc.ItemsView): + pass -class Iterator(Iterable[T], _Protocol[T]): - @abstractmethod - def __next__(self) -> T: pass +class ValuesView(MappingView, extra=collections_abc.ValuesView): + pass -class Sequence(Sized, Iterable[T], Container[T], Generic[T]): - @abstractmethod - def __getitem__(self, x): pass - - @abstractmethod - def __reversed__(self, s: slice) -> Iterator[T]: pass - @abstractmethod - def index(self, x) -> int: pass +class _DictMeta(GenericMeta): - @abstractmethod - def count(self, x) -> int: pass + def __instancecheck__(self, obj): + if not super().__instancecheck__(obj): + return False + keytype, valuetype = self.__parameters__ + for key, value in obj.items(): + if not (isinstance(key, keytype) and + isinstance(value, valuetype)): + return False + return True -for t in list, tuple, str, bytes, range: - Sequence.register(t) +class Dict(dict, MutableMapping, metaclass=_DictMeta): + pass -class AbstractSet(Sized, Iterable[T], Generic[T]): - @abstractmethod - def __contains__(self, x: object) -> bool: pass - @abstractmethod - def __and__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass - @abstractmethod - def __or__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass - @abstractmethod - def __sub__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass - @abstractmethod - def __xor__(self, s: 'AbstractSet[T]') -> 'AbstractSet[T]': pass - @abstractmethod - def isdisjoint(self, s: 'AbstractSet[T]') -> bool: pass - +def NamedTuple(typename, fields): + """Typed version of namedtuple. -for t in set, frozenset, type({}.keys()), type({}.items()): - AbstractSet.register(t) + Usage:: + Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)]) -class Mapping(Sized, Iterable[KT], Generic[KT, VT]): - @abstractmethod - def __getitem__(self, k: KT) -> VT: pass - @abstractmethod - def __setitem__(self, k: KT, v: VT) -> None: pass - @abstractmethod - def __delitem__(self, v: KT) -> None: pass - @abstractmethod - def __contains__(self, o: object) -> bool: pass + This is equivalent to:: - @abstractmethod - def clear(self) -> None: pass - @abstractmethod - def copy(self) -> 'Mapping[KT, VT]': pass - @abstractmethod - def get(self, k): pass - @abstractmethod - def pop(self, k, default=None): pass - @abstractmethod - def popitem(self) -> Tuple[KT, VT]: pass - @abstractmethod - def setdefault(self, k, default=None): pass - @abstractmethod - def update(self, m: Union['Mapping[KT, VT]', Iterable[Tuple[KT, VT]]]) -> None: pass - @abstractmethod - def keys(self) -> AbstractSet[KT]: pass - @abstractmethod - def values(self) -> AbstractSet[VT]: pass - @abstractmethod - def items(self) -> AbstractSet[Tuple[KT, VT]]: pass + Employee = collections.namedtuple('Employee', ['name', 'id']) + The resulting class has one extra attribute: _field_types, + giving a dict mapping field names to types. (The field names + are in the _fields attribute, which is part of the namedtuple + API.) + """ + fields = [(n, t) for n, t in fields] + cls = collections.namedtuple(typename, [n for n, t in fields]) + cls._field_types = dict(fields) + return cls -# TODO Consider more types: os.environ, etc. However, these add dependencies. -Mapping.register(dict) +class IO(Generic[AnyStr]): + """Generic base class for TextIO and BinaryIO. -# Note that the BinaryIO and TextIO classes must be in sync with typing module -# stubs. + This is an abstract, generic version of the return of open(). + NOTE: This does not distinguish between the different possible + classes (text vs. binary, read vs. write vs. read/write, + append-only, unbuffered). The TextIO and BinaryIO subclasses + below capture the distinctions between text vs. binary, which is + pervasive in the interface; however we currently do not offer a + way to track the other distinctions in the type system. + """ -class IO(Generic[AnyStr]): @abstractproperty - def mode(self) -> str: pass + def mode(self) -> str: + pass + @abstractproperty - def name(self) -> str: pass + def name(self) -> str: + pass + @abstractmethod - def close(self) -> None: pass + def close(self) -> None: + pass + @abstractmethod - def closed(self) -> bool: pass + def closed(self) -> bool: + pass + @abstractmethod - def fileno(self) -> int: pass + def fileno(self) -> int: + pass + @abstractmethod - def flush(self) -> None: pass + def flush(self) -> None: + pass + @abstractmethod - def isatty(self) -> bool: pass + def isatty(self) -> bool: + pass + @abstractmethod - def read(self, n: int = -1) -> AnyStr: pass + def read(self, n: int = -1) -> AnyStr: + pass + @abstractmethod - def readable(self) -> bool: pass + def readable(self) -> bool: + pass + @abstractmethod - def readline(self, limit: int = -1) -> AnyStr: pass + def readline(self, limit: int = -1) -> AnyStr: + pass + @abstractmethod - def readlines(self, hint: int = -1) -> List[AnyStr]: pass + def readlines(self, hint: int = -1) -> List[AnyStr]: + pass + @abstractmethod - def seek(self, offset: int, whence: int = 0) -> int: pass + def seek(self, offset: int, whence: int = 0) -> int: + pass + @abstractmethod - def seekable(self) -> bool: pass + def seekable(self) -> bool: + pass + @abstractmethod - def tell(self) -> int: pass + def tell(self) -> int: + pass + @abstractmethod - def truncate(self, size: int = None) -> int: pass + def truncate(self, size: int = None) -> int: + pass + @abstractmethod - def writable(self) -> bool: pass + def writable(self) -> bool: + pass + @abstractmethod - def write(self, s: AnyStr) -> int: pass + def write(self, s: AnyStr) -> int: + pass + @abstractmethod - def writelines(self, lines: List[AnyStr]) -> None: pass + def writelines(self, lines: List[AnyStr]) -> None: + pass @abstractmethod - def __enter__(self) -> 'IO[AnyStr]': pass + def __enter__(self) -> 'IO[AnyStr]': + pass + @abstractmethod - def __exit__(self, type, value, traceback) -> None: pass + def __exit__(self, type, value, traceback) -> None: + pass class BinaryIO(IO[bytes]): + """Typed version of the return of open() in binary mode.""" + @abstractmethod - def write(self, s: Union[bytes, bytearray]) -> int: pass + def write(self, s: Union[bytes, bytearray]) -> int: + pass + @abstractmethod - def __enter__(self) -> 'BinaryIO': pass + def __enter__(self) -> 'BinaryIO': + pass class TextIO(IO[str]): + """Typed version of the return of open() in text mode.""" + @abstractproperty - def buffer(self) -> BinaryIO: pass + def buffer(self) -> BinaryIO: + pass + @abstractproperty - def encoding(self) -> str: pass + def encoding(self) -> str: + pass + @abstractproperty - def errors(self) -> str: pass + def errors(self) -> str: + pass + @abstractproperty - def line_buffering(self) -> bool: pass + def line_buffering(self) -> bool: + pass + @abstractproperty - def newlines(self) -> Any: pass + def newlines(self) -> Any: + pass + @abstractmethod - def __enter__(self) -> 'TextIO': pass + def __enter__(self) -> 'TextIO': + pass + + +class io: + """Wrapper namespace for IO generic classes.""" + + __all__ = ['IO', 'TextIO', 'BinaryIO'] + IO = IO + TextIO = TextIO + BinaryIO = BinaryIO + +io.__name__ = __name__ + '.io' +sys.modules[io.__name__] = io + + +Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')), + lambda p: p.pattern) +Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')), + lambda m: m.re.pattern) -# TODO Register IO/TextIO/BinaryIO as the base class of file-like types. +class re: + """Wrapper namespace for re type aliases.""" + __all__ = ['Pattern', 'Match'] + Pattern = Pattern + Match = Match -del t +re.__name__ = __name__ + '.re' +sys.modules[re.__name__] = re diff --git a/mypy/build.py b/mypy/build.py index 1f050432940b..0271be9a4523 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -357,7 +357,7 @@ def process(self, initial_state: 'UnprocessedFile') -> BuildResult: # Collect a list of all files. trees = [] # type: List[MypyFile] for state in self.states: - trees.append((cast('ParsedFile', state)).tree) + trees.append(cast(ParsedFile, state).tree) # Perform any additional passes after type checking for all the files. self.final_passes(trees, self.type_checker.type_map) diff --git a/mypy/nodes.py b/mypy/nodes.py index 86a2f90eb7dd..a7d70b013d49 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -862,7 +862,7 @@ def __init__(self, name: str) -> None: self.literal_hash = ('Var', name,) def type_node(self): - return cast('TypeInfo', self.node) + return cast(TypeInfo, self.node) def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_name_expr(self) diff --git a/mypy/semanal.py b/mypy/semanal.py index 1cfa13f6fccf..da998830a2ba 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -894,7 +894,7 @@ def analyse_lvalue(self, lval: Node, nested: bool = False, lval.accept(self) elif (isinstance(lval, TupleExpr) or isinstance(lval, ListExpr)): - items = (Any(lval)).items + items = cast(Any, lval).items if len(items) == 0 and isinstance(lval, TupleExpr): self.fail("Can't assign to ()", lval) self.analyse_tuple_or_list_lvalue(cast(Union[ListExpr, TupleExpr], lval), diff --git a/mypy/stats.py b/mypy/stats.py index 4fec629c0cf6..38ce9a3b9147 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -93,7 +93,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: else: items = [lvalue] for item in items: - if hasattr(item, 'is_def') and Any(item).is_def: + if hasattr(item, 'is_def') and cast(Any, item).is_def: t = self.typemap.get(item) if t: self.type(t) diff --git a/mypy/test/data/pythoneval.test b/mypy/test/data/pythoneval.test index 7b626c3465b1..362e5a0fd601 100644 --- a/mypy/test/data/pythoneval.test +++ b/mypy/test/data/pythoneval.test @@ -18,7 +18,8 @@ from typing import Sized, Sequence, Iterator, Iterable, Mapping, AbstractSet def check(o, t): rep = re.sub('0x[0-9a-f]+', '0x...', repr(o)) rep = rep.replace('sequenceiterator', 'str_iterator') - print(rep, t, isinstance(o, t)) + trep = str(t).replace('_abcoll.Sized', 'collections.abc.Sized') + print(rep, trep, isinstance(o, t)) def f(): check('x', Sized) @@ -31,13 +32,13 @@ def f(): f() [out] -'x' True -[1] True -{1: 3} False - True -'x' True -{} True -{1} True +'x' True +[1] typing.Sequence[~T] True +{1: 3} typing.Sequence[~T] False + typing.Iterator[~T] True +'x' typing.Iterable[~T] True +{} typing.Mapping[~KT, ~VT] True +{1} typing.AbstractSet[~T] True [case testSized] from typing import Sized @@ -184,7 +185,7 @@ import math import typing print(math.__name__) print(type(math.__dict__)) -print(type(typing.__doc__)) +print(type(math.__doc__ or '')) print(math.__class__) [out] math @@ -328,8 +329,8 @@ print(A().f) 3 [case testIsinstanceWithTuple] -from typing import Any -x = Any((1, 'x')) +from typing import cast, Any +x = cast(Any, (1, 'x')) if isinstance(x, tuple): print(x[0], x[1]) [out] @@ -364,9 +365,9 @@ b'zar' [case testNameNotImportedFromTyping] import typing -Any(1) +cast(int, 2) [out] -_program.py, line 2: Name 'Any' is not defined +_program.py, line 2: Name 'cast' is not defined [case testBinaryIOType] from typing import BinaryIO diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 4772838b935f..ce5c497ca3bd 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -30,7 +30,7 @@ python_34_eval_files = ['pythoneval-asyncio.test'] # Path to Python 3 interpreter -python3_path = 'python3' +python3_path = sys.executable default_python2_interpreter = 'python' diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 21c38769be0e..7b95fa3e6156 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1,4 +1,5 @@ import glob +import importlib import os.path import random import shutil @@ -110,10 +111,11 @@ def test_stubgen(testcase): with open(path, 'w') as file: file.write(source) file.close() + # Without this we may sometimes be unable to import the module below, as importlib + # caches os.listdir() results in Python 3.3+ (Guido explained this to me). + reset_importlib_caches() try: if testcase.name.endswith('_import'): - # For some reason this sleep fixes random test failures. - time.sleep(0.01) generate_stub_for_module(name, out_dir, quiet=True) else: generate_stub(path, out_dir) @@ -128,6 +130,13 @@ def test_stubgen(testcase): os.remove(path) +def reset_importlib_caches(): + try: + importlib.invalidate_caches() + except (ImportError, AttributeError): + pass + + def load_output(dirname): result = [] entries = glob.glob('%s/*' % dirname) diff --git a/mypy/types.py b/mypy/types.py index 6e1e82c80bb1..30c8db38ae61 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -585,7 +585,7 @@ def visit_callable_type(self, t: CallableType) -> Type: def visit_tuple_type(self, t: TupleType) -> Type: return TupleType(self.translate_types(t.items), - Any(t.fallback.accept(self)), + cast(Any, t.fallback.accept(self)), t.line, t.repr) def visit_star_type(self, t: StarType) -> Type: diff --git a/stubs/3.2/importlib.py b/stubs/3.2/importlib.py index d2d920dfba79..7230da2d52c9 100644 --- a/stubs/3.2/importlib.py +++ b/stubs/3.2/importlib.py @@ -6,3 +6,4 @@ # TODO more precise type? def import_module(name: str, package: str = None) -> Any: pass +def invalidate_caches() -> None: pass diff --git a/travis.sh b/travis.sh index 02bcf114029f..6c4e920af29f 100755 --- a/travis.sh +++ b/travis.sh @@ -45,21 +45,15 @@ echo "$PYTHON" "$DRIVER" $STUBTEST || fail rm $STUBTEST -# Sample checks +# Checks sample code -# Only run under 3.2 - -if [ "`"$PYTHON" -c 'from sys import version_info as vi; print(vi.major, vi.minor)'`" == "3 2" ]; then - echo Type checking lib-python... - echo - cd lib-python/3.2 - for f in test/test_*.py; do - mod=test.`basename "$f" .py` - echo $mod - "$PYTHON" "$DRIVER" -m $mod || fail - done -else - echo "Skipping lib-python type checks (not Python 3.2!)" -fi +echo Type checking lib-python... +echo +cd lib-python/3.2 +for f in test/test_*.py; do + mod=test.`basename "$f" .py` + echo $mod + "$PYTHON" "$DRIVER" -m $mod || fail +done exit $result