diff --git a/mypy/checker.py b/mypy/checker.py index b2a444721604..317d5bfeadf2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4470,6 +4470,14 @@ def infer_variable_type( is_lvalue_final=name.is_final, is_lvalue_member=isinstance(lvalue, MemberExpr), ) + and not ( + # Trust None assignments to dunder methods + # This is a bit ad-hoc, but it improves protocol + # (non-)assignability, for instance `__hash__ = None` + self.scope.active_class() + and is_dunder(name.name) + and isinstance(get_proper_type(init_type), NoneType) + ) and not self.no_partial_types ): # We cannot use the type of the initialization expression for full type diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 5dc10b9736c4..7d173bc67dbe 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2939,7 +2939,25 @@ class Gleemer: [case testPartialTypeProtocolHashable] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types +from typing import Protocol + +class Hashable(Protocol): + def __hash__(self) -> int: ... + +class ObjectHashable: + def __hash__(self) -> int: ... + +class DataArray(ObjectHashable): + __hash__ = None + + def f(self, x: Hashable) -> None: + reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" +[builtins fixtures/tuple.pyi] + + +[case testPartialTypeProtocolHashableLocalPartialTypes] +# flags: --no-strict-optional --local-partial-types from typing import Protocol class Hashable(Protocol):