python / python/mypy

generics are removed from function signatures that are assigned to `Callable` types

Open
#17,050 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the minimal examples in the linked mypy playground and compare the revealed types with the expected generic signatures. Trace the type inference and Callable/Concatenate handling involved in those examples; done means the generic T or Self is preserved rather than reduced to Never.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.