Skip to content

Commit 6c3c4a7

Browse files
authored
Bind self-types in checkmember for decorated classmethods (#19025)
Fixes #19023. Fixes #18993.
1 parent 8241059 commit 6c3c4a7

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

mypy/checkmember.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,14 +1230,20 @@ def analyze_class_attribute_access(
12301230
is_trivial_self = node.node.is_trivial_self
12311231
if isinstance(t, FunctionLike) and is_classmethod and not is_trivial_self:
12321232
t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
1233-
result = add_class_tvars(
1233+
t = add_class_tvars(
12341234
t,
12351235
isuper,
12361236
is_classmethod,
12371237
mx,
12381238
original_vars=original_vars,
12391239
is_trivial_self=is_trivial_self,
12401240
)
1241+
if is_decorated and not is_staticmethod:
1242+
t = expand_self_type_if_needed(
1243+
t, mx, cast(Decorator, node.node).var, itype, is_class=is_classmethod
1244+
)
1245+
1246+
result = t
12411247
# __set__ is not called on class objects.
12421248
if not mx.is_lvalue:
12431249
result = analyze_descriptor_access(result, mx)

test-data/unit/check-selftype.test

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,3 +2219,44 @@ class B:
22192219
class C(A, B): # OK: both methods take Self
22202220
pass
22212221
[builtins fixtures/tuple.pyi]
2222+
2223+
[case testSelfInFuncDecoratedClassmethod]
2224+
from collections.abc import Callable
2225+
from typing import Self, TypeVar
2226+
2227+
T = TypeVar("T")
2228+
2229+
def debug(make: Callable[[type[T]], T]) -> Callable[[type[T]], T]:
2230+
return make
2231+
2232+
class Foo:
2233+
@classmethod
2234+
@debug
2235+
def make(cls) -> Self:
2236+
return cls()
2237+
2238+
class Bar(Foo): ...
2239+
2240+
reveal_type(Foo.make()) # N: Revealed type is "__main__.Foo"
2241+
reveal_type(Foo().make()) # N: Revealed type is "__main__.Foo"
2242+
reveal_type(Bar.make()) # N: Revealed type is "__main__.Bar"
2243+
reveal_type(Bar().make()) # N: Revealed type is "__main__.Bar"
2244+
[builtins fixtures/tuple.pyi]
2245+
2246+
[case testSelfInClassDecoratedClassmethod]
2247+
from typing import Callable, Generic, TypeVar, Self
2248+
2249+
T = TypeVar("T")
2250+
2251+
class W(Generic[T]):
2252+
def __init__(self, fn: Callable[..., T]) -> None: ...
2253+
def __call__(self) -> T: ...
2254+
2255+
class Check:
2256+
@W
2257+
def foo(self) -> Self:
2258+
...
2259+
2260+
reveal_type(Check.foo()) # N: Revealed type is "def () -> __main__.Check"
2261+
reveal_type(Check().foo()) # N: Revealed type is "__main__.Check"
2262+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)