Skip to content

Commit 91b8610

Browse files
committed
Make None compatible with Hashable (#9371)
Fixes #8768.
1 parent d284d19 commit 91b8610

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

mypy/subtypes.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,15 @@ def visit_any(self, left: AnyType) -> bool:
207207

208208
def visit_none_type(self, left: NoneType) -> bool:
209209
if state.strict_optional:
210-
return (isinstance(self.right, NoneType) or
211-
is_named_instance(self.right, 'builtins.object') or
212-
isinstance(self.right, Instance) and self.right.type.is_protocol and
213-
not self.right.type.protocol_members)
210+
if isinstance(self.right, NoneType) or is_named_instance(self.right,
211+
'builtins.object'):
212+
return True
213+
if isinstance(self.right, Instance) and self.right.type.is_protocol:
214+
members = self.right.type.protocol_members
215+
# None is compatible with Hashable (and other similar protocols). This is
216+
# slightly sloppy since we don't check the signature of "__hash__".
217+
return not members or members == ["__hash__"]
218+
return False
214219
else:
215220
return True
216221

test-data/unit/check-protocols.test

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,3 +2499,40 @@ reveal_type(abs(3)) # N: Revealed type is 'builtins.int*'
24992499
reveal_type(abs(ALL)) # N: Revealed type is 'builtins.int*'
25002500
[builtins fixtures/float.pyi]
25012501
[typing fixtures/typing-full.pyi]
2502+
2503+
[case testProtocolWithSlots]
2504+
from typing import Protocol
2505+
2506+
class A(Protocol):
2507+
__slots__ = ()
2508+
2509+
[builtins fixtures/tuple.pyi]
2510+
2511+
[case testNoneVsProtocol]
2512+
# mypy: strict-optional
2513+
from typing_extensions import Protocol
2514+
2515+
class MyHashable(Protocol):
2516+
def __hash__(self) -> int: ...
2517+
2518+
def f(h: MyHashable) -> None: pass
2519+
f(None)
2520+
2521+
class Proto(Protocol):
2522+
def __hash__(self) -> int: ...
2523+
def method(self) -> None: ...
2524+
2525+
def g(h: Proto) -> None: pass
2526+
g(None) # E: Argument 1 to "g" has incompatible type "None"; expected "Proto"
2527+
2528+
class Proto2(Protocol):
2529+
def hash(self) -> None: ...
2530+
2531+
def h(h: Proto2) -> None: pass
2532+
h(None) # E: Argument 1 to "h" has incompatible type "None"; expected "Proto2"
2533+
2534+
class EmptyProto(Protocol): ...
2535+
2536+
def hh(h: EmptyProto) -> None: pass
2537+
hh(None)
2538+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)