From 2b828f111e38368854c3539e32698260145fa339 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 29 Sep 2016 23:24:31 +0200 Subject: [PATCH 1/3] Response to most comments --- python2/typing.py | 10 +++++++--- src/typing.py | 13 ++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/python2/typing.py b/python2/typing.py index 3b88f359b..6a8b299cc 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 diff --git a/src/typing.py b/src/typing.py index 5b1d1713a..b1f7ea01c 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 @@ -502,7 +505,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 From 2d6f3dda50769465581d2a0255daa0f20e0120c7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 29 Sep 2016 23:51:25 +0200 Subject: [PATCH 2/3] change behavior of Union w.r.t. Any; update docstrings --- python2/test_typing.py | 10 ---------- python2/typing.py | 29 ++++++++++------------------- src/test_typing.py | 10 ---------- src/typing.py | 29 ++++++++++------------------- 4 files changed, 20 insertions(+), 58 deletions(-) diff --git a/python2/test_typing.py b/python2/test_typing.py index 5e510d4fc..61c82f4cb 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -201,10 +201,6 @@ 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) def test_union_object(self): u = Union[object] @@ -214,12 +210,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 6a8b299cc..b0dda2c3b 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -431,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__ = () @@ -602,16 +606,6 @@ 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?). @@ -650,14 +644,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) @@ -730,7 +721,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..5fc7ffb39 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -202,10 +202,6 @@ 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) def test_union_object(self): u = Union[object] @@ -215,12 +211,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 b1f7ea01c..99c69397a 100644 --- a/src/typing.py +++ b/src/typing.py @@ -378,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__ = () @@ -545,16 +549,6 @@ 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?). @@ -592,14 +586,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) @@ -665,7 +656,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__ = () From c5b66dea9ca1ac98f5d7b73c7230252f7cc5d354 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 30 Sep 2016 00:34:02 +0200 Subject: [PATCH 3/3] Response to comments --- python2/test_typing.py | 7 +++++++ python2/typing.py | 4 ++++ src/test_typing.py | 7 +++++++ src/typing.py | 4 ++++ 4 files changed, 22 insertions(+) diff --git a/python2/test_typing.py b/python2/test_typing.py index 61c82f4cb..73a9f0d76 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -201,6 +201,13 @@ def test_subclass_error(self): def test_union_any(self): u = Union[Any] 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] diff --git a/python2/typing.py b/python2/typing.py index b0dda2c3b..8880a2586 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -606,6 +606,10 @@ class Manager(Employee): pass Union[Manager, int, Employee] == Union[int, Employee] Union[Employee, Manager] == Employee + - Similar for object:: + + Union[int, object] == object + - You cannot subclass or instantiate a union. - You cannot write Union[X][Y] (what would it mean?). diff --git a/src/test_typing.py b/src/test_typing.py index 5fc7ffb39..cf3171f77 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -202,6 +202,13 @@ def test_subclass_error(self): def test_union_any(self): u = Union[Any] 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] diff --git a/src/typing.py b/src/typing.py index 99c69397a..694cd93cd 100644 --- a/src/typing.py +++ b/src/typing.py @@ -549,6 +549,10 @@ class Manager(Employee): pass Union[Manager, int, Employee] == Union[int, Employee] Union[Employee, Manager] == Employee + - Similar for object:: + + Union[int, object] == object + - You cannot subclass or instantiate a union. - You cannot write Union[X][Y] (what would it mean?).