I’m trying to write a simple metaclass that intercepts every function declaration and replaces it with a dummy function:
from dataclasses import dataclass
from typing import Any, Mapping
@dataclass
class DummyCall:
args: tuple[Any, ...]
kwargs: dict[str, Any]
def _dummy_function(*args: Any, **kwargs: Any) -> DummyCall:
return DummyCall(args, kwargs)
class _dummy_dict(dict[str, Any]):
def __setitem__(self, key: str, value: Any) -> None:
if callable(value):
super().__setitem__(key, _dummy_function)
else:
super().__setitem__(key, value)
class dummy(type):
@classmethod
def __prepare__(metcls, name, bases, **kwds):
return _dummy_dict()
I now want to type-hint the __prepare__
method. I’ve tried the following:
def __prepare__(*_, *__): …
…but of course this doesn’t work. From the error I got I tried to reconstruct the type, and ended up with the following:
def __prepare__(metcls: Any, name: str, bases: tuple[type, ...], **kwds: Any) -> Mapping[str, Any]: …
Unfortunately, this still doesn’t satisfy MyPy. I get the following error:
error: Signature of "__prepare__" incompatible with supertype "type"
note: Superclass:
note: def __prepare__(metacls, str, Tuple[type, ...], **kwds: Any) -> Mapping[str, object]
note: Subclass:
note: @classmethod
note: def __prepare__(metcls, name: str, bases: Tuple[type, ...], **kwds: Any) -> Mapping[str, Any]
I also interestingly get different errors in my code editor with a MyPy plugin:
Signature of "__prepare__" incompatible with supertype "type"mypy
Superclass:mypy
@classmethodmypy
def __prepare__(metacls, str, Tuple[type, ...], **kwds: Any) -> Mapping[str, object]mypy
Subclass:mypy
@classmethodmypy
def __prepare__(metcls, name: str, bases: Tuple[type, ...], **kwds: Any) -> Mapping[str, Any]mypy
Here, it is reported that the superclass definition is annotated with @classmethod
, while the command-line mypy doesn’t say this.
The reason was that I didn’t use the exact same argument names as in the .pyi file.
This works:
@classmethod
def __prepare__(metacls, __name: str, __bases: tuple[type, ...], **kwds: Any) -> Mapping[str, object]:
…
Interestingly, the type doesn’t need to match exactly. I was able to use tuple
instead of Tuple
in the typestub.