Skip to content

Commit

Permalink
Allow overriding an attribute with a property defined in the "decorat…
Browse files Browse the repository at this point in the history
…or-style" (python#4125).
  • Loading branch information
tyralla committed Oct 17, 2021
1 parent 9bd6517 commit 37db499
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 5 deletions.
18 changes: 17 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,23 @@ def check_method_override_for_base_with_name(
original_class_or_static = fdef.is_class or fdef.is_static
else:
original_class_or_static = False # a variable can't be class or static
if isinstance(original_type, AnyType) or isinstance(typ, AnyType):

if context.is_property and isinstance(original_node, Var):
if isinstance(defn, Decorator):
if defn.var.is_settable_property:
assert isinstance(defn.var.type, CallableType)
if not is_equivalent(defn.var.type.ret_type, original_type):
self.fail('Signature of "{}" incompatible with {}'.format(
defn.name, base.name), context)
else:
self.fail('Overriding an attribute with a property requires '
'defining a setter method', context)
elif isinstance(defn, OverloadedFuncDef):
# potential errors already reported by the checks above
pass
else:
assert False, 'should be unreachable'
elif isinstance(original_type, AnyType) or isinstance(typ, AnyType):
pass
elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike):
original = self.bind_and_map_method(base_attr, original_type,
Expand Down
23 changes: 23 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,29 @@ a.f = a.f # E: Property "f" defined in "A" is read-only
a.f.x # E: "int" has no attribute "x"
[builtins fixtures/property.pyi]

[case testOverrideAttributeWithProperty]
class A:
x: int
y: int
z: int
class B(A):
@property
def x(self) -> int:
...
@x.setter
def x(self, v: int) -> None:
...
@property
def y(self) -> int: # E: Overriding an attribute with a property requires defining a setter method
...
@property
def z(self) -> str: # E: Signature of "z" incompatible with A
...
@z.setter
def z(self, v: str) -> None:
...
[builtins fixtures/property.pyi]

-- Descriptors
-- -----------

Expand Down
5 changes: 3 additions & 2 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -1245,8 +1245,9 @@ class A:
@dataclass
class B(A):
@property
def foo(self) -> int: pass # E: Signature of "foo" incompatible with supertype "A"

def foo(self) -> int: pass
@foo.setter
def foo(self, v: int) -> None: pass
reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B"

[builtins fixtures/dataclasses.pyi]
Expand Down
5 changes: 5 additions & 0 deletions test-data/unit/check-final.test
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,13 @@ class B(A):
class C(A):
@property # E: Cannot override final attribute "x" (previously declared in base class "A")
def x(self) -> None: pass
@x.setter
def x(self, v: int) -> None: pass
@property # E: Cannot override final attribute "y" (previously declared in base class "A")
def y(self) -> None: pass
@y.setter
def y(self, v: int) -> None: pass

[builtins fixtures/property.pyi]
[out]

Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -1475,10 +1475,12 @@ class A:
self.x = [] # E: Need type annotation for "x" (hint: "x: List[<type>] = ...")

class B(A):
# TODO?: This error is kind of a false positive, unfortunately
@property
def x(self) -> List[int]: # E: Signature of "x" incompatible with supertype "A"
def x(self) -> List[int]:
return [123]
@x.setter
def x(self, v: int) -> None:
...
[builtins fixtures/list.pyi]

[case testInferSetInitializedToEmpty]
Expand Down

0 comments on commit 37db499

Please sign in to comment.