decorator: int = 3
@decorator # no error
class A:
pass
class B:
pass
NewB = decorator(B) # E: "int" not callable
Mypy doesn't appear to do any checking of class decorators. Applying the decorator manually works.
Guido and I found out about this in https://github.com/python/typeshed/pull/1136#discussion_r109725629. Looks like this can be fixed somewhere around analyze_class_decorator in semanal.py.
A use case reported in #3483
from typing import Type
class A:
pass
def deco(cls) -> Type[A]:
return A
@deco
class B:
pass
def func(cls: Type[A]):
assert cls is A
func(B)
might be non-trivial to implement. I am adding this example here, so that we will not forget about it.
Another possibly easier to solve problem is that the functions called are not type-checked
from typing import TypeVar, Callable
_C = TypeVar('_C', bound=type)
def blah(x:int=17) -> Callable[[_C], _C]: ...
@blah(y=18)
class A:
pass
blah(y=18)
Or even more fun
x=17
@x(huh=17)
class B:
pass
x(huh=17)
I'm thinking how this could/should be used in combination with Protocols to deal with class decorators adding some methods to a class
class FooBar(Protocol):
def bar(self) -> int:
return 1
T = TypeVar("T", bound=Type)
def add_bar(cls: T) -> Intersection[FooBar, T]:
def bar(self) -> int:
return 2
cls.bar = bar
@add_bar
class Foo: pass
Foo().bar()
Now this depends on Intersection which was described in PEP483, but not implemented; I don't see another way of doing this cleanly right now.
Most helpful comment
I'm thinking how this could/should be used in combination with
Protocols to deal with class decorators adding some methods to a classNow this depends on
Intersectionwhich was described in PEP483, but not implemented; I don't see another way of doing this cleanly right now.