I want to define a typing protocol for a custom mapping class. This class needs to be very similar to a MutableMapping, except that it has a couple of additional methods beyond those those that collections.abc.MutableMapping defines as abstract methods (specifically .copy()), and I also want to specify in advance which types any implementations of the custom mapping class must use as keys and values.
After reading through PEP 544 I thought I should be able to do this:
from typing import Hashable, MutableMapping, Protocol, TypeVar
TVarMapProt = TypeVar("TVarMapProt", bound="VariableMappingProtocol")
class VariableMappingProtocol(MutableMapping[Hashable, int], Protocol):
"""
Protocol for the type behaviour of a class which
acts basically like a dict of int objects.
"""
def copy(self: TVarMapProt) -> TVarMapProt:
# TODO replace return type with Self if PEP 673 goes through
...
The idea being that in my code I can state that a type VariableMappingProtocol is expected, and then the user would have to use their own class that was defined like this in order to avoid a typing error:
TCusVarMap = TypeVar("CusVarMap", bound="CustomVariableMapping")
class CustomVariableMapping
"""Defines all the methods that a MutableMapping does, but mapping Hashables to ints"""
def __getitem__(self, key: Hashable) -> int:
# implementation goes here
...
# etc. for __setitem__, and so on...
def copy(self) -> TCusVarMap:
# implementation goes here
...
The problem is that when I run mypy on the code defining VariableMappingProtocol I get this error:
test.py:7: error: All bases of a protocol must be protocols
Found 1 error in 1 file (checked 1 source file)
If I delete MutableMapping so that VariableMappingProtocol only inherits from Protocol then the error goes away. But then I'm not getting the structural typing I want for all the abstract methods from MutableMapping.
So it seems the problem is that typing.MutableMapping is not considered a Protocol? But that's weird, especially as I can treat some of the other types in typing as Protocols, e.g. this example (from PEP 544)
from typing import Protocol, Sized
class SizedProtocol(Sized, Protocol):
"""
Protocol for the type behaviour of a class which has a __len__ method.
"""
...
which doesn't throw any mypy errors.
How can I inherit from MutableMapping as a Protocol, and thus avoid the need to write out all the methods of MutableMapping in my VariableMappingProtocol definition?