Skip to content

Commit 96b71f5

Browse files
committed
Some more fixes
1 parent 26566c6 commit 96b71f5

File tree

5 files changed

+122
-107
lines changed

5 files changed

+122
-107
lines changed

mypy/checker.py

Lines changed: 64 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from collections import defaultdict
77
from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet
88
from contextlib import ExitStack, contextmanager
9-
from typing import Callable, Final, Generic, NamedTuple, Optional, TypeVar, Union, cast, overload, TypeGuard
10-
from typing_extensions import TypeAlias as _TypeAlias
9+
from typing import Callable, Final, Generic, NamedTuple, Optional, TypeVar, Union, cast, overload
10+
from typing_extensions import TypeAlias as _TypeAlias, TypeGuard
1111

1212
import mypy.checkexpr
1313
from mypy import errorcodes as codes, join, message_registry, nodes, operators
@@ -648,8 +648,10 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
648648
assert isinstance(defn.items[0], Decorator)
649649
self.visit_decorator(defn.items[0])
650650
assert isinstance(defn.items[1], Decorator)
651-
self.visit_decorator(defn.items[1])
652-
defn.items[0].var.setter_type = defn.items[1].func.type
651+
self.visit_func_def(defn.items[1].func)
652+
setter_type = self.function_type(defn.items[1].func)
653+
assert isinstance(setter_type, CallableType)
654+
defn.items[0].var.setter_type = setter_type
653655
for fdef in defn.items:
654656
assert isinstance(fdef, Decorator)
655657
if defn.is_property:
@@ -2057,39 +2059,18 @@ def check_setter_type_override(
20572059
base_node, expanded_type, fill_typevars(defn.info)
20582060
))
20592061
else:
2062+
assert isinstance(original_type, CallableType)
20602063
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
2061-
original_type = get_setter_type(original_type)
2064+
assert isinstance(original_type, CallableType)
2065+
original_type = get_proper_type(original_type.arg_types[0])
20622066

20632067
typ = get_raw_setter_type(defn)
20642068
assert isinstance(typ, CallableType)
20652069
typ = bind_self(typ, self.scope.active_self_type())
2066-
typ = get_setter_type(typ)
2070+
typ = get_proper_type(typ.arg_types[0])
20672071

20682072
if not is_subtype(original_type, typ):
2069-
self.fail(
2070-
"Incompatible override of a setter type",
2071-
defn,
2072-
code=codes.OVERRIDE,
2073-
)
2074-
base_str, override_str = format_type_distinctly(
2075-
original_type, typ, options=self.options
2076-
)
2077-
self.note(
2078-
f' (base class "{base.name}" defined the type as {base_str},',
2079-
defn,
2080-
code = codes.OVERRIDE,
2081-
)
2082-
self.note(
2083-
f" override has type {override_str})",
2084-
defn,
2085-
code=codes.OVERRIDE,
2086-
)
2087-
if is_subtype(typ, original_typ):
2088-
self.note(
2089-
" Setter types should behave contravariantly",
2090-
defn,
2091-
code=codes.OVERRIDE,
2092-
)
2073+
self.msg.incompatible_setter_override(defn.items[1], typ, original_type, base)
20932074

20942075
def check_method_override_for_base_with_name(
20952076
self, defn: FuncDef | OverloadedFuncDef | Decorator, name: str, base: TypeInfo
@@ -2273,6 +2254,7 @@ def check_method_override_for_base_with_name(
22732254
self.msg.signature_incompatible_with_supertype(
22742255
defn.name, name, base.name, context, original=original_type, override=typ
22752256
)
2257+
return False
22762258

22772259
def bind_and_map_method(
22782260
self, sym: SymbolTableNode, typ: FunctionLike, sub_info: TypeInfo, super_info: TypeInfo
@@ -3427,32 +3409,15 @@ def check_compatibility_all_supers(
34273409
# base classes are also incompatible
34283410
return True
34293411
if lvalue_type and custom_setter:
3430-
base_type, _ = self.lvalue_type_from_base(lvalue_node, base, setter_type=True)
3412+
base_type, _ = self.lvalue_type_from_base(
3413+
lvalue_node, base, setter_type=True
3414+
)
3415+
# Setter type must be ready if the getter type is ready.
3416+
assert base_type is not None
34313417
if not is_subtype(base_type, lvalue_type):
3432-
self.fail(
3433-
"Incompatible override of a setter type",
3434-
lvalue,
3435-
code=codes.OVERRIDE,
3418+
self.msg.incompatible_setter_override(
3419+
lvalue, lvalue_type, base_type, base
34363420
)
3437-
base_str, override_str = format_type_distinctly(
3438-
base_type, lvalue_type, options=self.options
3439-
)
3440-
self.note(
3441-
f' (base class "{base.name}" defined the type as {base_str},',
3442-
lvalue,
3443-
code=codes.OVERRIDE,
3444-
)
3445-
self.note(
3446-
f" override has type {override_str})",
3447-
lvalue,
3448-
code=codes.OVERRIDE,
3449-
)
3450-
if is_subtype(lvalue_type, base_type):
3451-
self.note(
3452-
" Setter types should behave contravariantly",
3453-
lvalue,
3454-
code=codes.OVERRIDE,
3455-
)
34563421
return True
34573422
if base is last_immediate_base:
34583423
# At this point, the attribute was found to be compatible with all
@@ -3559,39 +3524,41 @@ def lvalue_type_from_base(
35593524
base_node = base_node.func
35603525
base_type = base_node.type
35613526

3562-
if base_type:
3563-
if not has_no_typevars(base_type):
3564-
self_type = self.scope.active_self_type()
3565-
assert self_type is not None, "Internal error: base lookup outside class"
3566-
if isinstance(self_type, TupleType):
3567-
instance = tuple_fallback(self_type)
3527+
if not base_type:
3528+
return None, None
3529+
if not has_no_typevars(base_type):
3530+
self_type = self.scope.active_self_type()
3531+
assert self_type is not None, "Internal error: base lookup outside class"
3532+
if isinstance(self_type, TupleType):
3533+
instance = tuple_fallback(self_type)
3534+
else:
3535+
instance = self_type
3536+
itype = map_instance_to_supertype(instance, base)
3537+
base_type = expand_type_by_instance(base_type, itype)
3538+
3539+
base_type = get_proper_type(base_type)
3540+
if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef):
3541+
# If we are a property, return the Type of the return
3542+
# value, not the Callable
3543+
if base_node.is_property:
3544+
base_type = get_proper_type(base_type.ret_type)
3545+
if isinstance(base_type, FunctionLike) and isinstance(
3546+
base_node, OverloadedFuncDef
3547+
):
3548+
# Same for properties with setter
3549+
if base_node.is_property:
3550+
if setter_type:
3551+
assert isinstance(base_node.items[0], Decorator)
3552+
base_type = base_node.items[0].var.setter_type
3553+
assert isinstance(base_type, CallableType)
3554+
base_type = self.bind_and_map_method(
3555+
base_var, base_type, expr_node.info, base
3556+
)
3557+
assert isinstance(base_type, CallableType)
3558+
base_type = get_proper_type(base_type.arg_types[0])
35683559
else:
3569-
instance = self_type
3570-
itype = map_instance_to_supertype(instance, base)
3571-
base_type = expand_type_by_instance(base_type, itype)
3572-
3573-
base_type = get_proper_type(base_type)
3574-
if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef):
3575-
# If we are a property, return the Type of the return
3576-
# value, not the Callable
3577-
if base_node.is_property:
3578-
base_type = get_proper_type(base_type.ret_type)
3579-
if isinstance(base_type, FunctionLike) and isinstance(
3580-
base_node, OverloadedFuncDef
3581-
):
3582-
# Same for properties with setter
3583-
if base_node.is_property:
3584-
if setter_type:
3585-
assert isinstance(base_node.items[0], Decorator)
3586-
base_type = base_node.items[0].var.setter_type
3587-
assert isinstance(base_type, CallableType)
3588-
base_type = self.bind_and_map_method(
3589-
base_var, base_type, expr_node.info, base
3590-
)
3591-
base_type = get_setter_type(base_type)
3592-
else:
3593-
base_type = base_type.items[0].ret_type
3594-
return base_type, base_node
3560+
base_type = base_type.items[0].ret_type
3561+
return base_type, base_node
35953562

35963563
def check_compatibility_classvar_super(
35973564
self, node: Var, base: TypeInfo, base_node: Node | None
@@ -8807,23 +8774,31 @@ def is_settable_property(defn: SymbolNode | None) -> TypeGuard[OverloadedFuncDef
88078774
return False
88088775

88098776

8810-
def is_custom_settable_property(defn: SymbolNode) -> bool:
8777+
def is_custom_settable_property(defn: SymbolNode | None) -> bool:
8778+
if defn is None:
8779+
return False
88118780
if not is_settable_property(defn):
88128781
return False
88138782
first_item = defn.items[0]
88148783
assert isinstance(first_item, Decorator)
88158784
if not first_item.var.is_settable_property:
88168785
return False
88178786
var = first_item.var
8818-
if var.setter_type is None:
8787+
if var.type is None or var.setter_type is None or isinstance(var.type, PartialType):
8788+
# The caller should defer in case of partial types or not ready variables.
8789+
return False
8790+
setter_type = var.setter_type.arg_types[1]
8791+
if isinstance(get_proper_type(setter_type), AnyType):
88198792
return False
88208793
return not is_same_type(
8821-
get_property_type(get_proper_type(var.type)), get_setter_type(var.setter_type)
8794+
get_property_type(get_proper_type(var.type)), setter_type
88228795
)
88238796

88248797

88258798
def get_raw_setter_type(defn: OverloadedFuncDef | Var) -> ProperType:
88268799
if isinstance(defn, Var):
8800+
# This function should not be called if the var is not ready.
8801+
assert defn.type is not None
88278802
return get_proper_type(defn.type)
88288803
first_item = defn.items[0]
88298804
assert isinstance(first_item, Decorator)
@@ -8832,13 +8807,6 @@ def get_raw_setter_type(defn: OverloadedFuncDef | Var) -> ProperType:
88328807
return var.setter_type
88338808

88348809

8835-
def get_setter_type(t: ProperType) -> ProperType:
8836-
# TODO: handle deferrals.
8837-
if isinstance(t, CallableType):
8838-
return get_proper_type(t.arg_types[0])
8839-
return t
8840-
8841-
88428810
def get_property_type(t: ProperType) -> ProperType:
88438811
if isinstance(t, CallableType):
88448812
return get_proper_type(t.ret_type)

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ def analyze_var(
777777
original_itype = itype
778778
itype = map_instance_to_supertype(itype, var.info)
779779
if var.is_settable_property and mx.is_lvalue:
780-
typ = var.setter_type
780+
typ: Type | None = var.setter_type
781781
if typ is None and var.is_ready:
782782
# Existing synthetic properties may not set setter type. Fall back to getter.
783783
typ = var.type

mypy/messages.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,34 @@ def overload_signature_incompatible_with_supertype(
11651165
note_template = 'Overload variants must be defined in the same order as they are in "{}"'
11661166
self.note(note_template.format(supertype), context, code=codes.OVERRIDE)
11671167

1168+
def incompatible_setter_override(
1169+
self, defn: Context, typ: Type, original_type: Type, base: TypeInfo
1170+
) -> None:
1171+
self.fail(
1172+
"Incompatible override of a setter type",
1173+
defn,
1174+
code=codes.OVERRIDE,
1175+
)
1176+
base_str, override_str = format_type_distinctly(
1177+
original_type, typ, options=self.options
1178+
)
1179+
self.note(
1180+
f' (base class "{base.name}" defined the type as {base_str},',
1181+
defn,
1182+
code=codes.OVERRIDE,
1183+
)
1184+
self.note(
1185+
f" override has type {override_str})",
1186+
defn,
1187+
code=codes.OVERRIDE,
1188+
)
1189+
if is_subtype(typ, original_type):
1190+
self.note(
1191+
" Setter types should behave contravariantly",
1192+
defn,
1193+
code=codes.OVERRIDE,
1194+
)
1195+
11681196
def signature_incompatible_with_supertype(
11691197
self,
11701198
name: str,

mypy/nodes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None:
10121012
# TODO: Should be Optional[TypeInfo]
10131013
self.info = VAR_NO_INFO
10141014
self.type: mypy.types.Type | None = type # Declared or inferred type, or None
1015-
self.setter_type: mypy.types.ProperType | None = None
1015+
self.setter_type: mypy.types.CallableType | None = None
10161016
# Is this the first argument to an ordinary method (usually "self")?
10171017
self.is_self = False
10181018
# Is this the first argument to a classmethod (typically "cls")?
@@ -1092,6 +1092,7 @@ def deserialize(cls, data: JsonDict) -> Var:
10921092
type = None if data["type"] is None else mypy.types.deserialize_type(data["type"])
10931093
setter_type = None if data["setter_type"] is None else mypy.types.deserialize_type(data["setter_type"])
10941094
v = Var(name, type)
1095+
assert setter_type is None or isinstance(setter_type, mypy.types.ProperType) and isinstance(setter_type, mypy.types.CallableType)
10951096
v.setter_type = setter_type
10961097
v.is_ready = False # Override True default set in __init__
10971098
v._fullname = data["fullname"]

test-data/unit/check-classes.test

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ class A:
784784
f: Callable[[str], None]
785785

786786
class B(A):
787-
@property # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]")
787+
@property
788788
def f(self) -> Callable[[object], None]: pass
789789
@func.setter
790790
def f(self, x: object) -> None: pass
@@ -8087,7 +8087,10 @@ class B2:
80878087
class C2(B2):
80888088
@property
80898089
def foo(self) -> str: ...
8090-
@foo.setter
8090+
@foo.setter # E: Incompatible override of a setter type \
8091+
# N: (base class "B2" defined the type as "B", \
8092+
# N: override has type "C") \
8093+
# N: Setter types should behave contravariantly
80918094
def foo(self, x: C) -> None: ...
80928095

80938096
class B3:
@@ -8109,7 +8112,10 @@ class B4:
81098112
class C4(B4):
81108113
@property
81118114
def foo(self) -> C: ...
8112-
@foo.setter
8115+
@foo.setter # E: Incompatible override of a setter type \
8116+
# N: (base class "B4" defined the type as "B", \
8117+
# N: override has type "C") \
8118+
# N: Setter types should behave contravariantly
81138119
def foo(self, x: C) -> None: ...
81148120

81158121
class B5:
@@ -8118,10 +8124,16 @@ class B5:
81188124
@foo.setter
81198125
def foo(self, x: B) -> None: ...
81208126
class C5(B5):
8121-
@property
8127+
@property # E: Signature of "foo" incompatible with supertype "B5" \
8128+
# N: Superclass: \
8129+
# N: str \
8130+
# N: Subclass: \
8131+
# N: C
81228132
def foo(self) -> C: ...
8123-
@foo.setter
8124-
def foo(self, x: C) -> None: ...
8133+
@foo.setter # E: Incompatible override of a setter type \
8134+
# N: (base class "B5" defined the type as "B", \
8135+
# N: override has type "str")
8136+
def foo(self, x: str) -> None: ...
81258137

81268138
class B6:
81278139
@property
@@ -8144,12 +8156,15 @@ class B1:
81448156
class C1(B1):
81458157
@property
81468158
def foo(self) -> B: ...
8147-
@foo.setter
8159+
@foo.setter # E: Incompatible override of a setter type \
8160+
# N: (base class "B1" defined the type as "B", \
8161+
# N: override has type "C") \
8162+
# N: Setter types should behave contravariantly
81488163
def foo(self, x: C) -> None: ...
81498164

81508165
class B2:
81518166
foo: C
8152-
class C2(2):
8167+
class C2(B2):
81538168
@property
81548169
def foo(self) -> C: ...
81558170
@foo.setter
@@ -8190,7 +8205,10 @@ class B3:
81908205
@foo.setter
81918206
def foo(self, x: B) -> None: ...
81928207
class C3(B3):
8193-
foo: C
8208+
foo: C # E: Incompatible override of a setter type \
8209+
# N: (base class "B3" defined the type as "B", \
8210+
# N: override has type "C") \
8211+
# N: Setter types should behave contravariantly
81948212
[builtins fixtures/property.pyi]
81958213

81968214
[case testOverrideMethodProperty]

0 commit comments

Comments
 (0)