How come that under Mypy, __subclasshook__
works for one-trick ponies from collections.abc
, but not for user-defined classes?
For instance, this program
from collections.abc import Hashable
class A:
def __hash__(self) -> int:
return 0
a: Hashable = A()
outputs
$ mypy demo.py --strict
Success: no issues found in 1 source file
But this equivalent program
from abc import ABCMeta, abstractmethod
def _check_methods(C: type, *methods: str) -> bool:
mro = C.__mro__
for method in methods:
for B in mro:
if method in B.__dict__:
if B.__dict__[method] is None:
return NotImplemented
break
else:
return NotImplemented
return True
class Hashable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __hash__(self) -> int:
return 0
@classmethod
def __subclasshook__(cls, C: type) -> bool:
if cls is Hashable:
return _check_methods(C, "__hash__")
return NotImplemented
class A:
def __hash__(self) -> int:
return 0
a: Hashable = A()
outputs
$ mypy demo.py --strict
demo.py:32: error: Incompatible types in assignment (expression has type "A", variable has type "Hashable")
Found 1 error in 1 file (checked 1 source file)
Does Mypy handle one-trick ponies in a special way?
Mypy does not use the implementations of the standard library but its specifications (’stub files’) from the typeshed package. In this package,
collections.abc.Hashable
is atyping.Protocol
.typeshed/stdlib/_collections_abc.pyi:
typeshed/stdlib/typing.pyi: