Skip to content

Refactor Union, Tuple, and Callable #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Oct 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e8c3ad6
Make Tuple[T, S] subcriptable and subclassable
ilevkivskyi Oct 23, 2016
cfcf81f
Make Callable[..., T] subscriptable and subclassable
ilevkivskyi Oct 23, 2016
1368e97
Make Union[T, S] subsctiptable
ilevkivskyi Oct 23, 2016
7ee4a1a
Disable plain Union and Optional as type arguments
ilevkivskyi Oct 23, 2016
5fdceba
Implement __eq__ for GenericMeta
ilevkivskyi Oct 23, 2016
f339586
Implement __eq__ for Unions
ilevkivskyi Oct 23, 2016
26f50a3
Refactor common code in __eq__ and __repr__
ilevkivskyi Oct 23, 2016
65de907
Remove redundant code
ilevkivskyi Oct 23, 2016
897de4e
Fix minor bugs + mini-refactoring
ilevkivskyi Oct 23, 2016
c687ffa
Fix repr of Callable subclasses
ilevkivskyi Oct 23, 2016
582a37f
Don't be too pedantic in GenericMeta._eval_type
ilevkivskyi Oct 23, 2016
02e6b14
Simplify __hash__ and factor out orig_chain
ilevkivskyi Oct 23, 2016
8d802cc
Remove outdated tests + be more pedanctic in _Union._eval_type
ilevkivskyi Oct 23, 2016
4e2527c
Add tests
ilevkivskyi Oct 23, 2016
92da67f
Factor out _subs_tree for Union and Generic
ilevkivskyi Oct 24, 2016
7563c92
Prohibit plain Generic[T] as type argument
ilevkivskyi Oct 24, 2016
f33545b
Correct a comment
ilevkivskyi Oct 24, 2016
4a996b2
Erase but preserve type also for Callable
ilevkivskyi Oct 24, 2016
6564742
Erase but preserve type for all generics
ilevkivskyi Oct 24, 2016
184f089
Restore original __next_in_mro__ and use more precise bases
Oct 24, 2016
b613325
Preserve __qualname__ on subscription
Oct 24, 2016
011b9d8
Add tests and factor common stuff for Union
ilevkivskyi Oct 24, 2016
52712a6
Add even more tests
ilevkivskyi Oct 24, 2016
2641151
One more test + minor code simplification
ilevkivskyi Oct 24, 2016
9f1fb98
Polishing code
ilevkivskyi Oct 25, 2016
279a67e
Minor changes
ilevkivskyi Oct 25, 2016
f173fde
Precalculate hash + refactor + more tests
ilevkivskyi Oct 25, 2016
819d472
Simplify repr for Union
ilevkivskyi Oct 25, 2016
e6f19a8
Add big test for __eq__, __repr__, and Any substitution
ilevkivskyi Oct 25, 2016
deed806
Add test to illustrate substitution
ilevkivskyi Oct 25, 2016
f6382a7
Minor change in tests
ilevkivskyi Oct 25, 2016
9bc7374
Add/expand comments and docstrings
ilevkivskyi Oct 25, 2016
c2724ce
Fix the __qualname__ in PY 3.2
ilevkivskyi Oct 25, 2016
fd3e344
Add tests to Python 2
ilevkivskyi Oct 25, 2016
cd7bbe2
Modify Union in Python 2
ilevkivskyi Oct 25, 2016
068ab5e
Update Python 2 tests
ilevkivskyi Oct 25, 2016
09c4797
Change remaining parts in Python 2
ilevkivskyi Oct 25, 2016
60303f7
Fix final test in PY 2
ilevkivskyi Oct 25, 2016
ea5dd05
Also call super().__init__ in PY 2 Generic
ilevkivskyi Oct 26, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 122 additions & 21 deletions python2/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ def test_union_unique(self):
self.assertEqual(Union[X, X], X)
self.assertNotEqual(Union[X, int], Union[X])
self.assertNotEqual(Union[X, int], Union[int])
self.assertEqual(Union[X, int].__union_params__, (X, int))
self.assertEqual(Union[X, int].__union_set_params__, {X, int})
self.assertEqual(Union[X, int].__args__, (X, int))
self.assertEqual(Union[X, int].__parameters__, (X,))
self.assertIs(Union[X, int].__origin__, Union)

def test_union_constrained(self):
A = TypeVar('A', str, bytes)
Expand Down Expand Up @@ -308,8 +309,6 @@ def Elem(*args):
class TupleTests(BaseTestCase):

def test_basics(self):
with self.assertRaises(TypeError):
issubclass(Tuple[int, str], Tuple)
with self.assertRaises(TypeError):
issubclass(Tuple, Tuple[int, str])
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -364,22 +363,6 @@ def test_eq_hash(self):
self.assertNotEqual(Callable[[int], int], Callable[[], int])
self.assertNotEqual(Callable[[int], int], Callable)

def test_cannot_subclass(self):
with self.assertRaises(TypeError):

class C(Callable):
pass

with self.assertRaises(TypeError):

class C(type(Callable)):
pass

with self.assertRaises(TypeError):

class C(Callable[[int], int]):
pass

def test_cannot_instantiate(self):
with self.assertRaises(TypeError):
Callable()
Expand Down Expand Up @@ -683,6 +666,124 @@ class D(C, List[T][U][V]): pass
self.assertEqual(C.__orig_bases__, (List[T][U][V],))
self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))

def test_extended_generic_rules_eq(self):
T = TypeVar('T')
U = TypeVar('U')
self.assertEqual(Tuple[T, T][int], Tuple[int, int])
self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]])
with self.assertRaises(TypeError):
Tuple[T, int][()]
with self.assertRaises(TypeError):
Tuple[T, U][T, ...]

self.assertEqual(Union[T, int][int], int)
self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str])
class Base(object): pass
class Derived(Base): pass
self.assertEqual(Union[T, Base][Derived], Base)
with self.assertRaises(TypeError):
Union[T, int][1]

self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT])
self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]])
with self.assertRaises(TypeError):
Callable[[T], U][..., int]
with self.assertRaises(TypeError):
Callable[[T], U][[], int]

def test_extended_generic_rules_repr(self):
T = TypeVar('T')
self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''),
'Union[Tuple, Callable]')
self.assertEqual(repr(Union[Tuple, Tuple[int]]).replace('typing.', ''),
'Tuple')
self.assertEqual(repr(Callable[..., Optional[T]][int]).replace('typing.', ''),
'Callable[..., Union[int, NoneType]]')
self.assertEqual(repr(Callable[[], List[T]][int]).replace('typing.', ''),
'Callable[[], List[int]]')

def test_generic_forvard_ref(self):
LLT = List[List['T']]
T = TypeVar('T')
self.assertEqual(typing._eval_type(LLT, globals(), locals()), List[List[T]])
TTE = Tuple[T, ...]
self.assertIs(typing._eval_type(TTE, globals(), locals()), Tuple[T, ...])

def test_extended_generic_rules_subclassing(self):
class T1(Tuple[T, KT]): pass
class T2(Tuple[T, ...]): pass
class C1(Callable[[T], T]): pass
class C2(Callable[..., int]):
def __call__(self):
return None

self.assertEqual(T1.__parameters__, (T, KT))
self.assertEqual(T1[int, str].__args__, (int, str))
self.assertEqual(T1[int, T].__origin__, T1)

self.assertEqual(T2.__parameters__, (T,))
with self.assertRaises(TypeError):
T1[int]
with self.assertRaises(TypeError):
T2[int, str]

self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]')
self.assertEqual(C2.__parameters__, ())
self.assertIsInstance(C2(), collections_abc.Callable)
self.assertIsSubclass(C2, collections_abc.Callable)
self.assertIsSubclass(C1, collections_abc.Callable)
self.assertIsInstance(T1(), tuple)
self.assertIsSubclass(T2, tuple)
self.assertIsSubclass(Tuple[int, ...], typing.Sequence)
self.assertIsSubclass(Tuple[int, ...], typing.Iterable)

def test_fail_with_bare_union(self):
with self.assertRaises(TypeError):
List[Union]
with self.assertRaises(TypeError):
Tuple[Optional]
with self.assertRaises(TypeError):
ClassVar[ClassVar]
with self.assertRaises(TypeError):
List[ClassVar[int]]

def test_fail_with_bare_generic(self):
T = TypeVar('T')
with self.assertRaises(TypeError):
List[Generic]
with self.assertRaises(TypeError):
Tuple[Generic[T]]
with self.assertRaises(TypeError):
List[typing._Protocol]

def test_type_erasure_special(self):
T = TypeVar('T')
class MyTup(Tuple[T, T]): pass
self.assertIs(MyTup[int]().__class__, MyTup)
self.assertIs(MyTup[int]().__orig_class__, MyTup[int])
class MyCall(Callable[..., T]):
def __call__(self): return None
self.assertIs(MyCall[T]().__class__, MyCall)
self.assertIs(MyCall[T]().__orig_class__, MyCall[T])
class MyDict(typing.Dict[T, T]): pass
self.assertIs(MyDict[int]().__class__, MyDict)
self.assertIs(MyDict[int]().__orig_class__, MyDict[int])
class MyDef(typing.DefaultDict[str, T]): pass
self.assertIs(MyDef[int]().__class__, MyDef)
self.assertIs(MyDef[int]().__orig_class__, MyDef[int])

def test_all_repr_eq_any(self):
objs = (getattr(typing, el) for el in typing.__all__)
for obj in objs:
self.assertNotEqual(repr(obj), '')
self.assertEqual(obj, obj)
if getattr(obj, '__parameters__', None) and len(obj.__parameters__) == 1:
self.assertEqual(obj[Any].__args__, (Any,))
if isinstance(obj, type):
for base in obj.__mro__:
self.assertNotEqual(repr(base), '')
self.assertEqual(base, base)

def test_pickle(self):
global C # pickle wants to reference the class by name
T = TypeVar('T')
Expand Down Expand Up @@ -724,7 +825,7 @@ class C(Generic[T]):
X = C[int]
self.assertEqual(X.__module__, __name__)
if not PY32:
self.assertEqual(X.__qualname__, 'C')
self.assertTrue(X.__qualname__.endswith('.<locals>.C'))
self.assertEqual(repr(X).split('.')[-1], 'C[int]')

class Y(C[int]):
Expand Down
Loading