diff --git a/.travis.yml b/.travis.yml index 0dfe9f5e..0c5b465b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,12 @@ language: python python: - "nightly" - "3.7-dev" - - "3.6" - - "3.5" + - "3.6.1" + - "3.6.0" + - "3.5.3" + - "3.5.2" + - "3.5.1" + - "3.5.0" - "3.4" - "3.3" - "2.7" @@ -14,5 +18,8 @@ install: script: - export PYTHONPATH=`python -c "import sys; print('python2' if sys.version.startswith('2') else 'src')"`; py.test $PYTHONPATH + - if [[ $TRAVIS_PYTHON_VERSION < '3.5' ]]; then python setup.py install; fi + - export PYTHONPATH=`python -c "import sys; print('typing_extensions/src_py2' if sys.version.startswith('2') else 'typing_extensions/src_py3')"`; + py.test $PYTHONPATH; - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8; fi - - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py typing_extensions/src_py2/test_typing_extensions.py typing_extensions/src_py3/test_typing_extensions.py; fi diff --git a/test-requirements.txt b/test-requirements.txt index f4566b45..9524d619 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,3 +4,4 @@ flake8-pyi; python_version >= '3.6' pytest>=2.8; python_version >= '3.3' pytest-xdist>=1.13; python_version >= '3.3' pytest-cov>=2.4.0; python_version >= '3.3' +typing>=3.6.1; python_version <= '3.4' diff --git a/tox.ini b/tox.ini index 90c4f844..5f92822c 100644 --- a/tox.ini +++ b/tox.ini @@ -22,4 +22,6 @@ exclude = # tests have more relaxed formatting rules # and its own specific config in .flake8-tests python2/test_typing.py, - src/test_typing.py + src/test_typing.py, + typing_extensions/src_py2/test_typing_extensions.py, + typing_extensions/src_py3/test_typing_extensions.py, diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst new file mode 100644 index 00000000..9090402e --- /dev/null +++ b/typing_extensions/README.rst @@ -0,0 +1,83 @@ +================= +Typing Extensions +================= + +.. image:: https://badges.gitter.im/python/typing.svg + :alt: Chat at https://gitter.im/python/typing + :target: https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge + +Overview +======== + +The ``typing`` module was added to the standard library in Python 3.5 on +a provisional basis and will no longer be provisional in Python 3.7. However, +this means users of Python 3.5 - 3.6 who are unable to upgrade will not be +able to take advantage of new types added to the ``typing`` module, such as +``typing.Text`` or ``typing.Coroutine``. + +The ``typing_extensions`` module contains both backports of these changes +as well as experimental types that will eventually be added to the ``typing`` +module. + +Users of other Python versions should continue to install and use +use the ``typing`` module from PyPi instead of using this one unless +specifically writing code that must be compatible with multiple Python +versions or requires experimental types. + +Included items +============== + +This module currently contains the following: + +All Python versions: +-------------------- + +- ``ClassVar`` +- ``ContextManager`` +- ``Counter`` +- ``DefaultDict`` +- ``Deque`` +- ``NewType`` +- ``NoReturn`` +- ``overload`` (note that older versions of ``typing`` only let you use ``overload`` in stubs) +- ``Text`` +- ``Type`` +- ``TYPE_CHECKING`` + +Python 3.3+ only: +----------------- + +- ``ChainMap`` + +Python 3.5+ only: +----------------- + +- ``AsyncIterable`` +- ``AsyncIterator`` +- ``AsyncContextManager`` +- ``Awaitable`` +- ``Coroutine`` + +Python 3.6+ only: +----------------- + +- ``AsyncGenerator`` + +Other Notes and Limitations +=========================== + +There are a few types whose interface was modified between different +versions of typing. For example, ``typing.Sequence`` was modified to +subclass ``typing.Reversible`` as of Python 3.5.3. + +These changes are _not_ backported to prevent subtle compatibility +issues when mixing the differing implementations of modified classes. + +Running tests +============= + +To run tests, navigate into the appropriate source directory and run +``test_typing_extensions.py``. You will also need to install the latest +version of ``typing`` if you are using a version of Python that does not +include ``typing`` as a part of the standard library. + diff --git a/typing_extensions/setup.py b/typing_extensions/setup.py new file mode 100644 index 00000000..2e9f1dbd --- /dev/null +++ b/typing_extensions/setup.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# coding: utf-8 + +import sys +from distutils.core import setup + +if sys.version_info < (2, 7, 0) or (3, 0, 0) <= sys.version_info < (3, 3, 0): + sys.stderr.write('ERROR: You need Python 2.7 or 3.3+ ' + 'to install the typing package.\n') + exit(1) + +version = '3.6.1' +description = 'Backported and Experimental Type Hints for Python 3.5+' +long_description = '''\ +Typing Extensions -- Backported and Experimental Type Hints for Python + +The ``typing`` module was added to the standard library in Python 3.5 on +a provisional basis and will no longer be provisional in Python 3.7. However, +this means users of Python 3.5 - 3.6 who are unable to upgrade will not be +able to take advantage of new types added to the ``typing`` module, such as +``typing.Text`` or ``typing.Coroutine``. + +The ``typing_extensions`` module contains both backports of these changes +as well as experimental types that will eventually be added to the ``typing`` +module. + +Users of other Python versions should continue to install and use +use the ``typing`` module from PyPi instead of using this one unless +specifically writing code that must be compatible with multiple Python +versions or requires experimental types. +''' + +classifiers = [ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Python Software Foundation License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Software Development', +] + +if sys.version_info.major == 2: + package_dir = 'src_py2' +elif sys.version_info.major == 3: + package_dir = 'src_py3' +else: + raise AssertionError() + +install_requires = [] +if sys.version_info < (3, 5): + install_requires.append('typing >= 3.6.1') + +setup(name='typing_extensions', + version=version, + description=description, + long_description=long_description, + author='Guido van Rossum, Jukka Lehtosalo, Lukasz Langa, Michael Lee', + author_email='jukka.lehtosalo@iki.fi', + url='https://github.com/python/typing', + license='PSF', + keywords='typing function annotations type hints hinting checking ' + 'checker typehints typehinting typechecking backport', + package_dir={'': package_dir}, + py_modules=['typing_extensions'], + classifiers=classifiers, + install_requires=install_requires) diff --git a/typing_extensions/src_py2/test_typing_extensions.py b/typing_extensions/src_py2/test_typing_extensions.py new file mode 100644 index 00000000..1597d47f --- /dev/null +++ b/typing_extensions/src_py2/test_typing_extensions.py @@ -0,0 +1,238 @@ +import sys +import os +import abc +import contextlib +import collections +from unittest import TestCase, main, skipUnless + +from typing_extensions import NoReturn, ClassVar +from typing_extensions import ContextManager, Counter, Deque, DefaultDict +from typing_extensions import NewType, overload +import typing +import typing_extensions + + +T = typing.TypeVar('T') +KT = typing.TypeVar('KT') +VT = typing.TypeVar('VT') + + +class BaseTestCase(TestCase): + + def assertIsSubclass(self, cls, class_or_tuple, msg=None): + if not issubclass(cls, class_or_tuple): + message = '%r is not a subclass of %r' % (cls, class_or_tuple) + if msg is not None: + message += ' : %s' % msg + raise self.failureException(message) + + def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): + if issubclass(cls, class_or_tuple): + message = '%r is a subclass of %r' % (cls, class_or_tuple) + if msg is not None: + message += ' : %s' % msg + raise self.failureException(message) + + +class Employee(object): + pass + + +class NoReturnTests(BaseTestCase): + + def test_noreturn_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, NoReturn) + + def test_noreturn_subclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(Employee, NoReturn) + with self.assertRaises(TypeError): + issubclass(NoReturn, Employee) + + def test_repr(self): + if hasattr(typing, 'NoReturn'): + self.assertEqual(repr(NoReturn), 'typing.NoReturn') + else: + self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') + + def test_not_generic(self): + with self.assertRaises(TypeError): + NoReturn[int] + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class A(NoReturn): + pass + with self.assertRaises(TypeError): + class A(type(NoReturn)): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + NoReturn() + with self.assertRaises(TypeError): + type(NoReturn)() + + +class ClassVarTests(BaseTestCase): + + def test_basics(self): + with self.assertRaises(TypeError): + ClassVar[1] + with self.assertRaises(TypeError): + ClassVar[int, str] + with self.assertRaises(TypeError): + ClassVar[int][str] + + def test_repr(self): + self.assertEqual(repr(ClassVar), 'typing.ClassVar') + cv = ClassVar[int] + self.assertEqual(repr(cv), 'typing.ClassVar[int]') + cv = ClassVar[Employee] + self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(type(ClassVar)): + pass + with self.assertRaises(TypeError): + class C(type(ClassVar[int])): + pass + + def test_cannot_init(self): + with self.assertRaises(TypeError): + ClassVar() + with self.assertRaises(TypeError): + type(ClassVar)() + with self.assertRaises(TypeError): + type(ClassVar[typing.Optional[int]])() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(1, ClassVar[int]) + with self.assertRaises(TypeError): + issubclass(int, ClassVar) + + +class CollectionsAbcTests(BaseTestCase): + + def test_contextmanager(self): + @contextlib.contextmanager + def manager(): + yield 42 + + cm = manager() + self.assertIsInstance(cm, ContextManager) + self.assertNotIsInstance(42, ContextManager) + + with self.assertRaises(TypeError): + isinstance(42, ContextManager[int]) + with self.assertRaises(TypeError): + isinstance(cm, ContextManager[int]) + with self.assertRaises(TypeError): + issubclass(type(cm), ContextManager[int]) + + def test_counter(self): + self.assertIsSubclass(collections.Counter, Counter) + self.assertIs(type(Counter()), collections.Counter) + self.assertIs(type(Counter[T]()), collections.Counter) + self.assertIs(type(Counter[int]()), collections.Counter) + + class A(Counter[int]): pass + class B(Counter[T]): pass + + self.assertIsInstance(A(), collections.Counter) + self.assertIs(type(B[int]()), B) + self.assertEqual(B.__bases__, (typing_extensions.Counter,)) + + def test_deque(self): + self.assertIsSubclass(collections.deque, Deque) + self.assertIs(type(Deque()), collections.deque) + self.assertIs(type(Deque[T]()), collections.deque) + self.assertIs(type(Deque[int]()), collections.deque) + + class A(Deque[int]): pass + class B(Deque[T]): pass + + self.assertIsInstance(A(), collections.deque) + self.assertIs(type(B[int]()), B) + + def test_defaultdict_instantiation(self): + self.assertIsSubclass(collections.defaultdict, DefaultDict) + self.assertIs(type(DefaultDict()), collections.defaultdict) + self.assertIs(type(DefaultDict[KT, VT]()), collections.defaultdict) + self.assertIs(type(DefaultDict[str, int]()), collections.defaultdict) + + class A(DefaultDict[str, int]): pass + class B(DefaultDict[KT, VT]): pass + + self.assertIsInstance(A(), collections.defaultdict) + self.assertIs(type(B[str, int]()), B) + + +class NewTypeTests(BaseTestCase): + + def test_basic(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + self.assertIsInstance(UserId(5), int) + self.assertIsInstance(UserName('Joe'), type('Joe')) + self.assertEqual(UserId(5) + 1, 6) + + def test_errors(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + with self.assertRaises(TypeError): + issubclass(UserId, int) + with self.assertRaises(TypeError): + class D(UserName): + pass + + +class OverloadTests(BaseTestCase): + + def test_overload_fails(self): + with self.assertRaises(RuntimeError): + @overload + def blah(): + pass + + blah() + + def test_overload_succeeds(self): + @overload + def blah(): + pass + + def blah(): + pass + + blah() + + +class AllTests(BaseTestCase): + + def test_typing_extensions_includes_standard(self): + a = typing_extensions.__all__ + self.assertIn('ClassVar', a) + self.assertIn('Type', a) + self.assertIn('Counter', a) + self.assertIn('DefaultDict', a) + self.assertIn('Deque', a) + self.assertIn('NewType', a) + self.assertIn('overload', a) + self.assertIn('Text', a) + self.assertIn('TYPE_CHECKING', a) + + def test_typing_extensions_defers_when_possible(self): + exclude = {'overload', 'Text', 'TYPE_CHECKING'} + for item in typing_extensions.__all__: + if item not in exclude and hasattr(typing, item): + self.assertIs( + getattr(typing_extensions, item), + getattr(typing, item)) + + +if __name__ == '__main__': + main() diff --git a/typing_extensions/src_py2/typing_extensions.py b/typing_extensions/src_py2/typing_extensions.py new file mode 100644 index 00000000..29858ca5 --- /dev/null +++ b/typing_extensions/src_py2/typing_extensions.py @@ -0,0 +1,91 @@ +import abc +import collections +import typing +from typing import ( + ClassVar, Type, + Counter, DefaultDict, Deque, + NewType, overload, Text, TYPE_CHECKING, +) + +# Please keep __all__ alphabetized within each category. +__all__ = [ + # Super-special typing primitives. + 'ClassVar', + 'Type', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + + # One-off things. + 'NewType', + 'overload', + 'Text', + 'TYPE_CHECKING', +] + + +if hasattr(typing, 'NoReturn'): + NoReturn = typing.NoReturn +else: + # TODO: Remove once typing.py has been updated + class NoReturnMeta(typing.TypingMeta): + """Metaclass for NoReturn.""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(NoReturnMeta, cls).__new__(cls, name, bases, namespace) + return self + + class _NoReturn(typing._FinalTypingBase): + """Special type indicating functions that never return. + Example:: + from typing import NoReturn + def stop() -> NoReturn: + raise Exception('no way') + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + __metaclass__ = NoReturnMeta + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + NoReturn = _NoReturn(_root=True) + + +T_co = typing.TypeVar('T_co', covariant=True) + +if hasattr(typing, 'ContextManager'): + ContextManager = typing.ContextManager +else: + # TODO: Remove once typing.py has been updated + class ContextManager(typing.Generic[T_co]): + __slots__ = () + + def __enter__(self): + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is ContextManager: + # In Python 3.6+, it is possible to set a method to None to + # explicitly indicate that the class does not implement an ABC + # (https://bugs.python.org/issue25958), but we do not support + # that pattern here because this fallback class is only used + # in Python 3.5 and earlier. + if (any("__enter__" in B.__dict__ for B in C.__mro__) and + any("__exit__" in B.__dict__ for B in C.__mro__)): + return True + return NotImplemented + diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py new file mode 100644 index 00000000..4e6f724f --- /dev/null +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -0,0 +1,632 @@ +import sys +import os +import abc +import contextlib +import collections +from unittest import TestCase, main, skipUnless +from typing import TypeVar, Optional +from typing import T, KT, VT # Not in __all__. +from typing import Tuple, List +from typing import Generic +from typing import get_type_hints +from typing import no_type_check +from typing_extensions import NoReturn, ClassVar, Type, NewType +import typing +import typing_extensions +import collections.abc as collections_abc + +# We assume Python versions *below* 3.5.0 will have the most +# up-to-date version of the typing module installed. Since +# the typing module isn't a part of the standard library in older +# versions of Python, those users are likely to have a reasonably +# modern version of `typing` installed from PyPi. +TYPING_LATEST = sys.version_info[:3] < (3, 5, 0) + +# Flags used to mark tests that only apply after a specific +# version of the typing module. +TYPING_3_5_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 1) +TYPING_3_5_3 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 3) +TYPING_3_6_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 6, 1) + +# For typing versions where issubclass(...) and +# isinstance(...) checks are forbidden. +# +# See https://github.com/python/typing/issues/136 +# and https://github.com/python/typing/pull/283 +SUBCLASS_CHECK_FORBIDDEN = TYPING_3_5_3 + +# For typing versions where instantiating collection +# types are allowed. +# +# See https://github.com/python/typing/issues/367 +CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1 + +# For Python versions supporting async/await and friends. +ASYNCIO = sys.version_info[:2] >= (3, 5) + +# For checks reliant on Python 3.6 syntax changes (e.g. classvar) +PY36 = sys.version_info[:2] >= (3, 6) + + +class BaseTestCase(TestCase): + def assertIsSubclass(self, cls, class_or_tuple, msg=None): + if not issubclass(cls, class_or_tuple): + message = '%r is not a subclass of %r' % (cls, class_or_tuple) + if msg is not None: + message += ' : %s' % msg + raise self.failureException(message) + + def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): + if issubclass(cls, class_or_tuple): + message = '%r is a subclass of %r' % (cls, class_or_tuple) + if msg is not None: + message += ' : %s' % msg + raise self.failureException(message) + + +class Employee: + pass + + +class NoReturnTests(BaseTestCase): + + def test_noreturn_instance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, NoReturn) + + def test_noreturn_subclass_type_error_1(self): + with self.assertRaises(TypeError): + issubclass(Employee, NoReturn) + + @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") + def test_noreturn_subclass_type_error_2(self): + with self.assertRaises(TypeError): + issubclass(NoReturn, Employee) + + def test_repr(self): + if hasattr(typing, 'NoReturn'): + self.assertEqual(repr(NoReturn), 'typing.NoReturn') + else: + self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') + + def test_not_generic(self): + with self.assertRaises(TypeError): + NoReturn[int] + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class A(NoReturn): + pass + if SUBCLASS_CHECK_FORBIDDEN: + with self.assertRaises(TypeError): + class A(type(NoReturn)): + pass + + def test_cannot_instantiate(self): + with self.assertRaises(TypeError): + NoReturn() + with self.assertRaises(TypeError): + type(NoReturn)() + + +class ClassVarTests(BaseTestCase): + + def test_basics(self): + with self.assertRaises(TypeError): + ClassVar[1] + with self.assertRaises(TypeError): + ClassVar[int, str] + with self.assertRaises(TypeError): + ClassVar[int][str] + + def test_repr(self): + if hasattr(typing, 'ClassVar'): + mod_name = 'typing' + else: + mod_name = 'typing_extensions' + self.assertEqual(repr(ClassVar), mod_name + '.ClassVar') + cv = ClassVar[int] + self.assertEqual(repr(cv), mod_name + '.ClassVar[int]') + cv = ClassVar[Employee] + self.assertEqual(repr(cv), mod_name + '.ClassVar[%s.Employee]' % __name__) + + @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(type(ClassVar)): + pass + with self.assertRaises(TypeError): + class C(type(ClassVar[int])): + pass + + def test_cannot_init(self): + with self.assertRaises(TypeError): + ClassVar() + with self.assertRaises(TypeError): + type(ClassVar)() + with self.assertRaises(TypeError): + type(ClassVar[Optional[int]])() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(1, ClassVar[int]) + with self.assertRaises(TypeError): + issubclass(int, ClassVar) + + +class OverloadTests(BaseTestCase): + + def test_overload_fails(self): + from typing_extensions import overload + + with self.assertRaises(RuntimeError): + + @overload + def blah(): + pass + + blah() + + def test_overload_succeeds(self): + from typing_extensions import overload + + @overload + def blah(): + pass + + def blah(): + pass + + blah() + + +ASYNCIO_TESTS = """ +import asyncio +from typing import Iterable +from typing_extensions import Awaitable, AsyncIterator + +T_a = TypeVar('T_a') + +class AwaitableWrapper(Awaitable[T_a]): + + def __init__(self, value): + self.value = value + + def __await__(self) -> typing.Iterator[T_a]: + yield + return self.value + +class AsyncIteratorWrapper(AsyncIterator[T_a]): + + def __init__(self, value: Iterable[T_a]): + self.value = value + + def __aiter__(self) -> AsyncIterator[T_a]: + return self + + @asyncio.coroutine + def __anext__(self) -> T_a: + data = yield from self.value + if data: + return data + else: + raise StopAsyncIteration + +class ACM: + async def __aenter__(self) -> int: + return 42 + async def __aexit__(self, etype, eval, tb): + return None +""" + +if ASYNCIO: + try: + exec(ASYNCIO_TESTS) + except ImportError: + ASYNCIO = False +else: + # fake names for the sake of static analysis + asyncio = None + AwaitableWrapper = AsyncIteratorWrapper = ACM = object + +PY36_TESTS = """ +from test import ann_module, ann_module2, ann_module3 +from typing_extensions import AsyncContextManager +from typing import NamedTuple + +class A: + y: float +class B(A): + x: ClassVar[Optional['B']] = None + y: int + b: int +class CSub(B): + z: ClassVar['CSub'] = B() +class G(Generic[T]): + lst: ClassVar[List[T]] = [] + +class NoneAndForward: + parent: 'NoneAndForward' + meaning: None + +class XRepr(NamedTuple): + x: int + y: int = 1 + def __str__(self): + return f'{self.x} -> {self.y}' + def __add__(self, other): + return 0 + +async def g_with(am: AsyncContextManager[int]): + x: int + async with am as x: + return x + +try: + g_with(ACM()).send(None) +except StopIteration as e: + assert e.args[0] == 42 +""" + +if PY36: + exec(PY36_TESTS) +else: + # fake names for the sake of static analysis + ann_module = ann_module2 = ann_module3 = None + A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object + XMeth = XRepr = NoneAndForward = object + +gth = get_type_hints + + +class GetTypeHintTests(BaseTestCase): + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_modules(self): + ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} + self.assertEqual(gth(ann_module), ann_module_type_hints) + self.assertEqual(gth(ann_module2), {}) + self.assertEqual(gth(ann_module3), {}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_classes(self): + self.assertEqual(gth(ann_module.C, ann_module.__dict__), + {'y': Optional[ann_module.C]}) + self.assertIsInstance(gth(ann_module.j_class), dict) + self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type}) + self.assertEqual(gth(ann_module.D), + {'j': str, 'k': str, 'y': Optional[ann_module.C]}) + self.assertEqual(gth(ann_module.Y), {'z': int}) + self.assertEqual(gth(ann_module.h_class), + {'y': Optional[ann_module.C]}) + self.assertEqual(gth(ann_module.S), {'x': str, 'y': str}) + self.assertEqual(gth(ann_module.foo), {'x': int}) + self.assertEqual(gth(NoneAndForward, globals()), + {'parent': NoneAndForward, 'meaning': type(None)}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_respect_no_type_check(self): + @no_type_check + class NoTpCheck: + class Inn: + def __init__(self, x: 'not a type'): ... + self.assertTrue(NoTpCheck.__no_type_check__) + self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__) + self.assertEqual(gth(ann_module2.NTC.meth), {}) + class ABase(Generic[T]): + def meth(x: int): ... + @no_type_check + class Der(ABase): ... + self.assertEqual(gth(ABase.meth), {'x': int}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_ClassVar(self): + self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), + {'var': ClassVar[ann_module2.CV]}) + self.assertEqual(gth(B, globals()), + {'y': int, 'x': ClassVar[Optional[B]], 'b': int}) + self.assertEqual(gth(CSub, globals()), + {'z': ClassVar[CSub], 'y': int, 'b': int, + 'x': ClassVar[Optional[B]]}) + self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) + + +class CollectionsAbcTests(BaseTestCase): + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_awaitable(self): + ns = {} + exec( + "async def foo() -> typing_extensions.Awaitable[int]:\n" + " return await AwaitableWrapper(42)\n", + globals(), ns) + foo = ns['foo'] + g = foo() + self.assertIsInstance(g, typing_extensions.Awaitable) + self.assertNotIsInstance(foo, typing_extensions.Awaitable) + g.send(None) # Run foo() till completion, to avoid warning. + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_coroutine(self): + ns = {} + exec( + "async def foo():\n" + " return\n", + globals(), ns) + foo = ns['foo'] + g = foo() + self.assertIsInstance(g, typing_extensions.Coroutine) + with self.assertRaises(TypeError): + isinstance(g, typing_extensions.Coroutine[int]) + self.assertNotIsInstance(foo, typing_extensions.Coroutine) + try: + g.send(None) + except StopIteration: + pass + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_async_iterable(self): + base_it = range(10) # type: Iterator[int] + it = AsyncIteratorWrapper(base_it) + self.assertIsInstance(it, typing_extensions.AsyncIterable) + self.assertIsInstance(it, typing_extensions.AsyncIterable) + self.assertNotIsInstance(42, typing_extensions.AsyncIterable) + + @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') + def test_async_iterator(self): + base_it = range(10) # type: Iterator[int] + it = AsyncIteratorWrapper(base_it) + if TYPING_3_5_1: + self.assertIsInstance(it, typing_extensions.AsyncIterator) + self.assertNotIsInstance(42, typing_extensions.AsyncIterator) + + def test_deque(self): + self.assertIsSubclass(collections.deque, typing_extensions.Deque) + class MyDeque(typing_extensions.Deque[int]): ... + self.assertIsInstance(MyDeque(), collections.deque) + + def test_counter(self): + self.assertIsSubclass(collections.Counter, typing_extensions.Counter) + + @skipUnless(CAN_INSTANTIATE_COLLECTIONS, "Behavior added in typing 3.6.1") + def test_defaultdict_instantiation(self): + self.assertIs( + type(typing_extensions.DefaultDict()), + collections.defaultdict) + self.assertIs( + type(typing_extensions.DefaultDict[KT, VT]()), + collections.defaultdict) + self.assertIs( + type(typing_extensions.DefaultDict[str, int]()), + collections.defaultdict) + + def test_defaultdict_subclass(self): + + class MyDefDict(typing_extensions.DefaultDict[str, int]): + pass + + dd = MyDefDict() + self.assertIsInstance(dd, MyDefDict) + + self.assertIsSubclass(MyDefDict, collections.defaultdict) + if TYPING_3_5_3: + self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + + def test_chainmap_instantiation(self): + self.assertIs(type(typing_extensions.ChainMap()), collections.ChainMap) + self.assertIs(type(typing_extensions.ChainMap[KT, VT]()), collections.ChainMap) + self.assertIs(type(typing_extensions.ChainMap[str, int]()), collections.ChainMap) + class CM(typing_extensions.ChainMap[KT, VT]): ... + if TYPING_3_5_3: + self.assertIs(type(CM[int, str]()), CM) + + def test_chainmap_subclass(self): + + class MyChainMap(typing_extensions.ChainMap[str, int]): + pass + + cm = MyChainMap() + self.assertIsInstance(cm, MyChainMap) + + self.assertIsSubclass(MyChainMap, collections.ChainMap) + if TYPING_3_5_3: + self.assertNotIsSubclass(collections.ChainMap, MyChainMap) + + def test_deque_instantiation(self): + self.assertIs(type(typing_extensions.Deque()), collections.deque) + self.assertIs(type(typing_extensions.Deque[T]()), collections.deque) + self.assertIs(type(typing_extensions.Deque[int]()), collections.deque) + class D(typing_extensions.Deque[T]): ... + if TYPING_3_5_3: + self.assertIs(type(D[int]()), D) + + def test_counter_instantiation(self): + self.assertIs(type(typing_extensions.Counter()), collections.Counter) + self.assertIs(type(typing_extensions.Counter[T]()), collections.Counter) + self.assertIs(type(typing_extensions.Counter[int]()), collections.Counter) + class C(typing_extensions.Counter[T]): ... + if TYPING_3_5_3: + self.assertIs(type(C[int]()), C) + self.assertEqual(C.__bases__, (typing_extensions.Counter,)) + + def test_counter_subclass_instantiation(self): + + class MyCounter(typing_extensions.Counter[int]): + pass + + d = MyCounter() + self.assertIsInstance(d, MyCounter) + self.assertIsInstance(d, collections.Counter) + if TYPING_3_5_1: + self.assertIsInstance(d, typing_extensions.Counter) + + @skipUnless(PY36, 'Python 3.6 required') + def test_async_generator(self): + ns = {} + exec("async def f():\n" + " yield 42\n", globals(), ns) + g = ns['f']() + self.assertIsSubclass(type(g), typing_extensions.AsyncGenerator) + + @skipUnless(PY36, 'Python 3.6 required') + def test_no_async_generator_instantiation(self): + with self.assertRaises(TypeError): + typing_extensions.AsyncGenerator() + with self.assertRaises(TypeError): + typing_extensions.AsyncGenerator[T, T]() + with self.assertRaises(TypeError): + typing_extensions.AsyncGenerator[int, int]() + + @skipUnless(PY36, 'Python 3.6 required') + def test_subclassing_async_generator(self): + class G(typing_extensions.AsyncGenerator[int, int]): + def asend(self, value): + pass + def athrow(self, typ, val=None, tb=None): + pass + + ns = {} + exec('async def g(): yield 0', globals(), ns) + g = ns['g'] + self.assertIsSubclass(G, typing_extensions.AsyncGenerator) + self.assertIsSubclass(G, typing_extensions.AsyncIterable) + self.assertIsSubclass(G, collections_abc.AsyncGenerator) + self.assertIsSubclass(G, collections_abc.AsyncIterable) + self.assertNotIsSubclass(type(g), G) + + instance = G() + self.assertIsInstance(instance, typing_extensions.AsyncGenerator) + self.assertIsInstance(instance, typing_extensions.AsyncIterable) + self.assertIsInstance(instance, collections_abc.AsyncGenerator) + self.assertIsInstance(instance, collections_abc.AsyncIterable) + self.assertNotIsInstance(type(g), G) + self.assertNotIsInstance(g, G) + + +class OtherABCTests(BaseTestCase): + + def test_contextmanager(self): + @contextlib.contextmanager + def manager(): + yield 42 + + cm = manager() + self.assertIsInstance(cm, typing_extensions.ContextManager) + self.assertNotIsInstance(42, typing_extensions.ContextManager) + + @skipUnless(ASYNCIO, 'Python 3.5 required') + def test_async_contextmanager(self): + class NotACM: + pass + self.assertIsInstance(ACM(), typing_extensions.AsyncContextManager) + self.assertNotIsInstance(NotACM(), typing_extensions.AsyncContextManager) + @contextlib.contextmanager + def manager(): + yield 42 + + cm = manager() + self.assertNotIsInstance(cm, typing_extensions.AsyncContextManager) + if TYPING_3_5_3: + self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,)) + if TYPING_3_6_1: + with self.assertRaises(TypeError): + isinstance(42, typing_extensions.AsyncContextManager[int]) + with self.assertRaises(TypeError): + typing_extensions.AsyncContextManager[int, str] + + +class TypeTests(BaseTestCase): + + def test_type_basic(self): + + class User: pass + class BasicUser(User): pass + class ProUser(User): pass + + def new_user(user_class: Type[User]) -> User: + return user_class() + + new_user(BasicUser) + + def test_type_typevar(self): + + class User: pass + class BasicUser(User): pass + class ProUser(User): pass + + U = TypeVar('U', bound=User) + + def new_user(user_class: Type[U]) -> U: + return user_class() + + new_user(BasicUser) + + @skipUnless(sys.version_info[:3] != (3, 5, 2), + 'Python 3.5.2 has a somewhat buggy Type impl') + def test_type_optional(self): + A = Optional[Type[BaseException]] + + def foo(a: A) -> Optional[BaseException]: + if a is None: + return None + else: + return a() + + assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) + assert foo(None) is None + + +class NewTypeTests(BaseTestCase): + + def test_basic(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + self.assertIsInstance(UserId(5), int) + self.assertIsInstance(UserName('Joe'), str) + self.assertEqual(UserId(5) + 1, 6) + + def test_errors(self): + UserId = NewType('UserId', int) + UserName = NewType('UserName', str) + with self.assertRaises(TypeError): + issubclass(UserId, int) + with self.assertRaises(TypeError): + class D(UserName): + pass + + +class AllTests(BaseTestCase): + def test_typing_extensions_includes_standard(self): + a = typing_extensions.__all__ + self.assertIn('ClassVar', a) + self.assertIn('Type', a) + self.assertIn('ChainMap', a) + self.assertIn('ContextManager', a) + self.assertIn('Counter', a) + self.assertIn('DefaultDict', a) + self.assertIn('Deque', a) + self.assertIn('NewType', a) + self.assertIn('overload', a) + self.assertIn('Text', a) + self.assertIn('TYPE_CHECKING', a) + + if ASYNCIO: + self.assertIn('Awaitable', a) + self.assertIn('AsyncIterator', a) + self.assertIn('AsyncIterable', a) + self.assertIn('Coroutine', a) + self.assertIn('AsyncContextManager', a) + + if PY36: + self.assertIn('AsyncGenerator', a) + + def test_typing_extensions_defers_when_possible(self): + exclude = {'overload', 'Text', 'TYPE_CHECKING'} + for item in typing_extensions.__all__: + if item not in exclude and hasattr(typing, item): + self.assertIs( + getattr(typing_extensions, item), + getattr(typing, item)) + + +if __name__ == '__main__': + main() diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py new file mode 100644 index 00000000..53e2def2 --- /dev/null +++ b/typing_extensions/src_py3/typing_extensions.py @@ -0,0 +1,622 @@ +import abc +import collections +import contextlib +import sys +import typing +import collections.abc as collections_abc + +if hasattr(typing, '_generic_new'): + _generic_new = typing._generic_new +else: + # Note: The '_generic_new(...)' function is used as a part of the + # process of creating a generic type and was added to the typing module + # as of Python 3.5.3. + # + # We've defined '_generic_new(...)' below to exactly match the behavior + # implemented in older versions of 'typing' bundled with Python 3.5.0 to + # 3.5.2. This helps eliminate redundancy when defining collection types + # like 'Deque' later. + # + # See https://github.com/python/typing/pull/308 for more details -- in + # particular, compare and contrast the definition of types like + # 'typing.List' before and after the merge. + + def _generic_new(base_cls, cls, *args, **kwargs): + return base_cls.__new__(cls, *args, **kwargs) + +# See https://github.com/python/typing/pull/439 +if hasattr(typing, '_geqv'): + from typing import _geqv + _geqv_defined = True +else: + _geqv = None + _geqv_defined = False + +if sys.version_info[:2] >= (3, 6): + import _collections_abc + _check_methods_in_mro = _collections_abc._check_methods +else: + def _check_methods_in_mro(C, *methods): + mro = C.__mro__ + for method in methods: + for B in mro: + if method in B.__dict__: + if B.__dict__[method] is None: + return NotImplemented + break + else: + return NotImplemented + return True + + +# Please keep __all__ alphabetized within each category. +__all__ = [ + # Super-special typing primitives. + 'ClassVar', + 'Type', + + # ABCs (from collections.abc). + # The following are added depending on presence + # of their non-generic counterparts in stdlib: + # 'Awaitable', + # 'AsyncIterator', + # 'AsyncIterable', + # 'Coroutine', + # 'AsyncGenerator', + # 'AsyncContextManager', + # 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + + # One-off things. + 'NewType', + 'overload', + 'Text', + 'TYPE_CHECKING', +] + + +# TODO +if hasattr(typing, 'NoReturn'): + NoReturn = typing.NoReturn +elif hasattr(typing, '_FinalTypingBase'): + class _NoReturn(typing._FinalTypingBase, _root=True): + """Special type indicating functions that never return. + Example:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise Exception('no way') + + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + __slots__ = () + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + NoReturn = _NoReturn(_root=True) +else: + class NoReturnMeta(typing.TypingMeta): + """Metaclass for NoReturn""" + def __new__(cls, name, bases, namespace, _root=False): + return super().__new__(cls, name, bases, namespace, _root=_root) + + def __instancecheck__(self, obj): + raise TypeError("NoReturn cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("NoReturn cannot be used with issubclass().") + + class NoReturn(typing.Final, metaclass=NoReturnMeta, _root=True): + """Special type indicating functions that never return. + Example:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise Exception('no way') + + This type is invalid in other positions, e.g., ``List[NoReturn]`` + will fail in static type checkers. + """ + __slots__ = () + + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +V_co = typing.TypeVar('V_co', covariant=True) # Any type covariant containers. +VT_co = typing.TypeVar('VT_co', covariant=True) # Value type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if hasattr(typing, 'ClassVar'): + ClassVar = typing.ClassVar +elif hasattr(typing, '_FinalTypingBase'): + class _ClassVar(typing._FinalTypingBase, _root=True): + """Special type construct to mark class variables. + + An annotation wrapped in ClassVar indicates that a given + attribute is intended to be used as a class variable and + should not be set on instances of that class. Usage:: + + class Starship: + stats: ClassVar[Dict[str, int]] = {} # class variable + damage: int = 10 # instance variable + + ClassVar accepts only types and cannot be further subscribed. + + Note that ClassVar is not a class itself, and should not + be used with isinstance() or issubclass(). + """ + + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(typing._type_check(item, + '{} accepts only single type.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(typing._type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _ClassVar): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + ClassVar = _ClassVar(_root=True) +else: + class ClassVarMeta(typing.TypingMeta): + """Metaclass for ClassVar""" + + def __new__(cls, name, bases, namespace, tp=None, _root=False): + self = super().__new__(cls, name, bases, namespace, _root=_root) + if tp is not None: + self.__type__ = tp + return self + + def __instancecheck__(self, obj): + raise TypeError("ClassVar cannot be used with isinstance().") + + def __subclasscheck__(self, cls): + raise TypeError("ClassVar cannot be used with issubclass().") + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is not None: + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + param = typing._type_check( + item, + '{} accepts only single type.'.format(cls.__name__[1:])) + return cls(self.__name__, self.__bases__, + dict(self.__dict__), tp=param, _root=True) + + def _eval_type(self, globalns, localns): + new_tp = typing._eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(self.__name__, self.__bases__, + dict(self.__dict__), tp=self.__type__, + _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(typing._type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, ClassVar): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + class ClassVar(typing.Final, metaclass=ClassVarMeta, _root=True): + """Special type construct to mark class variables. + + An annotation wrapped in ClassVar indicates that a given + attribute is intended to be used as a class variable and + should not be set on instances of that class. Usage:: + + class Starship: + stats: ClassVar[Dict[str, int]] = {} # class variable + damage: int = 10 # instance variable + + ClassVar accepts only types and cannot be further subscribed. + + Note that ClassVar is not a class itself, and should not + be used with isinstance() or issubclass(). + """ + + __type__ = None + + +def _overload_dummy(*args, **kwds): + """Helper for @overload to raise when called.""" + raise NotImplementedError( + "You should not call an overloaded function. " + "A series of @overload-decorated functions " + "outside a stub module should always be followed " + "by an implementation that is not @overload-ed.") + + +def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + """ + return _overload_dummy + + +# This is not a real generic class. Don't use outside annotations. +if hasattr(typing, 'Type'): + Type = typing.Type +else: + # Internal type variable used for Type[]. + CT_co = typing.TypeVar('CT_co', covariant=True, bound=type) + + class Type(typing.Generic[CT_co], extra=type): + """A special construct usable to annotate class objects. + + For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + + And a function that takes a class argument that's a subclass of + User and returns an instance of the corresponding class:: + + U = TypeVar('U', bound=User) + def new_user(user_class: Type[U]) -> U: + user = user_class() + # (Here we could write the user object to a database) + return user + joe = new_user(BasicUser) + + At this point the type checker knows that joe has type BasicUser. + """ + + __slots__ = () + + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + +def _define_guard(type_name): + """ + Returns True if the given type isn't defined in typing but + is defined in collections_abc. + + Adds the type to __all__ if the collection is found in either + typing or collection_abc. + """ + if hasattr(typing, type_name): + __all__.append(type_name) + globals()[type_name] = getattr(typing, type_name) + return False + elif hasattr(collections_abc, type_name): + __all__.append(type_name) + return True + else: + return False + + +if _define_guard('Awaitable'): + class Awaitable(typing.Generic[T_co], extra=collections_abc.Awaitable): + __slots__ = () + + +if _define_guard('Coroutine'): + class Coroutine(Awaitable[V_co], typing.Generic[T_co, T_contra, V_co], + extra=collections_abc.Coroutine): + __slots__ = () + + +if _define_guard('AsyncIterable'): + class AsyncIterable(typing.Generic[T_co], + extra=collections_abc.AsyncIterable): + __slots__ = () + + +if _define_guard('AsyncIterator'): + class AsyncIterator(AsyncIterable[T_co], + extra=collections_abc.AsyncIterator): + __slots__ = () + + +if hasattr(typing, 'Deque'): + Deque = typing.Deque +elif _geqv_defined: + class Deque(collections.deque, typing.MutableSequence[T], + extra=collections.deque): + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, Deque): + return collections.deque(*args, **kwds) + return _generic_new(collections.deque, cls, *args, **kwds) +else: + class Deque(collections.deque, typing.MutableSequence[T], + extra=collections.deque): + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Deque: + return collections.deque(*args, **kwds) + return _generic_new(collections.deque, cls, *args, **kwds) + + +if hasattr(typing, 'ContextManager'): + ContextManager = typing.ContextManager +elif hasattr(contextlib, 'AbstractContextManager'): + class ContextManager(typing.Generic[T_co], + extra=contextlib.AbstractContextManager): + __slots__ = () +else: + class ContextManager(typing.Generic[T_co]): + __slots__ = () + + def __enter__(self): + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is ContextManager: + # In Python 3.6+, it is possible to set a method to None to + # explicitly indicate that the class does not implement an ABC + # (https://bugs.python.org/issue25958), but we do not support + # that pattern here because this fallback class is only used + # in Python 3.5 and earlier. + if (any("__enter__" in B.__dict__ for B in C.__mro__) and + any("__exit__" in B.__dict__ for B in C.__mro__)): + return True + return NotImplemented + + +if hasattr(typing, 'AsyncContextManager'): + AsyncContextManager = typing.AsyncContextManager + __all__.append('AsyncContextManager') +elif hasattr(contextlib, 'AbstractAsyncContextManager'): + class AsyncContextManager(typing.Generic[T_co], + extra=contextlib.AbstractAsyncContextManager): + __slots__ = () + + __all__.append('AsyncContextManager') +elif sys.version_info[:2] >= (3, 5): + exec(""" +class AsyncContextManager(typing.Generic[T_co]): + __slots__ = () + + async def __aenter__(self): + return self + + @abc.abstractmethod + async def __aexit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is AsyncContextManager: + return _check_methods_in_mro(C, "__aenter__", "__aexit__") + return NotImplemented + +__all__.append('AsyncContextManager') +""") + + +if hasattr(typing, 'DefaultDict'): + DefaultDict = typing.DefaultDict +elif _geqv_defined: + class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], + extra=collections.defaultdict): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, DefaultDict): + return collections.defaultdict(*args, **kwds) + return _generic_new(collections.defaultdict, cls, *args, **kwds) +else: + class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], + extra=collections.defaultdict): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is DefaultDict: + return collections.defaultdict(*args, **kwds) + return _generic_new(collections.defaultdict, cls, *args, **kwds) + + +if hasattr(typing, 'Counter'): + Counter = typing.Counter +elif (3, 5, 0) <= sys.version_info[:3] <= (3, 5, 1): + assert _geqv_defined + _TInt = typing.TypeVar('_TInt') + + class CounterMeta(typing.GenericMeta): + """Metaclass for Counter""" + def __getitem__(self, item): + return super().__getitem__((item, int)) + + class Counter(collections.Counter, + typing.Dict[T, int], + metaclass=CounterMeta, + extra=collections.Counter): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, Counter): + return collections.Counter(*args, **kwds) + return _generic_new(collections.Counter, cls, *args, **kwds) + +elif _geqv_defined: + class Counter(collections.Counter, + typing.Dict[T, int], + extra=collections.Counter): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, Counter): + return collections.Counter(*args, **kwds) + return _generic_new(collections.Counter, cls, *args, **kwds) + +else: + class Counter(collections.Counter, + typing.Dict[T, int], + extra=collections.Counter): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is Counter: + return collections.Counter(*args, **kwds) + return _generic_new(collections.Counter, cls, *args, **kwds) + + +if hasattr(typing, 'ChainMap'): + ChainMap = typing.ChainMap + __all__.append('ChainMap') +elif hasattr(collections, 'ChainMap'): + # ChainMap only exists in 3.3+ + if _geqv_defined: + class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], + extra=collections.ChainMap): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, ChainMap): + return collections.ChainMap(*args, **kwds) + return _generic_new(collections.ChainMap, cls, *args, **kwds) + else: + class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], + extra=collections.ChainMap): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is ChainMap: + return collections.ChainMap(*args, **kwds) + return _generic_new(collections.ChainMap, cls, *args, **kwds) + + __all__.append('ChainMap') + + +if _define_guard('AsyncGenerator'): + class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra], + extra=collections_abc.AsyncGenerator): + __slots__ = () + + +if hasattr(typing, 'NewType'): + NewType = typing.NewType +else: + def NewType(name, tp): + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy function that simply returns its argument. Usage:: + + UserId = NewType('UserId', int) + + def name_by_id(user_id: UserId) -> str: + ... + + UserId('user') # Fails type check + + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + + num = UserId(5) + 1 # type: int + """ + + def new_type(x): + return x + + new_type.__name__ = name + new_type.__supertype__ = tp + return new_type + + +if hasattr(typing, 'Text'): + Text = typing.Text +else: + Text = str + + +if hasattr(typing, 'TYPE_CHECKING'): + TYPE_CHECKING = typing.TYPE_CHECKING +else: + # Constant that's True when type checking, but False here. + TYPE_CHECKING = False