Skip to content

Add more tests for ABC behaviour of typing classes #294

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 2 commits into from
Oct 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 78 additions & 0 deletions python2/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from typing import NamedTuple
from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
import abc
import typing
try:
import collections.abc as collections_abc
Expand Down Expand Up @@ -1059,6 +1060,8 @@ def __len__(self):
return 0

self.assertEqual(len(MMC()), 0)
assert callable(MMC.update)
self.assertIsInstance(MMC(), typing.Mapping)

class MMB(typing.MutableMapping[KT, VT]):
def __getitem__(self, k):
Expand All @@ -1083,6 +1086,81 @@ def __len__(self):
self.assertIsSubclass(MMB, typing.Mapping)
self.assertIsSubclass(MMC, typing.Mapping)

self.assertIsInstance(MMB[KT, VT](), typing.Mapping)
self.assertIsInstance(MMB[KT, VT](), collections.Mapping)

self.assertIsSubclass(MMA, collections.Mapping)
self.assertIsSubclass(MMB, collections.Mapping)
self.assertIsSubclass(MMC, collections.Mapping)

self.assertIsSubclass(MMB[str, str], typing.Mapping)
self.assertIsSubclass(MMC, MMA)

class I(typing.Iterable): pass
self.assertNotIsSubclass(list, I)

class G(typing.Generator[int, int, int]): pass
def g(): yield 0
self.assertIsSubclass(G, typing.Generator)
self.assertIsSubclass(G, typing.Iterable)
if hasattr(collections, 'Generator'):
self.assertIsSubclass(G, collections.Generator)
self.assertIsSubclass(G, collections.Iterable)
self.assertNotIsSubclass(type(g), G)

def test_subclassing_subclasshook(self):

class Base(typing.Iterable):
@classmethod
def __subclasshook__(cls, other):
if other.__name__ == 'Foo':
return True
else:
return False

class C(Base): pass
class Foo: pass
class Bar: pass
self.assertIsSubclass(Foo, Base)
self.assertIsSubclass(Foo, C)
self.assertNotIsSubclass(Bar, C)

def test_subclassing_register(self):

class A(typing.Container): pass
class B(A): pass

class C: pass
A.register(C)
self.assertIsSubclass(C, A)
self.assertNotIsSubclass(C, B)

class D: pass
B.register(D)
self.assertIsSubclass(D, A)
self.assertIsSubclass(D, B)

class M(): pass
collections.MutableMapping.register(M)
self.assertIsSubclass(M, typing.Mapping)

def test_collections_as_base(self):

class M(collections.Mapping): pass
self.assertIsSubclass(M, typing.Mapping)
self.assertIsSubclass(M, typing.Iterable)

class S(collections.MutableSequence): pass
self.assertIsSubclass(S, typing.MutableSequence)
self.assertIsSubclass(S, typing.Iterable)

class I(collections.Iterable): pass
self.assertIsSubclass(I, typing.Iterable)

class A(collections.Mapping): pass
class B: pass
A.register(B)
self.assertIsSubclass(B, typing.Mapping)

class TypeTests(BaseTestCase):

Expand Down
11 changes: 8 additions & 3 deletions python2/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ def __extrahook__(cls, subclass):
res = cls.__extra__.__subclasshook__(subclass)
if res is not NotImplemented:
return res
if cls.__extra__ in subclass.__mro__:
if cls.__extra__ in getattr(subclass, '__mro__', ()):
return True
for scls in cls.__extra__.__subclasses__():
if isinstance(scls, GenericMeta):
Expand All @@ -1046,6 +1046,8 @@ class GenericMeta(TypingMeta, abc.ABCMeta):

def __new__(cls, name, bases, namespace,
tvars=None, args=None, origin=None, extra=None):
if extra is None:
extra = namespace.get('__extra__')
if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
bases = (extra,) + bases
self = super(GenericMeta, cls).__new__(cls, name, bases, namespace)
Expand Down Expand Up @@ -1093,14 +1095,17 @@ def __new__(cls, name, bases, namespace,
self.__parameters__ = tvars
self.__args__ = args
self.__origin__ = origin
self.__extra__ = namespace.get('__extra__')
self.__extra__ = extra
# Speed hack (https://github.com/python/typing/issues/196).
self.__next_in_mro__ = _next_in_mro(self)

# This allows unparameterized generic collections to be used
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
self.__subclasshook__ = _make_subclasshook(self)
if ('__subclasshook__' not in namespace and extra # allow overriding
or hasattr(self.__subclasshook__, '__name__') and
self.__subclasshook__.__name__ == '__extrahook__'):
self.__subclasshook__ = _make_subclasshook(self)
if isinstance(extra, abc.ABCMeta):
self._abc_registry = extra._abc_registry
return self
Expand Down
79 changes: 79 additions & 0 deletions src/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from typing import NamedTuple
from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
import abc
import typing
try:
import collections.abc as collections_abc
Expand Down Expand Up @@ -1385,6 +1386,8 @@ def __len__(self):
return 0

self.assertEqual(len(MMC()), 0)
assert callable(MMC.update)
self.assertIsInstance(MMC(), typing.Mapping)

class MMB(typing.MutableMapping[KT, VT]):
def __getitem__(self, k):
Expand All @@ -1409,6 +1412,82 @@ def __len__(self):
self.assertIsSubclass(MMB, typing.Mapping)
self.assertIsSubclass(MMC, typing.Mapping)

self.assertIsInstance(MMB[KT, VT](), typing.Mapping)
self.assertIsInstance(MMB[KT, VT](), collections.Mapping)

self.assertIsSubclass(MMA, collections.Mapping)
self.assertIsSubclass(MMB, collections.Mapping)
self.assertIsSubclass(MMC, collections.Mapping)

self.assertIsSubclass(MMB[str, str], typing.Mapping)
self.assertIsSubclass(MMC, MMA)

class I(typing.Iterable): ...
self.assertNotIsSubclass(list, I)

class G(typing.Generator[int, int, int]): ...
def g(): yield 0
self.assertIsSubclass(G, typing.Generator)
self.assertIsSubclass(G, typing.Iterable)
if hasattr(collections, 'Generator'):
self.assertIsSubclass(G, collections.Generator)
self.assertIsSubclass(G, collections.Iterable)
self.assertNotIsSubclass(type(g), G)

def test_subclassing_subclasshook(self):

class Base(typing.Iterable):
@classmethod
def __subclasshook__(cls, other):
if other.__name__ == 'Foo':
return True
else:
return False

class C(Base): ...
class Foo: ...
class Bar: ...
self.assertIsSubclass(Foo, Base)
self.assertIsSubclass(Foo, C)
self.assertNotIsSubclass(Bar, C)

def test_subclassing_register(self):

class A(typing.Container): ...
class B(A): ...

class C: ...
A.register(C)
self.assertIsSubclass(C, A)
self.assertNotIsSubclass(C, B)

class D: ...
B.register(D)
self.assertIsSubclass(D, A)
self.assertIsSubclass(D, B)

class M(): ...
collections.MutableMapping.register(M)
self.assertIsSubclass(M, typing.Mapping)

def test_collections_as_base(self):

class M(collections.Mapping): ...
self.assertIsSubclass(M, typing.Mapping)
self.assertIsSubclass(M, typing.Iterable)

class S(collections.MutableSequence): ...
self.assertIsSubclass(S, typing.MutableSequence)
self.assertIsSubclass(S, typing.Iterable)

class I(collections.Iterable): ...
self.assertIsSubclass(I, typing.Iterable)

class A(collections.Mapping, metaclass=abc.ABCMeta): ...
class B: ...
A.register(B)
self.assertIsSubclass(B, typing.Mapping)


class OtherABCTests(BaseTestCase):

Expand Down
5 changes: 4 additions & 1 deletion src/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,10 @@ def __new__(cls, name, bases, namespace,
# This allows unparameterized generic collections to be used
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
self.__subclasshook__ = _make_subclasshook(self)
if ('__subclasshook__' not in namespace and extra # allow overriding
or hasattr(self.__subclasshook__, '__name__') and
self.__subclasshook__.__name__ == '__extrahook__'):
self.__subclasshook__ = _make_subclasshook(self)
if isinstance(extra, abc.ABCMeta):
self._abc_registry = extra._abc_registry
return self
Expand Down