I am trying to type the __new__
method in a metaclass in Python so that it pleases mypy. The code would be something like this (taken from pep-3115 - "Metaclasses in Python 3000" and stripped down a bit):
from __future__ import annotations
from typing import Type
# The metaclass
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> type:
result = type.__new__(cls, name, bases, classdict)
print('in __new__')
return result
class MyClass(metaclass=MetaClass):
pass
With this, mypy complains, Incompatible return type for "__new__" (returns "type", but must return a subtype of "MetaClass")
, pointing at the line def __new__
.
I have also tried with:
def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
Then mypy complains (about the return result
line): Incompatible return value type (got "type", expected "MetaClass")
.
I have also tried with a type var (TSubMetaclass = TypeVar('TSubMetaclass', bound='MetaClass')
) and the result is the same as using MetaClass
.
Using super().__new__
instead of type.__new__
gave similar results.
What would be the correct way to do it?
First, the return type is MetaClass
, not type
. Second, you need to explicitly cast the return value, since type.__new__
doesn't know it is returning an instance of MetaClass
. (Its specific return type is determined by its first argument, which isn't known statically.)
from __future__ import annotations
from typing import Type, cast
# The metaclass
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> MetaClass:
result = type.__new__(cls, name, bases, classdict)
print('in __new__')
return cast(MetaClass, result)
class MyClass(metaclass=MetaClass):
pass
To use super
, you need to adjust the static type of the cls
parameter.
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
result = super().__new__(name, bases, classdict)
print('in __new__')
return cast(MetaClass, result)