Skip to content

Commit 063ca66

Browse files
Michael0x2ailevkivskyi
authored andcommitted
Add typing extensions (#443)
This pull request adds a 'typing_exensions' subproject to 'typing'. The 'typing_extensions' module backports any new additions to 'typing' for Python 3.5+ users who are using older versions of 'typing' that were bundled with their standard library (and so can't update to the latest versions). As well, it will contain new experimental features than could eventually end up in 'typing'. See python/typing#435 for motivation and additional context.
0 parents  commit 063ca66

File tree

6 files changed

+1737
-0
lines changed

6 files changed

+1737
-0
lines changed

typing_extensions/README.rst

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=================
2+
Typing Extensions
3+
=================
4+
5+
.. image:: https://badges.gitter.im/python/typing.svg
6+
:alt: Chat at https://gitter.im/python/typing
7+
:target: https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
8+
9+
Overview
10+
========
11+
12+
The ``typing`` module was added to the standard library in Python 3.5 on
13+
a provisional basis and will no longer be provisional in Python 3.7. However,
14+
this means users of Python 3.5 - 3.6 who are unable to upgrade will not be
15+
able to take advantage of new types added to the ``typing`` module, such as
16+
``typing.Text`` or ``typing.Coroutine``.
17+
18+
The ``typing_extensions`` module contains both backports of these changes
19+
as well as experimental types that will eventually be added to the ``typing``
20+
module.
21+
22+
Users of other Python versions should continue to install and use
23+
use the ``typing`` module from PyPi instead of using this one unless
24+
specifically writing code that must be compatible with multiple Python
25+
versions or requires experimental types.
26+
27+
Included items
28+
==============
29+
30+
This module currently contains the following:
31+
32+
All Python versions:
33+
--------------------
34+
35+
- ``ClassVar``
36+
- ``ContextManager``
37+
- ``Counter``
38+
- ``DefaultDict``
39+
- ``Deque``
40+
- ``NewType``
41+
- ``NoReturn``
42+
- ``overload`` (note that older versions of ``typing`` only let you use ``overload`` in stubs)
43+
- ``Text``
44+
- ``Type``
45+
- ``TYPE_CHECKING``
46+
47+
Python 3.3+ only:
48+
-----------------
49+
50+
- ``ChainMap``
51+
52+
Python 3.5+ only:
53+
-----------------
54+
55+
- ``AsyncIterable``
56+
- ``AsyncIterator``
57+
- ``AsyncContextManager``
58+
- ``Awaitable``
59+
- ``Coroutine``
60+
61+
Python 3.6+ only:
62+
-----------------
63+
64+
- ``AsyncGenerator``
65+
66+
Other Notes and Limitations
67+
===========================
68+
69+
There are a few types whose interface was modified between different
70+
versions of typing. For example, ``typing.Sequence`` was modified to
71+
subclass ``typing.Reversible`` as of Python 3.5.3.
72+
73+
These changes are _not_ backported to prevent subtle compatibility
74+
issues when mixing the differing implementations of modified classes.
75+
76+
Running tests
77+
=============
78+
79+
To run tests, navigate into the appropriate source directory and run
80+
``test_typing_extensions.py``. You will also need to install the latest
81+
version of ``typing`` if you are using a version of Python that does not
82+
include ``typing`` as a part of the standard library.
83+

typing_extensions/setup.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env python
2+
# coding: utf-8
3+
4+
import sys
5+
from distutils.core import setup
6+
7+
if sys.version_info < (2, 7, 0) or (3, 0, 0) <= sys.version_info < (3, 3, 0):
8+
sys.stderr.write('ERROR: You need Python 2.7 or 3.3+ '
9+
'to install the typing package.\n')
10+
exit(1)
11+
12+
version = '3.6.1'
13+
description = 'Backported and Experimental Type Hints for Python 3.5+'
14+
long_description = '''\
15+
Typing Extensions -- Backported and Experimental Type Hints for Python
16+
17+
The ``typing`` module was added to the standard library in Python 3.5 on
18+
a provisional basis and will no longer be provisional in Python 3.7. However,
19+
this means users of Python 3.5 - 3.6 who are unable to upgrade will not be
20+
able to take advantage of new types added to the ``typing`` module, such as
21+
``typing.Text`` or ``typing.Coroutine``.
22+
23+
The ``typing_extensions`` module contains both backports of these changes
24+
as well as experimental types that will eventually be added to the ``typing``
25+
module.
26+
27+
Users of other Python versions should continue to install and use
28+
use the ``typing`` module from PyPi instead of using this one unless
29+
specifically writing code that must be compatible with multiple Python
30+
versions or requires experimental types.
31+
'''
32+
33+
classifiers = [
34+
'Development Status :: 3 - Alpha',
35+
'Environment :: Console',
36+
'Intended Audience :: Developers',
37+
'License :: OSI Approved :: Python Software Foundation License',
38+
'Operating System :: OS Independent',
39+
'Programming Language :: Python :: 2.7',
40+
'Programming Language :: Python :: 3.3',
41+
'Programming Language :: Python :: 3.4',
42+
'Programming Language :: Python :: 3.5',
43+
'Programming Language :: Python :: 3.6',
44+
'Topic :: Software Development',
45+
]
46+
47+
if sys.version_info.major == 2:
48+
package_dir = 'src_py2'
49+
elif sys.version_info.major == 3:
50+
package_dir = 'src_py3'
51+
else:
52+
raise AssertionError()
53+
54+
install_requires = []
55+
if sys.version_info < (3, 5):
56+
install_requires.append('typing >= 3.6.1')
57+
58+
setup(name='typing_extensions',
59+
version=version,
60+
description=description,
61+
long_description=long_description,
62+
author='Guido van Rossum, Jukka Lehtosalo, Lukasz Langa, Michael Lee',
63+
author_email='[email protected]',
64+
url='https://github.com/python/typing',
65+
license='PSF',
66+
keywords='typing function annotations type hints hinting checking '
67+
'checker typehints typehinting typechecking backport',
68+
package_dir={'': package_dir},
69+
py_modules=['typing_extensions'],
70+
classifiers=classifiers,
71+
install_requires=install_requires)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import sys
2+
import os
3+
import abc
4+
import contextlib
5+
import collections
6+
from unittest import TestCase, main, skipUnless
7+
8+
from typing_extensions import NoReturn, ClassVar
9+
from typing_extensions import ContextManager, Counter, Deque, DefaultDict
10+
from typing_extensions import NewType, overload
11+
import typing
12+
import typing_extensions
13+
14+
15+
T = typing.TypeVar('T')
16+
KT = typing.TypeVar('KT')
17+
VT = typing.TypeVar('VT')
18+
19+
20+
class BaseTestCase(TestCase):
21+
22+
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
23+
if not issubclass(cls, class_or_tuple):
24+
message = '%r is not a subclass of %r' % (cls, class_or_tuple)
25+
if msg is not None:
26+
message += ' : %s' % msg
27+
raise self.failureException(message)
28+
29+
def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
30+
if issubclass(cls, class_or_tuple):
31+
message = '%r is a subclass of %r' % (cls, class_or_tuple)
32+
if msg is not None:
33+
message += ' : %s' % msg
34+
raise self.failureException(message)
35+
36+
37+
class Employee(object):
38+
pass
39+
40+
41+
class NoReturnTests(BaseTestCase):
42+
43+
def test_noreturn_instance_type_error(self):
44+
with self.assertRaises(TypeError):
45+
isinstance(42, NoReturn)
46+
47+
def test_noreturn_subclass_type_error(self):
48+
with self.assertRaises(TypeError):
49+
issubclass(Employee, NoReturn)
50+
with self.assertRaises(TypeError):
51+
issubclass(NoReturn, Employee)
52+
53+
def test_repr(self):
54+
if hasattr(typing, 'NoReturn'):
55+
self.assertEqual(repr(NoReturn), 'typing.NoReturn')
56+
else:
57+
self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn')
58+
59+
def test_not_generic(self):
60+
with self.assertRaises(TypeError):
61+
NoReturn[int]
62+
63+
def test_cannot_subclass(self):
64+
with self.assertRaises(TypeError):
65+
class A(NoReturn):
66+
pass
67+
with self.assertRaises(TypeError):
68+
class A(type(NoReturn)):
69+
pass
70+
71+
def test_cannot_instantiate(self):
72+
with self.assertRaises(TypeError):
73+
NoReturn()
74+
with self.assertRaises(TypeError):
75+
type(NoReturn)()
76+
77+
78+
class ClassVarTests(BaseTestCase):
79+
80+
def test_basics(self):
81+
with self.assertRaises(TypeError):
82+
ClassVar[1]
83+
with self.assertRaises(TypeError):
84+
ClassVar[int, str]
85+
with self.assertRaises(TypeError):
86+
ClassVar[int][str]
87+
88+
def test_repr(self):
89+
self.assertEqual(repr(ClassVar), 'typing.ClassVar')
90+
cv = ClassVar[int]
91+
self.assertEqual(repr(cv), 'typing.ClassVar[int]')
92+
cv = ClassVar[Employee]
93+
self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__)
94+
95+
def test_cannot_subclass(self):
96+
with self.assertRaises(TypeError):
97+
class C(type(ClassVar)):
98+
pass
99+
with self.assertRaises(TypeError):
100+
class C(type(ClassVar[int])):
101+
pass
102+
103+
def test_cannot_init(self):
104+
with self.assertRaises(TypeError):
105+
ClassVar()
106+
with self.assertRaises(TypeError):
107+
type(ClassVar)()
108+
with self.assertRaises(TypeError):
109+
type(ClassVar[typing.Optional[int]])()
110+
111+
def test_no_isinstance(self):
112+
with self.assertRaises(TypeError):
113+
isinstance(1, ClassVar[int])
114+
with self.assertRaises(TypeError):
115+
issubclass(int, ClassVar)
116+
117+
118+
class CollectionsAbcTests(BaseTestCase):
119+
120+
def test_contextmanager(self):
121+
@contextlib.contextmanager
122+
def manager():
123+
yield 42
124+
125+
cm = manager()
126+
self.assertIsInstance(cm, ContextManager)
127+
self.assertNotIsInstance(42, ContextManager)
128+
129+
with self.assertRaises(TypeError):
130+
isinstance(42, ContextManager[int])
131+
with self.assertRaises(TypeError):
132+
isinstance(cm, ContextManager[int])
133+
with self.assertRaises(TypeError):
134+
issubclass(type(cm), ContextManager[int])
135+
136+
def test_counter(self):
137+
self.assertIsSubclass(collections.Counter, Counter)
138+
self.assertIs(type(Counter()), collections.Counter)
139+
self.assertIs(type(Counter[T]()), collections.Counter)
140+
self.assertIs(type(Counter[int]()), collections.Counter)
141+
142+
class A(Counter[int]): pass
143+
class B(Counter[T]): pass
144+
145+
self.assertIsInstance(A(), collections.Counter)
146+
self.assertIs(type(B[int]()), B)
147+
self.assertEqual(B.__bases__, (typing_extensions.Counter,))
148+
149+
def test_deque(self):
150+
self.assertIsSubclass(collections.deque, Deque)
151+
self.assertIs(type(Deque()), collections.deque)
152+
self.assertIs(type(Deque[T]()), collections.deque)
153+
self.assertIs(type(Deque[int]()), collections.deque)
154+
155+
class A(Deque[int]): pass
156+
class B(Deque[T]): pass
157+
158+
self.assertIsInstance(A(), collections.deque)
159+
self.assertIs(type(B[int]()), B)
160+
161+
def test_defaultdict_instantiation(self):
162+
self.assertIsSubclass(collections.defaultdict, DefaultDict)
163+
self.assertIs(type(DefaultDict()), collections.defaultdict)
164+
self.assertIs(type(DefaultDict[KT, VT]()), collections.defaultdict)
165+
self.assertIs(type(DefaultDict[str, int]()), collections.defaultdict)
166+
167+
class A(DefaultDict[str, int]): pass
168+
class B(DefaultDict[KT, VT]): pass
169+
170+
self.assertIsInstance(A(), collections.defaultdict)
171+
self.assertIs(type(B[str, int]()), B)
172+
173+
174+
class NewTypeTests(BaseTestCase):
175+
176+
def test_basic(self):
177+
UserId = NewType('UserId', int)
178+
UserName = NewType('UserName', str)
179+
self.assertIsInstance(UserId(5), int)
180+
self.assertIsInstance(UserName('Joe'), type('Joe'))
181+
self.assertEqual(UserId(5) + 1, 6)
182+
183+
def test_errors(self):
184+
UserId = NewType('UserId', int)
185+
UserName = NewType('UserName', str)
186+
with self.assertRaises(TypeError):
187+
issubclass(UserId, int)
188+
with self.assertRaises(TypeError):
189+
class D(UserName):
190+
pass
191+
192+
193+
class OverloadTests(BaseTestCase):
194+
195+
def test_overload_fails(self):
196+
with self.assertRaises(RuntimeError):
197+
@overload
198+
def blah():
199+
pass
200+
201+
blah()
202+
203+
def test_overload_succeeds(self):
204+
@overload
205+
def blah():
206+
pass
207+
208+
def blah():
209+
pass
210+
211+
blah()
212+
213+
214+
class AllTests(BaseTestCase):
215+
216+
def test_typing_extensions_includes_standard(self):
217+
a = typing_extensions.__all__
218+
self.assertIn('ClassVar', a)
219+
self.assertIn('Type', a)
220+
self.assertIn('Counter', a)
221+
self.assertIn('DefaultDict', a)
222+
self.assertIn('Deque', a)
223+
self.assertIn('NewType', a)
224+
self.assertIn('overload', a)
225+
self.assertIn('Text', a)
226+
self.assertIn('TYPE_CHECKING', a)
227+
228+
def test_typing_extensions_defers_when_possible(self):
229+
exclude = {'overload', 'Text', 'TYPE_CHECKING'}
230+
for item in typing_extensions.__all__:
231+
if item not in exclude and hasattr(typing, item):
232+
self.assertIs(
233+
getattr(typing_extensions, item),
234+
getattr(typing, item))
235+
236+
237+
if __name__ == '__main__':
238+
main()

0 commit comments

Comments
 (0)