Open JelleZijlstra opened 7 years ago
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 Protocol
s 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.
Are there plans to fix this? It's strange that mypy doesn't support a core syntax.
It looks like some of these examples (e.g. the x=17
example above) may have been fixed, so in the interest of having a current, non-working example:
from typing import Any
def foo(_: Any) -> int:
return 5
@foo
class Foo:
pass
def test_foo() -> None:
assert Foo + 7 == 12 # Does not pass typechecking
The error for the final line is: error: Unsupported operand types for + ("Type[Foo]" and "int") [operator]
.
Function decorators seem to work fine though:
def qux(func: Callable[[], str]) -> Callable[[], int]:
def wrap() -> int:
return int(func())
return wrap
@qux
def five() -> str:
return "5"
def test_qux() -> None:
assert five() + 10 == 15 # Passes typechecking
It looks like some of these examples (e.g. the
x=17
example above) may have been fixed
yeah, class decorators are now typechecked, but the issue now is that they are unable to change the type of the class they're decorating. see https://github.com/python/mypy/issues/11117#issuecomment-1754948638
yeah, class decorators are now typechecked, but the issue now is that they are unable to change the type of the class they're decorating. see #11117 (comment)
Imho, this is important functionality.
For example, it could be useful for implementers of alternative dataclasses, such as pydantic or sqlmodel. Especially once support for intersection gets into mypy. I believe it would be much better than the current approach, that states:
Mypy does not yet recognize aliases of dataclasses.dataclass, and will probably never recognize dynamically computed decorators.
(taken from https://mypy.readthedocs.io/en/stable/additional_features.html)
It would also obsolete the hacky and limited typing_extensions.dataclass_transform
.
Just my two (hopefully correct) cents.
It would also obsolete the hacky and limited
typing_extensions.dataclass_transform
.
I don't think it would. The dataclass_transform
marks that a class has fields made with field-makers. Whereas an annotated decorator can tell the type checker that a class can be passed to dataclasses
functions like fields
and replace
.
These are separate concepts. You can have classes with fields that can't be passed to dataclass functions.
Mypy doesn't appear to do any checking of class decorators. Applying the decorator manually works.