Skip to content

Commit

Permalink
Fixed bug that produces a false negative when attempting to assign a …
Browse files Browse the repository at this point in the history
…method to a `Callable` type where the first parameter of the method is typed as `Self`, but the callback requires the class itself. This addresses #6568. (#6569)
  • Loading branch information
erictraut authored Nov 28, 2023
1 parent 5b3523f commit 5471f6c
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 27 deletions.
16 changes: 0 additions & 16 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23979,22 +23979,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
}

// Handle the special case where the source is a Self type and the
// destination is not.
if (!isTypeVar(specializedDestType) || !specializedDestType.details.isSynthesizedSelf) {
if (
isTypeVar(specializedSrcType) &&
specializedSrcType.details.isSynthesizedSelf &&
specializedSrcType.details.boundType
) {
specializedSrcType = applySolvedTypeVars(
specializedSrcType.details.boundType,
new TypeVarContext(getTypeVarScopeId(specializedSrcType)),
{ unknownIfNotFound: true }
);
}
}

if (
!assignType(
specializedSrcType,
Expand Down
10 changes: 6 additions & 4 deletions packages/pyright-internal/src/tests/samples/memberAccess11.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# This sample tests that methods are bound properly regardless of
# whether they are decorated.

from typing import Callable
from typing import Callable, TypeVar

Callback = Callable[["MyClass", int], str]
S = TypeVar("S", bound="MyClass")

Callback = Callable[[S, int], str]

def decorator1(method: Callback) -> Callback:
def wrapper(self: "MyClass", a: int) -> str:

def decorator1(method: Callback[S]) -> Callback[S]:
def wrapper(self: S, a: int) -> str:
return "wrapped " + method(self, a)

return wrapper
Expand Down
3 changes: 2 additions & 1 deletion packages/pyright-internal/src/tests/samples/paramSpec24.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
T = TypeVar("T")
O = TypeVar("O")
P = ParamSpec("P")
Self_A = TypeVar("Self_A", bound="A")


class _callable_cache(Protocol[P, T]):
Expand All @@ -35,7 +36,7 @@ def __get__(self, instance: O, owner: type[O]) -> Self:


@overload
def cache(fn: Callable[Concatenate[A, P], T]) -> _wrapped_cache[A, P, T]: # type: ignore
def cache(fn: Callable[Concatenate[Self_A, P], T]) -> _wrapped_cache[Self_A, P, T]: # type: ignore
...


Expand Down
11 changes: 5 additions & 6 deletions packages/pyright-internal/src/tests/samples/self4.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# This sample tests the case where a method decorator uses an explicit
# type annotation for the "self" parameter.

from typing import Callable, Generic, TypeVar
from typing import Callable, Generic, TypeVar, Any

_T = TypeVar("_T")
T = TypeVar("T")
S = TypeVar("S", bound="MyClass[Any]")


def my_generic_wrapper(
f: Callable[["MyClass[_T]"], str]
) -> Callable[["MyClass[_T]"], int]:
def my_generic_wrapper(f: Callable[[S], str]) -> Callable[[S], int]:
...


class MyClass(Generic[_T]):
class MyClass(Generic[T]):
@my_generic_wrapper
def do_something(self) -> str:
...

0 comments on commit 5471f6c

Please sign in to comment.