diff --git a/python2/test_typing.py b/python2/test_typing.py index 5e510d4fc..73a9f0d76 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -201,10 +201,13 @@ def test_subclass_error(self): def test_union_any(self): u = Union[Any] self.assertEqual(u, Any) - u = Union[int, Any] - self.assertEqual(u, Any) - u = Union[Any, int] - self.assertEqual(u, Any) + u1 = Union[int, Any] + u2 = Union[Any, int] + u3 = Union[Any, object] + self.assertEqual(u1, u2) + self.assertNotEqual(u1, Any) + self.assertNotEqual(u2, Any) + self.assertNotEqual(u3, Any) def test_union_object(self): u = Union[object] @@ -214,12 +217,6 @@ def test_union_object(self): u = Union[object, int] self.assertEqual(u, object) - def test_union_any_object(self): - u = Union[object, Any] - self.assertEqual(u, Any) - u = Union[Any, object] - self.assertEqual(u, Any) - def test_unordered(self): u1 = Union[int, float] u2 = Union[float, int] diff --git a/python2/typing.py b/python2/typing.py index 3b88f359b..8880a2586 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -149,7 +149,7 @@ def __new__(cls, *args, **kwds): isinstance(args[1], tuple)): # Close enough. raise TypeError("Cannot subclass %r" % cls) - return object.__new__(cls) + return super(_TypingBase, cls).__new__(cls) # Things that are not classes also need these. def _eval_type(self, globalns, localns): @@ -168,7 +168,11 @@ def __call__(self, *args, **kwds): class _FinalTypingBase(_TypingBase): - """Mix-in class to prevent instantiation.""" + """Mix-in class to prevent instantiation. + + Prevents instantiation unless _root=True is given in class call. + It is used to create pseudo-singleton instances Any, Union, Tuple, etc. + """ __slots__ = () @@ -256,7 +260,7 @@ def __init__(self, name, type_var, impl_type, type_checker): assert isinstance(name, basestring), repr(name) assert isinstance(impl_type, type), repr(impl_type) assert not isinstance(impl_type, TypingMeta), repr(impl_type) - assert isinstance(type_var, (type, _TypingBase)) + assert isinstance(type_var, (type, _TypingBase)), repr(type_var) self.name = name self.type_var = type_var self.impl_type = impl_type @@ -427,9 +431,13 @@ def __new__(cls, name, bases, namespace): class _Any(_FinalTypingBase): """Special type indicating an unconstrained type. - - Any object is an instance of Any. - - Any class is a subclass of Any. - - As a special case, Any and object are subclasses of each other. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + or class checks. """ __metaclass__ = AnyMeta __slots__ = () @@ -598,16 +606,10 @@ class Manager(Employee): pass Union[Manager, int, Employee] == Union[int, Employee] Union[Employee, Manager] == Employee - - Corollary: if Any is present it is the sole survivor, e.g.:: - - Union[int, Any] == Any - - Similar for object:: Union[int, object] == object - - To cut a tie: Union[object, Any] == Union[Any, object] == Any. - - You cannot subclass or instantiate a union. - You cannot write Union[X][Y] (what would it mean?). @@ -646,14 +648,11 @@ def __new__(cls, parameters=None, *args, **kwds): assert not all_params, all_params # Weed out subclasses. # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If Any or object is present it will be the sole survivor. - # If both Any and object are present, Any wins. - # Never discard type variables, except against Any. + # If object is present it will be sole survivor among proper classes. + # Never discard type variables. # (In particular, Union[str, AnyStr] != AnyStr.) all_params = set(params) for t1 in params: - if t1 is Any: - return Any if not isinstance(t1, type): continue if any(isinstance(t2, type) and issubclass(t1, t2) @@ -726,7 +725,7 @@ def __new__(cls, name, bases, namespace): class _Optional(_FinalTypingBase): """Optional type. - Optional[X] is equivalent to Union[X, type(None)]. + Optional[X] is equivalent to Union[X, None]. """ __metaclass__ = OptionalMeta diff --git a/src/test_typing.py b/src/test_typing.py index 6aa74db32..cf3171f77 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -202,10 +202,13 @@ def test_subclass_error(self): def test_union_any(self): u = Union[Any] self.assertEqual(u, Any) - u = Union[int, Any] - self.assertEqual(u, Any) - u = Union[Any, int] - self.assertEqual(u, Any) + u1 = Union[int, Any] + u2 = Union[Any, int] + u3 = Union[Any, object] + self.assertEqual(u1, u2) + self.assertNotEqual(u1, Any) + self.assertNotEqual(u2, Any) + self.assertNotEqual(u3, Any) def test_union_object(self): u = Union[object] @@ -215,12 +218,6 @@ def test_union_object(self): u = Union[object, int] self.assertEqual(u, object) - def test_union_any_object(self): - u = Union[object, Any] - self.assertEqual(u, Any) - u = Union[Any, object] - self.assertEqual(u, Any) - def test_unordered(self): u1 = Union[int, float] u2 = Union[float, int] diff --git a/src/typing.py b/src/typing.py index 5b1d1713a..694cd93cd 100644 --- a/src/typing.py +++ b/src/typing.py @@ -143,7 +143,6 @@ class _TypingBase(metaclass=TypingMeta, _root=True): __slots__ = () - def __init__(self, *args, **kwds): pass @@ -158,7 +157,7 @@ def __new__(cls, *args, **kwds): isinstance(args[1], tuple)): # Close enough. raise TypeError("Cannot subclass %r" % cls) - return object.__new__(cls) + return super().__new__(cls) # Things that are not classes also need these. def _eval_type(self, globalns, localns): @@ -177,7 +176,11 @@ def __call__(self, *args, **kwds): class _FinalTypingBase(_TypingBase, _root=True): - """Mix-in class to prevent instantiation.""" + """Mix-in class to prevent instantiation. + + Prevents instantiation unless _root=True is given in class call. + It is used to create pseudo-singleton instances Any, Union, Tuple, etc. + """ __slots__ = () @@ -273,7 +276,7 @@ def __init__(self, name, type_var, impl_type, type_checker): assert isinstance(name, str), repr(name) assert isinstance(impl_type, type), repr(impl_type) assert not isinstance(impl_type, TypingMeta), repr(impl_type) - assert isinstance(type_var, (type, _TypingBase)) + assert isinstance(type_var, (type, _TypingBase)), repr(type_var) self.name = name self.type_var = type_var self.impl_type = impl_type @@ -375,9 +378,13 @@ def _type_repr(obj): class _Any(_FinalTypingBase, _root=True): """Special type indicating an unconstrained type. - - Any object is an instance of Any. - - Any class is a subclass of Any. - - As a special case, Any and object are subclasses of each other. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + or class checks. """ __slots__ = () @@ -502,7 +509,7 @@ def inner(*args, **kwds): try: return cached(*args, **kwds) except TypeError: - pass # Do not duplicate real errors. + pass # All real errors (not unhashable args) are raised below. return func(*args, **kwds) return inner @@ -542,16 +549,10 @@ class Manager(Employee): pass Union[Manager, int, Employee] == Union[int, Employee] Union[Employee, Manager] == Employee - - Corollary: if Any is present it is the sole survivor, e.g.:: - - Union[int, Any] == Any - - Similar for object:: Union[int, object] == object - - To cut a tie: Union[object, Any] == Union[Any, object] == Any. - - You cannot subclass or instantiate a union. - You cannot write Union[X][Y] (what would it mean?). @@ -589,14 +590,11 @@ def __new__(cls, parameters=None, *args, _root=False): assert not all_params, all_params # Weed out subclasses. # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If Any or object is present it will be the sole survivor. - # If both Any and object are present, Any wins. - # Never discard type variables, except against Any. + # If object is present it will be sole survivor among proper classes. + # Never discard type variables. # (In particular, Union[str, AnyStr] != AnyStr.) all_params = set(params) for t1 in params: - if t1 is Any: - return Any if not isinstance(t1, type): continue if any(isinstance(t2, type) and issubclass(t1, t2) @@ -662,7 +660,7 @@ def __subclasscheck__(self, cls): class _Optional(_FinalTypingBase, _root=True): """Optional type. - Optional[X] is equivalent to Union[X, type(None)]. + Optional[X] is equivalent to Union[X, None]. """ __slots__ = ()