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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy