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

Type variables of generic returned Protocols not resolved/leaking #1720

Closed
alexd2580 opened this issue Apr 3, 2021 · 1 comment
Closed
Labels
as designed Not a bug, working as intended

Comments

@alexd2580
Copy link

Continuation of #1703

As mentioned in the above issue, the fix for Callables works flawlessly.
The issue with unresolved type variables still surfaces when using subclasses of Protocol to describe function types.

Motivation

I am using Protocols to be able to give a name to an overloaded function type.
In my use case, I'm trying to annotate a function that returns a Protocol with an overloaded generic __call__ method.

Minimal reproducible example

from typing import TypeVar

from typing_extensions import Protocol

R = TypeVar("R", covariant=True)
S = TypeVar("S", contravariant=True)


class ArityOne(Protocol[S, R]):
    def __call__(self, __s: S) -> R:
        ...


def id_f(x: ArityOne[S, R]) -> ArityOne[S, R]:
    return x


X = TypeVar("X")


def identity(x: X) -> X:
    return x


i: int = id_f(identity)(4)

Actual behavior

>>> python --version
Python 3.6.13
>>> python callable_return_type.py 
# No error
>>> mypy --version
mypy 0.812
>>> mypy callable_return_type.py 
callable_return_type.py:25: error: Incompatible types in assignment (expression has type "X", variable has type "int")
callable_return_type.py:25: error: Argument 1 to "__call__" of "ArityOne" has incompatible type "int"; expected "X"
Found 2 errors in 1 file (checked 1 source file)
>>> pyright --version
pyright 1.1.127
>>> pyright callable_return_type.py 
No configuration file found.
stubPath /[REDACTED] is not a valid directory.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/[REDACTED]/callable_return_type.py
  /[REDACTED]/callable_return_type.py:25:25 - error: Argument of type "Literal[4]" cannot be assigned to parameter "__s" of type "X@identity" in function "__call__"
    Type "Literal[4]" cannot be assigned to type "X@identity" (reportGeneralTypeIssues)
  /[REDACTED]/callable_return_type.py:25:10 - error: Expression of type "X@identity" cannot be assigned to declared type "int"
    "object" is incompatible with "int" (reportGeneralTypeIssues)
2 errors, 0 warnings, 0 infos 
Completed in 0.514sec

Expected behavior

The provided code should typecheck without issues.
Even when ArityOne's __call__ method is @overloaded.

@erictraut
Copy link
Collaborator

Type variables in the Python type system have well-defined scopes, and they must be resolved within those scopes.

The expression id_f(identity) assigns the type variables defined in the scope of identity to the type variables within the scope of id_f, but it doesn't offer enough details to resolve these type variables in the identity scope. The result of this expression is ArityOne[X@identity, X@identity]. This can't be "solved" because the type variable X@identity is now out of scope.

I'll note that mypy issues the same errors in this instance.

While I was able to justify adding special-case logic for a Callable return type in #1703, I don't think I can justify adding it for a class (protocol or otherwise). Classes provide their own well-defined scopes for type variables, and it's important to honor those scopes. Extending the "hack" that I implemented for Callable will create other undesirable side effects if it is applied to protocol classes. That means you'll need to find another workaround for this particular case.

@erictraut erictraut added the as designed Not a bug, working as intended label Apr 3, 2021
heejaechang pushed a commit to heejaechang/pyright that referenced this issue Nov 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
as designed Not a bug, working as intended
Projects
None yet
Development

No branches or pull requests

2 participants