Skip to content

Commit 07286d7

Browse files
committed
Fix alias assignments in class body
1 parent 6de5eef commit 07286d7

File tree

5 files changed

+31
-19
lines changed

5 files changed

+31
-19
lines changed

mypy/checker.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,18 +2336,23 @@ def check_method_override_for_base_with_name(
23362336
)
23372337
return False
23382338

2339-
def get_property_instance(self, method: Decorator) -> Instance | None:
2339+
def get_property_instance(self, method: Decorator | OverloadedFuncDef) -> Instance | None:
2340+
deco = method if isinstance(method, Decorator) else method.items[0]
23402341
property_deco_name = next(
23412342
(
23422343
name
2343-
for d in method.original_decorators
2344+
for d in deco.original_decorators
23442345
for name in PROPERTY_DECORATOR_NAMES
23452346
if refers_to_fullname(d, name)
23462347
),
23472348
None,
23482349
)
23492350
if property_deco_name is not None:
2350-
return self.named_type(property_deco_name)
2351+
# Extra attr preserves the underlying node to support alias assignments
2352+
# (see testPropertyAliasInClassBody)
2353+
return self.named_type(property_deco_name).copy_with_extra_attr(
2354+
"__mypy-wrapped-property", method.type
2355+
)
23512356
return None
23522357

23532358
def get_op_other_domain(self, tp: FunctionLike) -> Type | None:
@@ -4409,6 +4414,13 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44094414
refers to the variable (lvalue). If var is None, do nothing.
44104415
"""
44114416
if var and not self.current_node_deferred:
4417+
p_type = get_proper_type(type)
4418+
if (
4419+
isinstance(p_type, Instance)
4420+
and p_type.extra_attrs
4421+
and "__mypy-wrapped-property" in p_type.extra_attrs.attrs
4422+
):
4423+
type = p_type.extra_attrs.attrs["__mypy-wrapped-property"]
44124424
var.type = type
44134425
var.is_inferred = True
44144426
var.is_ready = True

mypy/checkexpr.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,19 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
384384
if isinstance(result, PartialType):
385385
result = self.chk.handle_partial_var_type(result, lvalue, node, e)
386386
elif isinstance(node, Decorator):
387-
result = self.analyze_var_ref(node.var, e)
387+
property_type = self.chk.get_property_instance(node)
388+
if property_type is not None:
389+
result = property_type
390+
else:
391+
result = self.analyze_var_ref(node.var, e)
388392
elif isinstance(node, OverloadedFuncDef):
389393
result = node.type
390394
if result is None:
391395
if self.chk.in_checked_function() and node.items:
392396
self.chk.handle_cannot_determine_type(node.name, e)
393397
result = AnyType(TypeOfAny.from_error)
394-
elif isinstance(node.items[0], Decorator):
395-
property_type = self.chk.get_property_instance(node.items[0])
396-
if property_type is not None:
397-
result = property_type
398+
elif property_type := self.chk.get_property_instance(node):
399+
result = property_type
398400
elif isinstance(node, (FuncDef, TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)):
399401
result = self.analyze_static_reference(node, e, e.is_alias_rvalue or lvalue)
400402
else:
@@ -435,11 +437,8 @@ def analyze_static_reference(
435437
special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object.
436438
# TODO: merge with analyze_ref_expr() when we are confident about performance.
437439
"""
438-
if isinstance(node, (Var, OverloadedFuncDef)):
440+
if isinstance(node, (Var, Decorator, OverloadedFuncDef)):
439441
return node.type or AnyType(TypeOfAny.special_form)
440-
elif isinstance(node, Decorator):
441-
property_type = self.chk.get_property_instance(node)
442-
return property_type or node.type or AnyType(TypeOfAny.special_form)
443442
elif isinstance(node, FuncDef):
444443
return function_type(node, self.named_type("builtins.function"))
445444
elif isinstance(node, TypeInfo):

mypy/checkmember.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,15 +1226,15 @@ def analyze_class_attribute_access(
12261226
erase_vars.add(itype.type.self_type)
12271227
t = erase_typevars(t, {tv.id for tv in erase_vars})
12281228

1229-
if isinstance(node.node, Decorator) and node.node.func.is_property:
1229+
if (
1230+
isinstance(node.node, Decorator)
1231+
and node.node.func.is_property
1232+
or isinstance(node.node, OverloadedFuncDef)
1233+
and node.node.is_property
1234+
):
12301235
property_type = mx.chk.get_property_instance(node.node)
12311236
if property_type is not None:
12321237
return property_type
1233-
elif isinstance(node.node, OverloadedFuncDef) and node.node.is_property:
1234-
assert isinstance(node.node.items[0], Decorator)
1235-
property_type = mx.chk.get_property_instance(node.node.items[0])
1236-
if property_type is not None:
1237-
return property_type
12381238

12391239
is_classmethod = (
12401240
(is_decorated and cast(Decorator, node.node).func.is_class)

test-data/unit/check-callable.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ class A:
628628

629629
g2 = f2
630630

631+
reveal_type(A().f) # N: Revealed type is "builtins.int"
631632
reveal_type(A().g) # N: Revealed type is "builtins.int"
632633
reveal_type(A().g2) # N: Revealed type is "builtins.int"
633634
A().g = 1 # E: Property "g" defined in "A" is read-only

test-data/unit/check-protocols.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3346,7 +3346,7 @@ def test(arg: P) -> None: ...
33463346
# TODO: skip type mismatch diagnostics in this case.
33473347
test(B) # E: Argument 1 to "test" has incompatible type "type[B]"; expected "P" \
33483348
# N: Following member(s) of "B" have conflicts: \
3349-
# N: foo: expected "int", got "Callable[[B], int]" \
3349+
# N: foo: expected "int", got "property" \
33503350
# N: Only class variables allowed for class object access on protocols, foo is an instance variable of "B"
33513351
test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
33523352
# N: Only class variables allowed for class object access on protocols, foo is an instance variable of "C"

0 commit comments

Comments
 (0)