admin管理员组

文章数量:1318770

I want to define a decorator as a class to decorate another class's methods. However, when __init__() constructor is decorated with that class, mypy reports Unsupported decorated constructor type error.

Is there any error in my code? Or, is this a bug in mypy?

from types import MethodType
from typing import TypeVar, Callable, Type, ParamSpec, Generic

T = TypeVar("T")
P = ParamSpec("P")
R = TypeVar("R")

class method_decorator(Generic[P, R]):
    def __init__(self, method: Callable[P, R]):
        self.method = method

    def __get__(self, instance: T, cls: Type[T]) -> Callable[..., R]:
        return MethodType(self, instance)

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.method(*args, **kwargs)

class Foo:
    @method_decorator # error: Unsupported decorated constructor type  [misc]
    def __init__(self, x: int):
        self.x = x

    @method_decorator
    def mul(self, a: int) -> int:
        return self.x * a

def gen_foo(x: int) -> Foo:
    return Foo(x) # error: Returning Any from function declared to return "Foo"  [no-any-return]

foo = gen_foo(2)
print(foo.mul(3))

Type check result (with MyPy v1.13.0):

$ mypy --strict test.py
test.py:19: error: Unsupported decorated constructor type  [misc]
test.py:28: error: Returning Any from function declared to return "Foo"  [no-any-return]
Found 2 errors in 1 file (checked 1 source file)

I want to define a decorator as a class to decorate another class's methods. However, when __init__() constructor is decorated with that class, mypy reports Unsupported decorated constructor type error.

Is there any error in my code? Or, is this a bug in mypy?

from types import MethodType
from typing import TypeVar, Callable, Type, ParamSpec, Generic

T = TypeVar("T")
P = ParamSpec("P")
R = TypeVar("R")

class method_decorator(Generic[P, R]):
    def __init__(self, method: Callable[P, R]):
        self.method = method

    def __get__(self, instance: T, cls: Type[T]) -> Callable[..., R]:
        return MethodType(self, instance)

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.method(*args, **kwargs)

class Foo:
    @method_decorator # error: Unsupported decorated constructor type  [misc]
    def __init__(self, x: int):
        self.x = x

    @method_decorator
    def mul(self, a: int) -> int:
        return self.x * a

def gen_foo(x: int) -> Foo:
    return Foo(x) # error: Returning Any from function declared to return "Foo"  [no-any-return]

foo = gen_foo(2)
print(foo.mul(3))

Type check result (with MyPy v1.13.0):

$ mypy --strict test.py
test.py:19: error: Unsupported decorated constructor type  [misc]
test.py:28: error: Returning Any from function declared to return "Foo"  [no-any-return]
Found 2 errors in 1 file (checked 1 source file)
Share Improve this question asked Jan 21 at 11:13 s417-lamas417-lama 934 bronze badges 5
  • Some thoughts on my mind: __init__ needs to return None; I assume the Any is the bound of your TypeVar R. Be aware that instance can also be None in your decorator class; i.e. if you use Foo.__init__. – Daraan Commented Jan 21 at 11:54
  • @Daraan Thank you for your comment. However, reveal_type() for the decorated __init__() method showed Revealed type is "test.method_decorator[[self: test.Foo, x: builtins.int], None]. This suggests that R is correctly bound to None. – s417-lama Commented Jan 21 at 12:00
  • For reveal_type(Foo.__init__) and reveal_type(Foo().__init__) I get two times Any (playground, which is also the reason reveal_type(Foo()) is Any and you get the Returning Any from function declared to return Foo error. With which settings are you getting your output, is it really from mypy? – Daraan Commented Jan 21 at 13:45
  • @Daraan Please put reveal_type(__init__) just below the __init__() definition. – s417-lama Commented Jan 22 at 1:26
  • Ah, however, If you put it inside the class statement, you only get the result of method_decorator.__init__ which is not really useful as this does not yet invoke the descriptor protocol. At all other access points it will be the result of that object's __get__ which is were we need to put the focus that this one is typed like we need. – Daraan Commented Jan 22 at 8:57
Add a comment  | 

1 Answer 1

Reset to default 2

This just isn't supported by mypy, it only understands simple callable decorators on __init__ and __new__ as of 1.14.1. Here's the relevant part:

        if e.func.info and e.func.name in ("__init__", "__new__"):
            if e.type and not isinstance(get_proper_type(e.type), (FunctionLike, AnyType)):
                self.fail(message_registry.BAD_CONSTRUCTOR_TYPE, e)

本文标签: