Skip to content

Commit abfdf49

Browse files
authored
Fix hash of GenericMeta classes (#371)
* Fix hash of GenericMeta classes * Fix lint * Fix for Python 3.2 repr()
1 parent 27f6d63 commit abfdf49

File tree

6 files changed

+103
-2
lines changed

6 files changed

+103
-2
lines changed

python2/mod.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import TypeVar, Generic
2+
3+
T = TypeVar('T')
4+
5+
6+
class A(Generic[T]):
7+
pass
8+
9+
10+
class B(Generic[T]):
11+
class A(Generic[T]):
12+
pass

python2/test_typing.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,42 @@ def test_subscript_meta(self):
707707
self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int])
708708
self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta))
709709

710+
def test_generic_hashes(self):
711+
import mod
712+
class A(Generic[T]):
713+
pass
714+
715+
class B(Generic[T]):
716+
class A(Generic[T]):
717+
pass
718+
719+
self.assertEqual(A, A)
720+
self.assertEqual(mod.A[str], mod.A[str])
721+
self.assertEqual(B.A, B.A)
722+
self.assertEqual(mod.B.A[B.A[str]], mod.B.A[B.A[str]])
723+
724+
self.assertNotEqual(A, B.A)
725+
self.assertNotEqual(A, mod.A)
726+
self.assertNotEqual(A, mod.B.A)
727+
self.assertNotEqual(B.A, mod.A)
728+
self.assertNotEqual(B.A, mod.B.A)
729+
730+
self.assertNotEqual(A[str], B.A[str])
731+
self.assertNotEqual(A[List[Any]], B.A[List[Any]])
732+
self.assertNotEqual(A[str], mod.A[str])
733+
self.assertNotEqual(A[str], mod.B.A[str])
734+
self.assertNotEqual(B.A[int], mod.A[int])
735+
self.assertNotEqual(B.A[List[Any]], mod.B.A[List[Any]])
736+
737+
self.assertNotEqual(Tuple[A[str]], Tuple[B.A[str]])
738+
self.assertNotEqual(Tuple[A[List[Any]]], Tuple[B.A[List[Any]]])
739+
self.assertNotEqual(Union[str, A[str]], Union[str, mod.A[str]])
740+
self.assertNotEqual(Union[A[str], A[str]], Union[A[str], mod.A[str]])
741+
self.assertNotEqual(typing.FrozenSet[A[str]], typing.FrozenSet[mod.B.A[str]])
742+
743+
self.assertTrue(repr(Tuple[A[str]]).endswith('typing.A[str]]'))
744+
self.assertTrue(repr(Tuple[mod.A[str]]).endswith('mod.A[str]]'))
745+
710746
def test_extended_generic_rules_eq(self):
711747
T = TypeVar('T')
712748
U = TypeVar('U')

python2/typing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,8 @@ def __new__(cls, name, bases, namespace,
10731073

10741074
if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2.
10751075
self.__qualname__ = origin.__qualname__
1076-
self.__tree_hash__ = hash(self._subs_tree()) if origin else hash((self.__name__,))
1076+
self.__tree_hash__ = (hash(self._subs_tree()) if origin else
1077+
super(GenericMeta, self).__hash__())
10771078
return self
10781079

10791080
def __init__(self, *args, **kwargs):

src/mod.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import TypeVar, Generic
2+
3+
T = TypeVar('T')
4+
5+
6+
class A(Generic[T]):
7+
pass
8+
9+
10+
class B(Generic[T]):
11+
class A(Generic[T]):
12+
pass

src/test_typing.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,45 @@ def test_subscript_meta(self):
738738
self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int])
739739
self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta))
740740

741+
def test_generic_hashes(self):
742+
import mod
743+
class A(Generic[T]):
744+
...
745+
746+
class B(Generic[T]):
747+
class A(Generic[T]):
748+
...
749+
750+
self.assertEqual(A, A)
751+
self.assertEqual(mod.A[str], mod.A[str])
752+
self.assertEqual(B.A, B.A)
753+
self.assertEqual(mod.B.A[B.A[str]], mod.B.A[B.A[str]])
754+
755+
self.assertNotEqual(A, B.A)
756+
self.assertNotEqual(A, mod.A)
757+
self.assertNotEqual(A, mod.B.A)
758+
self.assertNotEqual(B.A, mod.A)
759+
self.assertNotEqual(B.A, mod.B.A)
760+
761+
self.assertNotEqual(A[str], B.A[str])
762+
self.assertNotEqual(A[List[Any]], B.A[List[Any]])
763+
self.assertNotEqual(A[str], mod.A[str])
764+
self.assertNotEqual(A[str], mod.B.A[str])
765+
self.assertNotEqual(B.A[int], mod.A[int])
766+
self.assertNotEqual(B.A[List[Any]], mod.B.A[List[Any]])
767+
768+
self.assertNotEqual(Tuple[A[str]], Tuple[B.A[str]])
769+
self.assertNotEqual(Tuple[A[List[Any]]], Tuple[B.A[List[Any]]])
770+
self.assertNotEqual(Union[str, A[str]], Union[str, mod.A[str]])
771+
self.assertNotEqual(Union[A[str], A[str]], Union[A[str], mod.A[str]])
772+
self.assertNotEqual(typing.FrozenSet[A[str]], typing.FrozenSet[mod.B.A[str]])
773+
774+
if sys.version_info[:2] > (3, 2):
775+
self.assertTrue(repr(Tuple[A[str]]).endswith('<locals>.A[str]]'))
776+
self.assertTrue(repr(Tuple[B.A[str]]).endswith('<locals>.B.A[str]]'))
777+
self.assertTrue(repr(Tuple[mod.A[str]]).endswith('mod.A[str]]'))
778+
self.assertTrue(repr(Tuple[mod.B.A[str]]).endswith('mod.B.A[str]]'))
779+
741780
def test_extended_generic_rules_eq(self):
742781
T = TypeVar('T')
743782
U = TypeVar('U')

src/typing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ def __new__(cls, name, bases, namespace,
10051005

10061006
if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2.
10071007
self.__qualname__ = origin.__qualname__
1008-
self.__tree_hash__ = hash(self._subs_tree()) if origin else hash((self.__name__,))
1008+
self.__tree_hash__ = (hash(self._subs_tree()) if origin else
1009+
super(GenericMeta, self).__hash__())
10091010
return self
10101011

10111012
def _get_type_vars(self, tvars):

0 commit comments

Comments
 (0)