Property, classmethod and staticmethod aliases not supported
MyPy does not support property aliases. Example:
$ cat a.py
class A:
@property
def f(self) -> int:
return 1
g = f
x: int = A().f
y: int = A().g
$ mypy a.py
a.py:8: error: Incompatible types in assignment (expression has type "Callable[[], int]", variable has type "int")
Thanks for reporting!
Yeah, unfortunately properties are known to be often problematic. They were implemented via some special-casing before a more general descriptor support was added.
The problem is not only with properties, but with static and class methods too.
$ cat b.py
class A:
@classmethod
def f(cls) -> int:
return 1
g = f
x: int = A().f()
y: int = A().g()
$ mypy b.py
b.py:8: error: Invalid self argument "A" to attribute function "g" with type "Callable[[Type[A]], int]"
$ cat c.py
class A:
@staticmethod
def f() -> int:
return 1
g = f
x: int = A().f()
y: int = A().g()
$ mypy c.py
c.py:8: error: Attribute function "g" with type "Callable[[], int]" does not accept self argument
Hm, this is more serious than I though then, raising priority to high.
And apparently with callable properties on a dataclass (though the same errors are returned for a class with callable properties, which do not receive a self passed in when they are called):
from typing import Callable
from dataclasses import dataclass
@dataclass
class Demo:
call_this: Callable[[str], str]
def call(s: str) -> str:
return s
demo = Demo(call_this=call)
demo.call_this("test")
When run through mypy, this gets:
demo.py:13: error: Invalid self argument "Demo" to attribute function "call_this" with type "Callable[[str], str]"
demo.py:13: error: Too many arguments
Currently, this issue forces rather awkward typing for @classmethods, as we can't even omit the typing inside the class definition. AFAIK, the current only workarounds are:
- Hint the alias as
typing.Callable[..., <return_type>]inside the class definition - Bind the alias outside of the class definition (no need to provide a type-hint)
I'm not sure what is supposed to be a convincing way to type the alias; some attempts below:
from __future__ import annotations
from typing import Callable
from types import MethodType
class A:
@classmethod
def _internal(cls) -> int:
return 0
public_1 = _internal # (bad; see `value_1`)
public_2: classmethod[int] = _internal # Incompatible types in assignment (expression has type "Callable[[Type[A]], int]", variable has type "classmethod[int]")
public_3: Callable[[], int] = _internal # Incompatible types in assignment (expression has type "Callable[[Type[A]], int]", variable has type "Callable[[], int]")
public_4: Callable[[type[A]], int] = _internal # (bad; see `value_4`)
public_5: MethodType = _internal # Incompatible types in assignment (expression has type "Callable[[Type[A]], int]", variable has type "MethodType")
public_6: Callable[..., int] = _internal # OK
public_7 = A._internal # OK
value_1: int = A.public_1() # Too few arguments
value_2: int = A.public_2() # (bad; see `public_2`)
value_3: int = A.public_3() # (bad; see `public_3`)
value_4: int = A.public_4() # Too few arguments
value_5: int = A.public_5() # (bad; see `public_5`)
value_6: int = A.public_6() # OK
value_7: int = public_7() # OK
public_4 / value_4 is especially concerning, as it is contradictory; what mypy thinks is the correct type for the public_4 method causes a mypy error when actually called.
Do you really require it to be an alias? Here's another workaround:
class A:
@classmethod
def public_8(cls) -> int:
return cls._internal()
Are there any updates on this issue?