Skip to content

Commit 055184f

Browse files
authored
Fix override checking for decorated property (#16856)
Fixes #16855
1 parent ab0bd8c commit 055184f

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

mypy/checker.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,22 +2020,29 @@ def check_method_override_for_base_with_name(
20202020
if original_node and is_property(original_node):
20212021
original_type = get_property_type(original_type)
20222022

2023-
if isinstance(typ, FunctionLike) and is_property(defn):
2024-
typ = get_property_type(typ)
2025-
if (
2026-
isinstance(original_node, Var)
2027-
and not original_node.is_final
2028-
and (not original_node.is_property or original_node.is_settable_property)
2029-
and isinstance(defn, Decorator)
2030-
):
2031-
# We only give an error where no other similar errors will be given.
2032-
if not isinstance(original_type, AnyType):
2033-
self.msg.fail(
2034-
"Cannot override writeable attribute with read-only property",
2035-
# Give an error on function line to match old behaviour.
2036-
defn.func,
2037-
code=codes.OVERRIDE,
2038-
)
2023+
if is_property(defn):
2024+
inner: FunctionLike | None
2025+
if isinstance(typ, FunctionLike):
2026+
inner = typ
2027+
else:
2028+
inner = self.extract_callable_type(typ, context)
2029+
if inner is not None:
2030+
typ = inner
2031+
typ = get_property_type(typ)
2032+
if (
2033+
isinstance(original_node, Var)
2034+
and not original_node.is_final
2035+
and (not original_node.is_property or original_node.is_settable_property)
2036+
and isinstance(defn, Decorator)
2037+
):
2038+
# We only give an error where no other similar errors will be given.
2039+
if not isinstance(original_type, AnyType):
2040+
self.msg.fail(
2041+
"Cannot override writeable attribute with read-only property",
2042+
# Give an error on function line to match old behaviour.
2043+
defn.func,
2044+
code=codes.OVERRIDE,
2045+
)
20392046

20402047
if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
20412048
pass

test-data/unit/check-functions.test

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,6 +2730,49 @@ f: Callable[[Sequence[TI]], None]
27302730
g: Callable[[Union[Sequence[TI], Sequence[TS]]], None]
27312731
f = g
27322732

2733+
[case testOverrideDecoratedProperty]
2734+
class Base:
2735+
@property
2736+
def foo(self) -> int: ...
2737+
2738+
class decorator:
2739+
def __init__(self, fn):
2740+
self.fn = fn
2741+
def __call__(self, decorated_self) -> int:
2742+
return self.fn(decorated_self)
2743+
2744+
class Child(Base):
2745+
@property
2746+
@decorator
2747+
def foo(self) -> int:
2748+
return 42
2749+
2750+
reveal_type(Child().foo) # N: Revealed type is "builtins.int"
2751+
2752+
class BadChild1(Base):
2753+
@decorator
2754+
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "Base" \
2755+
# N: Superclass: \
2756+
# N: int \
2757+
# N: Subclass: \
2758+
# N: decorator
2759+
return 42
2760+
2761+
class not_a_decorator:
2762+
def __init__(self, fn): ...
2763+
2764+
class BadChild2(Base):
2765+
@property
2766+
@not_a_decorator
2767+
def foo(self) -> int: # E: "not_a_decorator" not callable \
2768+
# E: Signature of "foo" incompatible with supertype "Base" \
2769+
# N: Superclass: \
2770+
# N: int \
2771+
# N: Subclass: \
2772+
# N: not_a_decorator
2773+
return 42
2774+
[builtins fixtures/property.pyi]
2775+
27332776
[case explicitOverride]
27342777
# flags: --python-version 3.12
27352778
from typing import override

0 commit comments

Comments
 (0)