From 4ac02d55c6d7d666253312ee5885c49fec943849 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 20 Mar 2018 12:10:01 -0700 Subject: [PATCH 1/9] Handle NotImplemented from __ne__; improve test cases Also change sharedparse.py to avoid this kind of bug in future. Fixes #4767. --- mypy/sharedparse.py | 114 +++++++-------------- test-data/unit/check-warnings.test | 23 ++++- test-data/unit/fixtures/notimplemented.pyi | 4 +- 3 files changed, 61 insertions(+), 80 deletions(-) diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 91015b6d693f..62f16781547e 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -3,30 +3,16 @@ """Shared logic between our three mypy parser files.""" -MAGIC_METHODS = { - "__abs__", +BINARY_MAGIC_METHODS = { "__add__", "__and__", - "__call__", "__cmp__", - "__complex__", - "__contains__", - "__del__", - "__delattr__", - "__delitem__", "__divmod__", "__div__", - "__enter__", - "__exit__", "__eq__", "__floordiv__", - "__float__", "__ge__", - "__getattr__", - "__getattribute__", - "__getitem__", "__gt__", - "__hex__", "__iadd__", "__iand__", "__idiv__", @@ -34,36 +20,22 @@ "__ilshift__", "__imod__", "__imul__", - "__init__", - "__init_subclass__", - "__int__", - "__invert__", "__ior__", "__ipow__", "__irshift__", "__isub__", - "__iter__", "__ixor__", "__le__", - "__len__", - "__long__", "__lshift__", "__lt__", "__mod__", "__mul__", "__ne__", - "__neg__", - "__new__", - "__nonzero__", - "__oct__", "__or__", - "__pos__", "__pow__", "__radd__", "__rand__", "__rdiv__", - "__repr__", - "__reversed__", "__rfloordiv__", "__rlshift__", "__rmod__", @@ -74,13 +46,45 @@ "__rshift__", "__rsub__", "__rxor__", + "__sub__", + "__xor__", +} + + +MAGIC_METHODS = { + "__abs__", + "__call__", + "__complex__", + "__contains__", + "__del__", + "__delattr__", + "__delitem__", + "__enter__", + "__exit__", + "__float__", + "__getattr__", + "__getattribute__", + "__getitem__", + "__hex__", + "__init__", + "__init_subclass__", + "__int__", + "__invert__", + "__iter__", + "__len__", + "__long__", + "__neg__", + "__new__", + "__nonzero__", + "__oct__", + "__pos__", + "__repr__", + "__reversed__", "__setattr__", "__setitem__", "__str__", - "__sub__", "__unicode__", - "__xor__", -} +}.union(BINARY_MAGIC_METHODS) MAGIC_METHODS_ALLOWING_KWARGS = { "__init__", @@ -91,52 +95,6 @@ MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS -BINARY_MAGIC_METHODS = { - "__add__", - "__and__", - "__cmp__", - "__divmod__", - "__div__", - "__eq__", - "__floordiv__", - "__ge__", - "__gt__", - "__iadd__", - "__iand__", - "__idiv__", - "__ifloordiv__", - "__ilshift__", - "__imod__", - "__imul__", - "__ior__", - "__ipow__", - "__irshift__", - "__isub__", - "__ixor__", - "__le__", - "__lshift__", - "__lt__", - "__mod__", - "__mul__", - "__or__", - "__pow__", - "__radd__", - "__rand__", - "__rdiv__", - "__rfloordiv__", - "__rlshift__", - "__rmod__", - "__rmul__", - "__ror__", - "__rpow__", - "__rrshift__", - "__rshift__", - "__rsub__", - "__rxor__", - "__sub__", - "__xor__", -} - def special_function_elide_names(name: str) -> bool: return name in MAGIC_METHODS_POS_ARGS_ONLY diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index d812ed83efa8..50d7a3dd095e 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -145,8 +145,29 @@ main:4: warning: Returning Any from function declared to return "int" [case testReturnAnyForNotImplementedInBinaryMagicMethods] # flags: --warn-return-any +from typing import Any class A: - def __eq__(self, other: object) -> bool: return NotImplemented + def __init__(self, x: Any) -> None: + self.x = x + def __eq__(self, other: Any) -> bool: + if self.__class__ is other.__class__: + return bool(self.x == other.x) + return NotImplemented + def __lt__(self, other: Any) -> bool: return NotImplemented + def __gt__(self, other: Any) -> bool: return NotImplemented + def __le__(self, other: Any) -> bool: return NotImplemented + def __ge__(self, other: Any) -> bool: return NotImplemented + def __ne__(self, other: Any) -> bool: + if self.__class__ is other.__class__: + return bool(self.x != other.x) + else: + # pylint will complain about this return not needing + # an "else", but want to check that Union[bool, NotImplemented] + # works as the inferred return typ + return NotImplemented + # TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", + # mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", + # which gives a run-time error (issue #4767) [builtins fixtures/notimplemented.pyi] [out] diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index e619a6c5ad85..9d6e6cebbb56 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -3,11 +3,13 @@ from typing import Any, cast class object: + __class__ = None def __init__(self) -> None: pass class type: pass class function: pass -class bool: pass +class bool: + def __init__(self, o: Any) -> None: pass class int: pass class str: pass NotImplemented = cast(Any, None) From dafce6ccb4bdbfc91b2cdc919a3923a335dc7b2e Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 20 Mar 2018 12:10:01 -0700 Subject: [PATCH 2/9] Handle NotImplemented from __ne__; improve test cases Also change sharedparse.py to avoid this kind of bug in future. Fixes #4767. --- mypy/sharedparse.py | 114 +++++++-------------- test-data/unit/check-warnings.test | 23 ++++- test-data/unit/fixtures/notimplemented.pyi | 4 +- 3 files changed, 61 insertions(+), 80 deletions(-) diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 91015b6d693f..62f16781547e 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -3,30 +3,16 @@ """Shared logic between our three mypy parser files.""" -MAGIC_METHODS = { - "__abs__", +BINARY_MAGIC_METHODS = { "__add__", "__and__", - "__call__", "__cmp__", - "__complex__", - "__contains__", - "__del__", - "__delattr__", - "__delitem__", "__divmod__", "__div__", - "__enter__", - "__exit__", "__eq__", "__floordiv__", - "__float__", "__ge__", - "__getattr__", - "__getattribute__", - "__getitem__", "__gt__", - "__hex__", "__iadd__", "__iand__", "__idiv__", @@ -34,36 +20,22 @@ "__ilshift__", "__imod__", "__imul__", - "__init__", - "__init_subclass__", - "__int__", - "__invert__", "__ior__", "__ipow__", "__irshift__", "__isub__", - "__iter__", "__ixor__", "__le__", - "__len__", - "__long__", "__lshift__", "__lt__", "__mod__", "__mul__", "__ne__", - "__neg__", - "__new__", - "__nonzero__", - "__oct__", "__or__", - "__pos__", "__pow__", "__radd__", "__rand__", "__rdiv__", - "__repr__", - "__reversed__", "__rfloordiv__", "__rlshift__", "__rmod__", @@ -74,13 +46,45 @@ "__rshift__", "__rsub__", "__rxor__", + "__sub__", + "__xor__", +} + + +MAGIC_METHODS = { + "__abs__", + "__call__", + "__complex__", + "__contains__", + "__del__", + "__delattr__", + "__delitem__", + "__enter__", + "__exit__", + "__float__", + "__getattr__", + "__getattribute__", + "__getitem__", + "__hex__", + "__init__", + "__init_subclass__", + "__int__", + "__invert__", + "__iter__", + "__len__", + "__long__", + "__neg__", + "__new__", + "__nonzero__", + "__oct__", + "__pos__", + "__repr__", + "__reversed__", "__setattr__", "__setitem__", "__str__", - "__sub__", "__unicode__", - "__xor__", -} +}.union(BINARY_MAGIC_METHODS) MAGIC_METHODS_ALLOWING_KWARGS = { "__init__", @@ -91,52 +95,6 @@ MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS -BINARY_MAGIC_METHODS = { - "__add__", - "__and__", - "__cmp__", - "__divmod__", - "__div__", - "__eq__", - "__floordiv__", - "__ge__", - "__gt__", - "__iadd__", - "__iand__", - "__idiv__", - "__ifloordiv__", - "__ilshift__", - "__imod__", - "__imul__", - "__ior__", - "__ipow__", - "__irshift__", - "__isub__", - "__ixor__", - "__le__", - "__lshift__", - "__lt__", - "__mod__", - "__mul__", - "__or__", - "__pow__", - "__radd__", - "__rand__", - "__rdiv__", - "__rfloordiv__", - "__rlshift__", - "__rmod__", - "__rmul__", - "__ror__", - "__rpow__", - "__rrshift__", - "__rshift__", - "__rsub__", - "__rxor__", - "__sub__", - "__xor__", -} - def special_function_elide_names(name: str) -> bool: return name in MAGIC_METHODS_POS_ARGS_ONLY diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index d812ed83efa8..50d7a3dd095e 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -145,8 +145,29 @@ main:4: warning: Returning Any from function declared to return "int" [case testReturnAnyForNotImplementedInBinaryMagicMethods] # flags: --warn-return-any +from typing import Any class A: - def __eq__(self, other: object) -> bool: return NotImplemented + def __init__(self, x: Any) -> None: + self.x = x + def __eq__(self, other: Any) -> bool: + if self.__class__ is other.__class__: + return bool(self.x == other.x) + return NotImplemented + def __lt__(self, other: Any) -> bool: return NotImplemented + def __gt__(self, other: Any) -> bool: return NotImplemented + def __le__(self, other: Any) -> bool: return NotImplemented + def __ge__(self, other: Any) -> bool: return NotImplemented + def __ne__(self, other: Any) -> bool: + if self.__class__ is other.__class__: + return bool(self.x != other.x) + else: + # pylint will complain about this return not needing + # an "else", but want to check that Union[bool, NotImplemented] + # works as the inferred return typ + return NotImplemented + # TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", + # mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", + # which gives a run-time error (issue #4767) [builtins fixtures/notimplemented.pyi] [out] diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index e619a6c5ad85..9d6e6cebbb56 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -3,11 +3,13 @@ from typing import Any, cast class object: + __class__ = None def __init__(self) -> None: pass class type: pass class function: pass -class bool: pass +class bool: + def __init__(self, o: Any) -> None: pass class int: pass class str: pass NotImplemented = cast(Any, None) From c08426653505c147b99e134ecd985eebd9ecc566 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 20 Mar 2018 13:17:35 -0700 Subject: [PATCH 3/9] Handle NotImplemented from __ne__; improve test cases Also change sharedparse.py to avoid this kind of bug in future. Fixes #4767. --- test-data/unit/check-warnings.test | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 50d7a3dd095e..27648380bbae 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -148,26 +148,26 @@ main:4: warning: Returning Any from function declared to return "int" from typing import Any class A: def __init__(self, x: Any) -> None: - self.x = x + self.x = x def __eq__(self, other: Any) -> bool: - if self.__class__ is other.__class__: - return bool(self.x == other.x) - return NotImplemented + if self.__class__ is other.__class__: + return bool(self.x == other.x) + return NotImplemented def __lt__(self, other: Any) -> bool: return NotImplemented def __gt__(self, other: Any) -> bool: return NotImplemented def __le__(self, other: Any) -> bool: return NotImplemented def __ge__(self, other: Any) -> bool: return NotImplemented def __ne__(self, other: Any) -> bool: - if self.__class__ is other.__class__: - return bool(self.x != other.x) - else: - # pylint will complain about this return not needing - # an "else", but want to check that Union[bool, NotImplemented] - # works as the inferred return typ - return NotImplemented - # TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", - # mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", - # which gives a run-time error (issue #4767) + if self.__class__ is other.__class__: + return bool(self.x != other.x) + else: + # pylint will complain about this return not needing + # an "else", but want to check that Union[bool, NotImplemented] + # works as the inferred return typ + return NotImplemented +# TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", +# mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", +# which gives a run-time error (issue #4767) [builtins fixtures/notimplemented.pyi] [out] From 2a1bd83a164340fb8cb2f02a632ec282685040bc Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 20 Mar 2018 15:51:49 -0700 Subject: [PATCH 4/9] adjusted comment in test to remove reference to pylint --- test-data/unit/check-warnings.test | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 27648380bbae..52f9a4115b47 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -158,12 +158,13 @@ class A: def __le__(self, other: Any) -> bool: return NotImplemented def __ge__(self, other: Any) -> bool: return NotImplemented def __ne__(self, other: Any) -> bool: + # The logic here is similar to __eq__, except the + # "return" is inside the if-then-else. The intention + # is to also verify that both ways of inferring + # Union[bool, type(NotImplemented)] are checked. if self.__class__ is other.__class__: return bool(self.x != other.x) else: - # pylint will complain about this return not needing - # an "else", but want to check that Union[bool, NotImplemented] - # works as the inferred return typ return NotImplemented # TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", # mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", From bc76eed197666984be07f7a342d18cfc9e433179 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Wed, 21 Mar 2018 15:13:12 -0700 Subject: [PATCH 5/9] added more __xxx__ methods and tests --- mypy/sharedparse.py | 22 +++++++-- test-data/unit/check-warnings.test | 73 ++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 62f16781547e..29aa595a73b9 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -7,27 +7,35 @@ "__add__", "__and__", "__cmp__", - "__divmod__", + "__concat__" + "__contains__", + "__delitem__", "__div__", + "__divmod__", "__eq__", "__floordiv__", "__ge__", + "__getitem__", "__gt__", "__iadd__", "__iand__", + "__iconcat__", "__idiv__", "__ifloordiv__", "__ilshift__", + "__imatmul__" "__imod__", "__imul__", "__ior__", "__ipow__", "__irshift__", "__isub__", + "__itruediv__", "__ixor__", "__le__", "__lshift__", "__lt__", + "__matmul__" "__mod__", "__mul__", "__ne__", @@ -36,8 +44,10 @@ "__radd__", "__rand__", "__rdiv__", + "__rdivmod__", "__rfloordiv__", "__rlshift__", + "__rmatmul__", "__rmod__", "__rmul__", "__ror__", @@ -45,8 +55,11 @@ "__rrshift__", "__rshift__", "__rsub__", + "__rtruediv__", "__rxor__", + "__setitem__", "__sub__", + "__truediv__", "__xor__", } @@ -55,20 +68,19 @@ "__abs__", "__call__", "__complex__", - "__contains__", "__del__", "__delattr__", - "__delitem__", "__enter__", "__exit__", "__float__", "__getattr__", "__getattribute__", - "__getitem__", "__hex__", + "__index__", "__init__", "__init_subclass__", "__int__", + "__inv__", "__invert__", "__iter__", "__len__", @@ -76,12 +88,12 @@ "__neg__", "__new__", "__nonzero__", + "__not__", "__oct__", "__pos__", "__repr__", "__reversed__", "__setattr__", - "__setitem__", "__str__", "__unicode__", }.union(BINARY_MAGIC_METHODS) diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 52f9a4115b47..6c56b67ecf60 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -172,6 +172,79 @@ class A: [builtins fixtures/notimplemented.pyi] [out] +[case testReturnNotImplementedAllMagicMethods] +# flags: --warn-return-any +from typing import Any +class A: + # The commented-out items give a warning: Returning Any from function declared to return "A" + # def __abs__(self) -> 'A': return NotImplemented + def __add__(self, other: Any) -> 'A': return NotImplemented + def __and__(self, other: Any) -> 'A': return NotImplemented + # def __complex__(self) -> 'A': return NotImplemented + # def __concat__(self, other: Any) -> 'A': return NotImplemented + # def __contains__(self, other: Any) -> 'A': return NotImplemented + # def __call__(self, *args, **kwargs) -> Any: return NotImplemented # <====== crashes + # def __del__(self) -> None: return NotImplemented # Returning Any fromfunction declared to return "None" + def __delitem__(self, other: Any) -> 'A': return NotImplemented + def __eq__(self, other: Any) -> bool: return NotImplemented + # def __float__(self) -> 'A': return NotImplemented + def __floordiv__(self, other: Any) -> 'A': return NotImplemented + def __ge__(self, other: Any) -> bool: return NotImplemented + def __getitem__(self, other: Any) -> 'A': return NotImplemented + def __gt__(self, other: Any) -> bool: return NotImplemented + def __iadd__(self, other: Any) -> 'A': return NotImplemented + def __iand__(self, other: Any) -> 'A': return NotImplemented + def __iconcat__(self, other: Any) -> 'A': return NotImplemented + def __ifloordiv__(self, other: Any) -> 'A': return NotImplemented + def __ilshift__(self, other: Any) -> 'A': return NotImplemented + # def __int__(self) -> 'A': return NotImplemented + # def __imatmul__(self, other: Any) -> 'A': return NotImplemented + # def __imod__(self, other: Any) -> 'A': return NotImplemented + def __imul__(self, other: Any) -> 'A': return NotImplemented + # def __index__(self) -> 'A': return NotImplemented + # def __inv__(self) -> 'A': return NotImplemented + # def __invert__(self) -> 'A': return NotImplemented + def __ior__(self, other: Any) -> 'A': return NotImplemented + def __ipow__(self, other: Any) -> 'A': return NotImplemented + def __irshift__(self, other: Any) -> 'A': return NotImplemented + def __isub__(self, other: Any) -> 'A': return NotImplemented + def __itruediv__(self, other: Any) -> 'A': return NotImplemented + def __ixor__(self, other: Any) -> 'A': return NotImplemented + def __le__(self, other: Any) -> bool: return NotImplemented + def __lshift__(self, other: Any) -> 'A': return NotImplemented + def __lt__(self, other: Any) -> bool: return NotImplemented + # def __matmul__(self, other: Any) -> 'A': return NotImplemented + # def __mod__(self, other: Any) -> 'A': return NotImplemented + def __mul__(self, other: Any) -> 'A': return NotImplemented + def __ne__(self, other: Any) -> bool: return NotImplemented + # def __neg__(self) -> 'A': return NotImplemented + # def __not__(self) -> 'A': return NotImplemented + def __or__(self, other: Any) -> 'A': return NotImplemented + # def __pos__(self) -> 'A': return NotImplemented + def __pow__(self, other: Any) -> 'A': return NotImplemented + def __rshift__(self, other: Any) -> 'A': return NotImplemented + def __setitem__(self, other: Any) -> 'A': return NotImplemented + def __sub__(self, other: Any) -> 'A': return NotImplemented + def __truediv__(self, other: Any) -> 'A': return NotImplemented + def __xor__(self, other: Any) -> 'A': return NotImplemented + # # The "reverse" methods: + def __radd__(self, other: Any) -> 'A': return NotImplemented + def __rsub__(self, other: Any) -> 'A': return NotImplemented + def __rmul__(self, other: Any) -> 'A': return NotImplemented + def __rmatmul__(self, other: Any) -> 'A': return NotImplemented + def __rtruediv__(self, other: Any) -> 'A': return NotImplemented + def __rfloordiv__(self, other: Any) -> 'A': return NotImplemented + def __rmod__(self, other: Any) -> 'A': return NotImplemented + # def __rdivmod__(self, other: Any) -> 'A': return NotImplemented + def __rpow__(self, other: Any) -> 'A': return NotImplemented + def __rlshift__(self, other: Any) -> 'A': return NotImplemented + def __rrshift__(self, other: Any) -> 'A': return NotImplemented + def __rand__(self, other: Any) -> 'A': return NotImplemented + def __rxor__(self, other: Any) -> 'A': return NotImplemented + def __ror__(self, other: Any) -> 'A': return NotImplemented +[builtins fixtures/notimplemented.pyi] +[out] + [case testReturnAnyForNotImplementedInNormalMethods] # flags: --warn-return-any class A: From 7ce54883957f30cedc180009e04878b0abfe1930 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 27 Mar 2018 07:56:42 -0700 Subject: [PATCH 6/9] Refactored sets of magic methods to make it more obvious what changes I had made. (Also removed a few more dups) --- mypy/sharedparse.py | 85 +++++++++++++++--------------- test-data/unit/check-warnings.test | 4 +- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 29aa595a73b9..478f902714a1 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -2,8 +2,46 @@ """Shared logic between our three mypy parser files.""" +MAGIC_METHODS_ALLOWING_KWARGS = frozenset([ + "__init__", + "__init_subclass__", + "__new__", + "__call__", +]) + + +_MAGIC_NONBINARY_METHODS = frozenset([ + "__abs__", + "__complex__", + "__del__", + "__delattr__", + "__enter__", + "__exit__", + "__float__", + "__getattr__", + "__getattribute__", + "__hex__", + "__index__", + "__int__", + "__inv__", + "__invert__", + "__iter__", + "__len__", + "__long__", + "__neg__", + "__nonzero__", + "__not__", + "__oct__", + "__pos__", + "__repr__", + "__reversed__", + "__setattr__", + "__str__", + "__unicode__", +]) + -BINARY_MAGIC_METHODS = { +BINARY_MAGIC_METHODS = frozenset([ "__add__", "__and__", "__cmp__", @@ -61,49 +99,10 @@ "__sub__", "__truediv__", "__xor__", -} +]) - -MAGIC_METHODS = { - "__abs__", - "__call__", - "__complex__", - "__del__", - "__delattr__", - "__enter__", - "__exit__", - "__float__", - "__getattr__", - "__getattribute__", - "__hex__", - "__index__", - "__init__", - "__init_subclass__", - "__int__", - "__inv__", - "__invert__", - "__iter__", - "__len__", - "__long__", - "__neg__", - "__new__", - "__nonzero__", - "__not__", - "__oct__", - "__pos__", - "__repr__", - "__reversed__", - "__setattr__", - "__str__", - "__unicode__", -}.union(BINARY_MAGIC_METHODS) - -MAGIC_METHODS_ALLOWING_KWARGS = { - "__init__", - "__init_subclass__", - "__new__", - "__call__", -} +MAGIC_METHODS = (_MAGIC_NONBINARY_METHODS | BINARY_MAGIC_METHODS | + MAGIC_METHODS_ALLOWING_KWARGS) MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 6c56b67ecf60..d0611ace9314 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -168,7 +168,7 @@ class A: return NotImplemented # TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", # mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", -# which gives a run-time error (issue #4767) +# which gives a run-time error (issue #4791) [builtins fixtures/notimplemented.pyi] [out] @@ -184,7 +184,7 @@ class A: # def __concat__(self, other: Any) -> 'A': return NotImplemented # def __contains__(self, other: Any) -> 'A': return NotImplemented # def __call__(self, *args, **kwargs) -> Any: return NotImplemented # <====== crashes - # def __del__(self) -> None: return NotImplemented # Returning Any fromfunction declared to return "None" + # def __del__(self) -> None: return NotImplemented # Returning Any from function declared to return "None" def __delitem__(self, other: Any) -> 'A': return NotImplemented def __eq__(self, other: Any) -> bool: return NotImplemented # def __float__(self) -> 'A': return NotImplemented From 61760310cf2c6dfec08cf5d27f3f7cee84958241 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 27 Mar 2018 12:29:04 -0700 Subject: [PATCH 7/9] revert sharedparse.py and add only __ne__; comment out tests that fail --- mypy/sharedparse.py | 98 ++++++++++++++++++++---------- test-data/unit/check-warnings.test | 16 ++--- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py index 478f902714a1..b186da088f2b 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -2,78 +2,120 @@ """Shared logic between our three mypy parser files.""" -MAGIC_METHODS_ALLOWING_KWARGS = frozenset([ - "__init__", - "__init_subclass__", - "__new__", - "__call__", -]) - -_MAGIC_NONBINARY_METHODS = frozenset([ +MAGIC_METHODS = { "__abs__", + "__add__", + "__and__", + "__call__", + "__cmp__", "__complex__", + "__contains__", "__del__", "__delattr__", + "__delitem__", + "__divmod__", + "__div__", "__enter__", "__exit__", + "__eq__", + "__floordiv__", "__float__", + "__ge__", "__getattr__", "__getattribute__", + "__getitem__", + "__gt__", "__hex__", - "__index__", + "__iadd__", + "__iand__", + "__idiv__", + "__ifloordiv__", + "__ilshift__", + "__imod__", + "__imul__", + "__init__", + "__init_subclass__", "__int__", - "__inv__", "__invert__", + "__ior__", + "__ipow__", + "__irshift__", + "__isub__", "__iter__", + "__ixor__", + "__le__", "__len__", "__long__", + "__lshift__", + "__lt__", + "__mod__", + "__mul__", + "__ne__", "__neg__", + "__new__", "__nonzero__", - "__not__", "__oct__", + "__or__", "__pos__", + "__pow__", + "__radd__", + "__rand__", + "__rdiv__", "__repr__", "__reversed__", + "__rfloordiv__", + "__rlshift__", + "__rmod__", + "__rmul__", + "__ror__", + "__rpow__", + "__rrshift__", + "__rshift__", + "__rsub__", + "__rxor__", "__setattr__", + "__setitem__", "__str__", + "__sub__", "__unicode__", -]) + "__xor__", +} +MAGIC_METHODS_ALLOWING_KWARGS = { + "__init__", + "__init_subclass__", + "__new__", + "__call__", +} + +MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS -BINARY_MAGIC_METHODS = frozenset([ +BINARY_MAGIC_METHODS = { "__add__", "__and__", "__cmp__", - "__concat__" - "__contains__", - "__delitem__", - "__div__", "__divmod__", + "__div__", "__eq__", "__floordiv__", "__ge__", - "__getitem__", "__gt__", "__iadd__", "__iand__", - "__iconcat__", "__idiv__", "__ifloordiv__", "__ilshift__", - "__imatmul__" "__imod__", "__imul__", "__ior__", "__ipow__", "__irshift__", "__isub__", - "__itruediv__", "__ixor__", "__le__", "__lshift__", "__lt__", - "__matmul__" "__mod__", "__mul__", "__ne__", @@ -82,10 +124,8 @@ "__radd__", "__rand__", "__rdiv__", - "__rdivmod__", "__rfloordiv__", "__rlshift__", - "__rmatmul__", "__rmod__", "__rmul__", "__ror__", @@ -93,18 +133,10 @@ "__rrshift__", "__rshift__", "__rsub__", - "__rtruediv__", "__rxor__", - "__setitem__", "__sub__", - "__truediv__", "__xor__", -]) - -MAGIC_METHODS = (_MAGIC_NONBINARY_METHODS | BINARY_MAGIC_METHODS | - MAGIC_METHODS_ALLOWING_KWARGS) - -MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS +} def special_function_elide_names(name: str) -> bool: diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index d0611ace9314..942865fb11c1 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -185,16 +185,16 @@ class A: # def __contains__(self, other: Any) -> 'A': return NotImplemented # def __call__(self, *args, **kwargs) -> Any: return NotImplemented # <====== crashes # def __del__(self) -> None: return NotImplemented # Returning Any from function declared to return "None" - def __delitem__(self, other: Any) -> 'A': return NotImplemented + # def __delitem__(self, other: Any) -> 'A': return NotImplemented def __eq__(self, other: Any) -> bool: return NotImplemented # def __float__(self) -> 'A': return NotImplemented def __floordiv__(self, other: Any) -> 'A': return NotImplemented def __ge__(self, other: Any) -> bool: return NotImplemented - def __getitem__(self, other: Any) -> 'A': return NotImplemented + # def __getitem__(self, other: Any) -> 'A': return NotImplemented def __gt__(self, other: Any) -> bool: return NotImplemented def __iadd__(self, other: Any) -> 'A': return NotImplemented def __iand__(self, other: Any) -> 'A': return NotImplemented - def __iconcat__(self, other: Any) -> 'A': return NotImplemented + # def __iconcat__(self, other: Any) -> 'A': return NotImplemented def __ifloordiv__(self, other: Any) -> 'A': return NotImplemented def __ilshift__(self, other: Any) -> 'A': return NotImplemented # def __int__(self) -> 'A': return NotImplemented @@ -208,7 +208,7 @@ class A: def __ipow__(self, other: Any) -> 'A': return NotImplemented def __irshift__(self, other: Any) -> 'A': return NotImplemented def __isub__(self, other: Any) -> 'A': return NotImplemented - def __itruediv__(self, other: Any) -> 'A': return NotImplemented + # def __itruediv__(self, other: Any) -> 'A': return NotImplemented def __ixor__(self, other: Any) -> 'A': return NotImplemented def __le__(self, other: Any) -> bool: return NotImplemented def __lshift__(self, other: Any) -> 'A': return NotImplemented @@ -223,16 +223,16 @@ class A: # def __pos__(self) -> 'A': return NotImplemented def __pow__(self, other: Any) -> 'A': return NotImplemented def __rshift__(self, other: Any) -> 'A': return NotImplemented - def __setitem__(self, other: Any) -> 'A': return NotImplemented + # def __setitem__(self, other: Any) -> 'A': return NotImplemented def __sub__(self, other: Any) -> 'A': return NotImplemented - def __truediv__(self, other: Any) -> 'A': return NotImplemented + # def __truediv__(self, other: Any) -> 'A': return NotImplemented def __xor__(self, other: Any) -> 'A': return NotImplemented # # The "reverse" methods: def __radd__(self, other: Any) -> 'A': return NotImplemented def __rsub__(self, other: Any) -> 'A': return NotImplemented def __rmul__(self, other: Any) -> 'A': return NotImplemented - def __rmatmul__(self, other: Any) -> 'A': return NotImplemented - def __rtruediv__(self, other: Any) -> 'A': return NotImplemented + # def __rmatmul__(self, other: Any) -> 'A': return NotImplemented + # def __rtruediv__(self, other: Any) -> 'A': return NotImplemented def __rfloordiv__(self, other: Any) -> 'A': return NotImplemented def __rmod__(self, other: Any) -> 'A': return NotImplemented # def __rdivmod__(self, other: Any) -> 'A': return NotImplemented From e969ca2cefbfc3b9143ac63c0fc050932b86e649 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Tue, 27 Mar 2018 15:52:31 -0700 Subject: [PATCH 8/9] Removed commented-out tests that currently fail (they'll be dealt with separately). --- test-data/unit/check-warnings.test | 37 ++---------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 942865fb11c1..358d74132876 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -159,16 +159,12 @@ class A: def __ge__(self, other: Any) -> bool: return NotImplemented def __ne__(self, other: Any) -> bool: # The logic here is similar to __eq__, except the - # "return" is inside the if-then-else. The intention - # is to also verify that both ways of inferring - # Union[bool, type(NotImplemented)] are checked. + # "return" is inside the if-then-else, to verify both + # ways of inferring Union[bool, type(NotImplemented)]. if self.__class__ is other.__class__: return bool(self.x != other.x) else: return NotImplemented -# TODO: If the programmer specifies "-> Union[bool, type(NotImplemented)]", -# mypy suggests changing this to "-> Union[bool, type[NotImplemented]]", -# which gives a run-time error (issue #4791) [builtins fixtures/notimplemented.pyi] [out] @@ -176,66 +172,37 @@ class A: # flags: --warn-return-any from typing import Any class A: - # The commented-out items give a warning: Returning Any from function declared to return "A" - # def __abs__(self) -> 'A': return NotImplemented def __add__(self, other: Any) -> 'A': return NotImplemented def __and__(self, other: Any) -> 'A': return NotImplemented - # def __complex__(self) -> 'A': return NotImplemented - # def __concat__(self, other: Any) -> 'A': return NotImplemented - # def __contains__(self, other: Any) -> 'A': return NotImplemented - # def __call__(self, *args, **kwargs) -> Any: return NotImplemented # <====== crashes - # def __del__(self) -> None: return NotImplemented # Returning Any from function declared to return "None" - # def __delitem__(self, other: Any) -> 'A': return NotImplemented def __eq__(self, other: Any) -> bool: return NotImplemented - # def __float__(self) -> 'A': return NotImplemented def __floordiv__(self, other: Any) -> 'A': return NotImplemented def __ge__(self, other: Any) -> bool: return NotImplemented - # def __getitem__(self, other: Any) -> 'A': return NotImplemented def __gt__(self, other: Any) -> bool: return NotImplemented def __iadd__(self, other: Any) -> 'A': return NotImplemented def __iand__(self, other: Any) -> 'A': return NotImplemented - # def __iconcat__(self, other: Any) -> 'A': return NotImplemented def __ifloordiv__(self, other: Any) -> 'A': return NotImplemented def __ilshift__(self, other: Any) -> 'A': return NotImplemented - # def __int__(self) -> 'A': return NotImplemented - # def __imatmul__(self, other: Any) -> 'A': return NotImplemented - # def __imod__(self, other: Any) -> 'A': return NotImplemented def __imul__(self, other: Any) -> 'A': return NotImplemented - # def __index__(self) -> 'A': return NotImplemented - # def __inv__(self) -> 'A': return NotImplemented - # def __invert__(self) -> 'A': return NotImplemented def __ior__(self, other: Any) -> 'A': return NotImplemented def __ipow__(self, other: Any) -> 'A': return NotImplemented def __irshift__(self, other: Any) -> 'A': return NotImplemented def __isub__(self, other: Any) -> 'A': return NotImplemented - # def __itruediv__(self, other: Any) -> 'A': return NotImplemented def __ixor__(self, other: Any) -> 'A': return NotImplemented def __le__(self, other: Any) -> bool: return NotImplemented def __lshift__(self, other: Any) -> 'A': return NotImplemented def __lt__(self, other: Any) -> bool: return NotImplemented - # def __matmul__(self, other: Any) -> 'A': return NotImplemented - # def __mod__(self, other: Any) -> 'A': return NotImplemented def __mul__(self, other: Any) -> 'A': return NotImplemented def __ne__(self, other: Any) -> bool: return NotImplemented - # def __neg__(self) -> 'A': return NotImplemented - # def __not__(self) -> 'A': return NotImplemented def __or__(self, other: Any) -> 'A': return NotImplemented - # def __pos__(self) -> 'A': return NotImplemented def __pow__(self, other: Any) -> 'A': return NotImplemented def __rshift__(self, other: Any) -> 'A': return NotImplemented - # def __setitem__(self, other: Any) -> 'A': return NotImplemented def __sub__(self, other: Any) -> 'A': return NotImplemented - # def __truediv__(self, other: Any) -> 'A': return NotImplemented def __xor__(self, other: Any) -> 'A': return NotImplemented - # # The "reverse" methods: def __radd__(self, other: Any) -> 'A': return NotImplemented def __rsub__(self, other: Any) -> 'A': return NotImplemented def __rmul__(self, other: Any) -> 'A': return NotImplemented - # def __rmatmul__(self, other: Any) -> 'A': return NotImplemented - # def __rtruediv__(self, other: Any) -> 'A': return NotImplemented def __rfloordiv__(self, other: Any) -> 'A': return NotImplemented def __rmod__(self, other: Any) -> 'A': return NotImplemented - # def __rdivmod__(self, other: Any) -> 'A': return NotImplemented def __rpow__(self, other: Any) -> 'A': return NotImplemented def __rlshift__(self, other: Any) -> 'A': return NotImplemented def __rrshift__(self, other: Any) -> 'A': return NotImplemented From 67b979f3fbf3af0e5aec40e1e34a6eefaf83af13 Mon Sep 17 00:00:00 2001 From: Peter Ludemann Date: Wed, 28 Mar 2018 14:43:42 -0700 Subject: [PATCH 9/9] remove extra tests; these will be handled separately --- test-data/unit/check-warnings.test | 64 +--------------------- test-data/unit/fixtures/notimplemented.pyi | 4 +- 2 files changed, 2 insertions(+), 66 deletions(-) diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 358d74132876..d812ed83efa8 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -145,70 +145,8 @@ main:4: warning: Returning Any from function declared to return "int" [case testReturnAnyForNotImplementedInBinaryMagicMethods] # flags: --warn-return-any -from typing import Any -class A: - def __init__(self, x: Any) -> None: - self.x = x - def __eq__(self, other: Any) -> bool: - if self.__class__ is other.__class__: - return bool(self.x == other.x) - return NotImplemented - def __lt__(self, other: Any) -> bool: return NotImplemented - def __gt__(self, other: Any) -> bool: return NotImplemented - def __le__(self, other: Any) -> bool: return NotImplemented - def __ge__(self, other: Any) -> bool: return NotImplemented - def __ne__(self, other: Any) -> bool: - # The logic here is similar to __eq__, except the - # "return" is inside the if-then-else, to verify both - # ways of inferring Union[bool, type(NotImplemented)]. - if self.__class__ is other.__class__: - return bool(self.x != other.x) - else: - return NotImplemented -[builtins fixtures/notimplemented.pyi] -[out] - -[case testReturnNotImplementedAllMagicMethods] -# flags: --warn-return-any -from typing import Any class A: - def __add__(self, other: Any) -> 'A': return NotImplemented - def __and__(self, other: Any) -> 'A': return NotImplemented - def __eq__(self, other: Any) -> bool: return NotImplemented - def __floordiv__(self, other: Any) -> 'A': return NotImplemented - def __ge__(self, other: Any) -> bool: return NotImplemented - def __gt__(self, other: Any) -> bool: return NotImplemented - def __iadd__(self, other: Any) -> 'A': return NotImplemented - def __iand__(self, other: Any) -> 'A': return NotImplemented - def __ifloordiv__(self, other: Any) -> 'A': return NotImplemented - def __ilshift__(self, other: Any) -> 'A': return NotImplemented - def __imul__(self, other: Any) -> 'A': return NotImplemented - def __ior__(self, other: Any) -> 'A': return NotImplemented - def __ipow__(self, other: Any) -> 'A': return NotImplemented - def __irshift__(self, other: Any) -> 'A': return NotImplemented - def __isub__(self, other: Any) -> 'A': return NotImplemented - def __ixor__(self, other: Any) -> 'A': return NotImplemented - def __le__(self, other: Any) -> bool: return NotImplemented - def __lshift__(self, other: Any) -> 'A': return NotImplemented - def __lt__(self, other: Any) -> bool: return NotImplemented - def __mul__(self, other: Any) -> 'A': return NotImplemented - def __ne__(self, other: Any) -> bool: return NotImplemented - def __or__(self, other: Any) -> 'A': return NotImplemented - def __pow__(self, other: Any) -> 'A': return NotImplemented - def __rshift__(self, other: Any) -> 'A': return NotImplemented - def __sub__(self, other: Any) -> 'A': return NotImplemented - def __xor__(self, other: Any) -> 'A': return NotImplemented - def __radd__(self, other: Any) -> 'A': return NotImplemented - def __rsub__(self, other: Any) -> 'A': return NotImplemented - def __rmul__(self, other: Any) -> 'A': return NotImplemented - def __rfloordiv__(self, other: Any) -> 'A': return NotImplemented - def __rmod__(self, other: Any) -> 'A': return NotImplemented - def __rpow__(self, other: Any) -> 'A': return NotImplemented - def __rlshift__(self, other: Any) -> 'A': return NotImplemented - def __rrshift__(self, other: Any) -> 'A': return NotImplemented - def __rand__(self, other: Any) -> 'A': return NotImplemented - def __rxor__(self, other: Any) -> 'A': return NotImplemented - def __ror__(self, other: Any) -> 'A': return NotImplemented + def __eq__(self, other: object) -> bool: return NotImplemented [builtins fixtures/notimplemented.pyi] [out] diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index 9d6e6cebbb56..e619a6c5ad85 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -3,13 +3,11 @@ from typing import Any, cast class object: - __class__ = None def __init__(self) -> None: pass class type: pass class function: pass -class bool: - def __init__(self, o: Any) -> None: pass +class bool: pass class int: pass class str: pass NotImplemented = cast(Any, None)