Skip to content

Add missing magic methods. #7154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions mypy/sharedparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,52 @@

_NON_BINARY_MAGIC_METHODS = {
"__abs__",
"__bool__"
"__call__",
"__ceil__",
"__complex__",
"__contains__",
"__del__",
"__delattr__",
"__delete__",
"__delitem__",
"__dir__",
"__enter__",
"__exit__",
"__float__",
"__floor__",
# "__get__", # TODO: breaks testAccessingGenericDescriptorFromClassBadOverload
"__getattr__",
"__getattribute__",
"__getitem__",
"__hash__",
"__hex__",
"__index__",
"__init__",
"__init_subclass__",
"__instancecheck__",
"__int__",
"__invert__",
"__iter__",
"__len__",
"__length_hint__",
"__long__",
"__missing__",
"__neg__",
"__new__",
# "__not__", # Not magic -- `not` calls `__bool__` or `__len__`.
"__nonzero__",
"__oct__",
"__pos__",
"__repr__",
"__reversed__",
"__round__",
"__set__",
"__set_name__",
"__setattr__",
"__setitem__",
"__str__",
"__subclasscheck__",
"__trunc__",
"__unicode__",
} # type: Final

Expand All @@ -50,14 +66,17 @@
"__add__",
"__and__",
"__cmp__",
"__divmod__",
"__concat__",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like a special method.

"__contains__",
"__div__",
"__divmod__",
"__eq__",
"__floordiv__",
"__ge__",
"__gt__",
"__iadd__",
"__iand__",
"__iconcat__",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, this doesn't seem to be special.

"__idiv__",
"__ifloordiv__",
"__ilshift__",
Expand All @@ -82,6 +101,7 @@
"__radd__",
"__rand__",
"__rdiv__",
"__rdivmod__",
"__rfloordiv__",
"__rlshift__",
"__rmatmul__",
Expand Down
84 changes: 84 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -6142,6 +6142,90 @@ class B:
class C(A, B): pass
[out]

[case testBinaryMagicMethods]
# flags: --warn-return-any
# See also check-warnings.test [case testReturnAnyForNotImplementedInBinaryMagicMethods]
from typing import Any
class A:
def __init__(self, x: Any) -> None:
self.x = x
def __abs__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __bool__(self) -> bool: return NotImplemented # E: Returning Any from function declared to return "bool"
def __call__(self, *args, **kwargs) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __ceil__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __complex__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __del__(self) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __delattr__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __delete__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __delitem__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __dir__(self, other: Any) -> Sequence_str: return NotImplemented # E: Returning Any from function declared to return "Sequence_str"
def __enter__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __exit__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"
def __float__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __floor__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __get__(self, instance: Any, other: Any) -> str: return NotImplemented # E: Returning Any from function declared to return "str"
def __getitem__(self, other: Any) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __int__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __index__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __inv__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I think that this isn't a magic method (but __invert__ is)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.python.org/3/library/operator.html
seems to show __inv__ and __invert__ as synonyms.
But by experiment, __inv__ doesn't work (either in Python2.7 or Python3.x).

So, is this a bug in the documentation or is there some use of __inv__ that I'm not aware of?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like __inv__ is specific to the operator module, so it doesn't need any special casing.

def __invert__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __neg__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __not__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __pos__(self) -> 'A': return NotImplemented # E: Returning Any from function declared to return "A"
def __setitem__(self, other: Any) -> None: return NotImplemented # E: Returning Any from function declared to return "None"

def __add__(self, other: Any) -> 'A': return NotImplemented
def __and__(self, other: Any) -> 'A': return NotImplemented
def __concat__(self, other: Any) -> 'A': return NotImplemented
def __contains__(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 __iconcat__(self, other: Any) -> 'A': return NotImplemented
def __ifloordiv__(self, other: Any) -> 'A': return NotImplemented
def __ilshift__(self, other: Any) -> '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 __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 __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 __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]

[case testAttributeDefOrder1]
import a

Expand Down
10 changes: 9 additions & 1 deletion test-data/unit/fixtures/notimplemented.pyi
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
# builtins stub used in NotImplemented related cases.
from typing import Any, cast
from typing import Any, Iterable, Mapping, Generic, TypeVar, cast

Tco = TypeVar('Tco', covariant=True)
T = TypeVar('T')
S = TypeVar('S')

class object:
def __init__(self) -> None: pass
def __eq__(self, o: object) -> bool: pass
def __ne__(self, o: object) -> bool: pass

class dict(Iterable[T], Mapping[T, S], Generic[T, S]): pass
class tuple(Iterable[Tco], Generic[Tco]): pass
class type: pass
class function: pass
class bool: pass
class int: pass
class str: pass
class Sequence_str: pass # Sequence[str] for testing __dir__
NotImplemented = cast(Any, None)