Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix override checking for decorated property #16856

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2022,22 +2022,29 @@ def check_method_override_for_base_with_name(
if original_node and is_property(original_node):
original_type = get_property_type(original_type)

if isinstance(typ, FunctionLike) and is_property(defn):
typ = get_property_type(typ)
if (
isinstance(original_node, Var)
and not original_node.is_final
and (not original_node.is_property or original_node.is_settable_property)
and isinstance(defn, Decorator)
):
# We only give an error where no other similar errors will be given.
if not isinstance(original_type, AnyType):
self.msg.fail(
"Cannot override writeable attribute with read-only property",
# Give an error on function line to match old behaviour.
defn.func,
code=codes.OVERRIDE,
)
if is_property(defn):
inner: FunctionLike | None
if isinstance(typ, FunctionLike):
inner = typ
else:
inner = self.extract_callable_type(typ, context)
if inner is not None:
typ = inner
typ = get_property_type(typ)
if (
isinstance(original_node, Var)
and not original_node.is_final
and (not original_node.is_property or original_node.is_settable_property)
and isinstance(defn, Decorator)
):
# We only give an error where no other similar errors will be given.
if not isinstance(original_type, AnyType):
self.msg.fail(
"Cannot override writeable attribute with read-only property",
# Give an error on function line to match old behaviour.
defn.func,
code=codes.OVERRIDE,
)

if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
pass
Expand Down
43 changes: 43 additions & 0 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,49 @@ f: Callable[[Sequence[TI]], None]
g: Callable[[Union[Sequence[TI], Sequence[TS]]], None]
f = g

[case testOverrideDecoratedProperty]
class Base:
@property
def foo(self) -> int: ...

class decorator:
def __init__(self, fn):
self.fn = fn
def __call__(self, decorated_self) -> int:
return self.fn(decorated_self)

class Child(Base):
@property
@decorator
def foo(self) -> int:
return 42

reveal_type(Child().foo) # N: Revealed type is "builtins.int"

class BadChild1(Base):
@decorator
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: decorator
return 42

class not_a_decorator:
def __init__(self, fn): ...

class BadChild2(Base):
@property
@not_a_decorator
def foo(self) -> int: # E: "not_a_decorator" not callable \
# E: Signature of "foo" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: not_a_decorator
return 42
[builtins fixtures/property.pyi]

[case explicitOverride]
# flags: --python-version 3.12
from typing import override
Expand Down
Loading