Skip to content

Commit d52ce3b

Browse files
authored
Fix constructor type for subclasses of Any (#19295)
Fixes #9815 Fixes #10848 Fixes #17781 Also discovered this while working on `checkmember` stuff. Previously, return type of the type object was the class where `__init__()` was defined (if there was an `Any` somewhere in MRO). And since we use return type for attribute access on type objects, it went completely sideways. Fix is simple (we accidentally used `info` instead of `def_info` in one place).
1 parent 9f455bd commit d52ce3b

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

mypy/typeops.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ def tuple_fallback(typ: TupleType) -> Instance:
125125
)
126126

127127

128-
def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Type | None:
128+
def get_self_type(func: CallableType, def_info: TypeInfo) -> Type | None:
129+
default_self = fill_typevars(def_info)
129130
if isinstance(get_proper_type(func.ret_type), UninhabitedType):
130131
return func.ret_type
131132
elif func.arg_types and func.arg_types[0] != default_self and func.arg_kinds[0] == ARG_POS:
@@ -227,9 +228,8 @@ def type_object_type_from_function(
227228
# self-types only in the defining class, similar to __new__ (but not exactly the same,
228229
# see comment in class_callable below). This is mostly useful for annotating library
229230
# classes such as subprocess.Popen.
230-
default_self = fill_typevars(info)
231231
if not is_new and not info.is_newtype:
232-
orig_self_types = [get_self_type(it, default_self) for it in signature.items]
232+
orig_self_types = [get_self_type(it, def_info) for it in signature.items]
233233
else:
234234
orig_self_types = [None] * len(signature.items)
235235

@@ -245,7 +245,7 @@ def type_object_type_from_function(
245245
# We need to map B's __init__ to the type (List[T]) -> None.
246246
signature = bind_self(
247247
signature,
248-
original_type=default_self,
248+
original_type=fill_typevars(info),
249249
is_classmethod=is_new,
250250
# Explicit instance self annotations have special handling in class_callable(),
251251
# we don't need to bind any type variables in them if they are generic.

test-data/unit/check-classes.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8796,3 +8796,21 @@ class C:
87968796
C().foo = "no" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
87978797
C().bar = "fine"
87988798
[builtins fixtures/property.pyi]
8799+
8800+
[case testCorrectConstructorTypeWithAnyFallback]
8801+
from typing import Generic, TypeVar
8802+
8803+
class B(Unknown): # type: ignore
8804+
def __init__(self) -> None: ...
8805+
class C(B): ...
8806+
8807+
reveal_type(C) # N: Revealed type is "def () -> __main__.C"
8808+
8809+
T = TypeVar("T")
8810+
class BG(Generic[T], Unknown): # type: ignore
8811+
def __init__(self) -> None: ...
8812+
class CGI(BG[int]): ...
8813+
class CGT(BG[T]): ...
8814+
8815+
reveal_type(CGI) # N: Revealed type is "def () -> __main__.CGI"
8816+
reveal_type(CGT) # N: Revealed type is "def [T] () -> __main__.CGT[T`1]"

0 commit comments

Comments
 (0)