Mypy: Class decorators are not type checked

Created on 5 Apr 2017  路  4Comments  路  Source: python/mypy

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.

bug priority-1-normal

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 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.

All 4 comments

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.

Was this page helpful?
0 / 5 - 0 ratings