Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generics are removed from function signatures that are assigned to Callable types #17050

Open
DetachHead opened this issue Mar 20, 2024 · 0 comments
Labels
bug mypy got something wrong

Comments

@DetachHead
Copy link
Contributor

DetachHead commented Mar 20, 2024

from typing import TypeVar, Callable

T = TypeVar("T")

def asdf(fn: Callable[[], T]) -> Callable[[], T]: ...

@asdf
def foo() -> list[T]: ...

reveal_type(foo) # "def () -> list[Never]", should be "def [T] () -> list[T]"

playground

use case

i'm making a @NoInstance decorator that bans usages of classmethods on instances:

from typing import TypeVar, ParamSpec, Concatenate, Callable, Generic, overload, Never

out_T = TypeVar("out_T", covariant=True)
P = ParamSpec("P")
out_R = TypeVar("out_R", covariant=True)

class NoInstance(Generic[P, out_R]):

    def __init__(self, function: Callable[Concatenate[type[out_T], P], out_R]):
        ...

    @overload
    def __get__(self, instance: None, owner: type[object]) -> Callable[P, out_R]:
        ...
    @overload
    def __get__(self, instance: object, owner: type[object]) -> Never:
        ...
    
    def __get__(self, instance: object, owner: type[object]) -> object:
        ...     
class Foo:
    @NoInstance
    @classmethod
    def foo(cls):
        ...

Foo.foo() # allowed
Foo().foo() # not allowed

but the primary use case for class methods that should not be called on instances are constructor methods, which means the resulting signature only has the typevar on the return type (in this case, Self):

class Foo:
    @NoInstance
    @classmethod
    def from_int(cls) -> Self:
        ...

reveal_type(Foo.from_int) # `"def () -> Never"`, because the `Self` generic was removed by the `Concatenate`. should be `def () -> Foo` instead
@DetachHead DetachHead added the bug mypy got something wrong label Mar 20, 2024
@DetachHead DetachHead changed the title Concatenate removes generics from function signatures when there are no other arguments with the same generic type in the signature generics are removed from function signatures that are assigned to Callable types Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

1 participant