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

When passing generic class (not instance) to decorator expecting callable, generic type is inferred as 'Unknown' in return type of decorated function. #6139

Closed
EthanBullVulpe opened this issue Oct 12, 2023 · 3 comments
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working

Comments

@EthanBullVulpe
Copy link

Describe the bug
When passing a generic class to a decorator function which expects a callable, the generic type is inferred as 'Unknown' in the return type of the decorated function.

Code or Screenshots

from typing import Callable, Generic, Protocol, TypeVar, ParamSpec

T = TypeVar("T")

class Example(Generic[T]):
        
    def __init__(self, t1: T, t2: T | None = None) -> None:
        ...
    
def example2(t1: T, t2: T | None = None) -> Example[T]:
    ...
        
class Example3:
        
    def __call__(self, t1: T, t2: T | None = None) -> Example[T]:
        ...

class ExampleCallable(Protocol):
        
    def __call__(self, t1: T, t2: T | None = None) -> Example[T]:
        ...
        
# All three match the ExampleCallable protocol, showing they have the same signature of their __call__ method.
e: ExampleCallable = Example
e: ExampleCallable = example2
e: ExampleCallable = Example3()

Params = ParamSpec("Params")
Output = TypeVar("Output")
    
def decorator(func: Callable[Params, Output]) -> Callable[Params, Output]:
    ...

decoed_func1 = decorator(Example) # inferred return type is "Example[Unknown]"
decoed_func2 = decorator(example2) # inferred return type is "Example[T]"
decoed_func3 = decorator(Example3()) # inferred return type is "Example[T]"

In the above snipit, the class Example, the function example2, and instances of Example3() all implement the same signature of __call__, as shown by the fact that they all match the ExampleCallable Protocol.

However, when the class Example is passed through decorator, the return type of the decorated function is inferred as Example[Unknown] rather than Example[T].

When example2, and Example3() are passed through decorator, the return type is correctly inferred as Example[T].

VS Code extension or command-line
I am running pyright through the pylance extension in vscode. Pylance version 2023.10.23 and Pyright version 1.1.331.

@EthanBullVulpe EthanBullVulpe added the bug Something isn't working label Oct 12, 2023
@erictraut
Copy link
Collaborator

The reason example2 and Example3() work is that they result in a value of type Example[T].

In the case of Example, pyright is interpreting this as Example[Unknown] because you haven't provided a type argument. I'll see if I can make this work without breaking other cases.

I'll note that if you change the Example.__init__ method to an Example.__new__ method, it works as you are hoping.

class Example(Generic[T]):
    def __new__(cls, t1: T, t2: T | None = None) -> "Example[T]":
        ...

decoed_func1 = decorator(Example) # Evaluated return type is "Example[T]"

It makes sense that the analogous __init__ method should produce the same results.

@erictraut erictraut added the needs investigation Requires additional investigation to determine course of action label Oct 12, 2023
erictraut pushed a commit that referenced this issue Oct 12, 2023
… converted to a callable for a generic class. This addresses #6139.
@erictraut erictraut removed the needs investigation Requires additional investigation to determine course of action label Oct 12, 2023
@erictraut
Copy link
Collaborator

This will be addressed in the next release. It was fixed by the change for #6582.

@erictraut erictraut added the addressed in next version Issue is fixed and will appear in next published version label Dec 1, 2023
@erictraut
Copy link
Collaborator

This is addressed in pyright 1.1.339, which I just published. It will also be included in a future release of pylance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants