Ensure that Node(), Node[T]() and Node[int]() all have the same __class__. Fixes #79.
diff --git a/prototyping/test_typing.py b/prototyping/test_typing.py
index 0183573..b390395 100644
--- a/prototyping/test_typing.py
+++ b/prototyping/test_typing.py
@@ -784,6 +784,25 @@
a.append(42)
assert a.get() == [1, 42]
+ def test_type_erasure(self):
+ T = TypeVar('T')
+
+ class Node(Generic[T]):
+ def __init__(self, label: T, left: 'Node[T]' = None, right: 'Node[T]' = None):
+ self.label = label # type: T
+ self.left = left # type: Optional[Node[T]]
+ self.right = right # type: Optional[Node[T]]
+
+ def foo(x: T):
+ a = Node(x)
+ b = Node[T](x)
+ c = Node[Any](x)
+ assert type(a) is Node
+ assert type(b) is Node
+ assert type(c) is Node
+
+ foo(42)
+
class VarianceTests(TestCase):
diff --git a/prototyping/typing.py b/prototyping/typing.py
index c087196..aae3046 100644
--- a/prototyping/typing.py
+++ b/prototyping/typing.py
@@ -882,6 +882,13 @@
"""
+def _gorg(a):
+ """Return the farthest origin of a generic class."""
+ while a.__origin__ is not None:
+ a = a.__origin__
+ return a
+
+
def _geqv(a, b):
"""Return whether two generic classes are equivalent.
@@ -892,9 +899,9 @@
The relation is reflexive, symmetric and transitive.
"""
- # TODO: Don't depend on name/module being equal.
assert isinstance(a, GenericMeta) and isinstance(b, GenericMeta)
- return a.__name__ == b.__name__ and a.__module__ == b.__module__
+ # Reduce each to its origin.
+ return _gorg(a) is _gorg(b)
class GenericMeta(TypingMeta, abc.ABCMeta):
@@ -1082,6 +1089,16 @@
# Same body as above.
"""
+ def __new__(cls, *args, **kwds):
+ next_in_mro = None
+ for i, c in enumerate(cls.__mro__):
+ if _gorg(c) is Generic:
+ next_in_mro = cls.__mro__[i+1]
+ break
+ else:
+ next_in_mro = object
+ return next_in_mro.__new__(_gorg(cls))
+
def cast(typ, val):
"""Cast a value to a type.