Skip to content

Commit 37db499

Browse files
committed
Allow overriding an attribute with a property defined in the "decorator-style" (python#4125).
1 parent 9bd6517 commit 37db499

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

mypy/checker.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,23 @@ def check_method_override_for_base_with_name(
15361536
original_class_or_static = fdef.is_class or fdef.is_static
15371537
else:
15381538
original_class_or_static = False # a variable can't be class or static
1539-
if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
1539+
1540+
if context.is_property and isinstance(original_node, Var):
1541+
if isinstance(defn, Decorator):
1542+
if defn.var.is_settable_property:
1543+
assert isinstance(defn.var.type, CallableType)
1544+
if not is_equivalent(defn.var.type.ret_type, original_type):
1545+
self.fail('Signature of "{}" incompatible with {}'.format(
1546+
defn.name, base.name), context)
1547+
else:
1548+
self.fail('Overriding an attribute with a property requires '
1549+
'defining a setter method', context)
1550+
elif isinstance(defn, OverloadedFuncDef):
1551+
# potential errors already reported by the checks above
1552+
pass
1553+
else:
1554+
assert False, 'should be unreachable'
1555+
elif isinstance(original_type, AnyType) or isinstance(typ, AnyType):
15401556
pass
15411557
elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike):
15421558
original = self.bind_and_map_method(base_attr, original_type,

test-data/unit/check-classes.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,29 @@ a.f = a.f # E: Property "f" defined in "A" is read-only
13601360
a.f.x # E: "int" has no attribute "x"
13611361
[builtins fixtures/property.pyi]
13621362

1363+
[case testOverrideAttributeWithProperty]
1364+
class A:
1365+
x: int
1366+
y: int
1367+
z: int
1368+
class B(A):
1369+
@property
1370+
def x(self) -> int:
1371+
...
1372+
@x.setter
1373+
def x(self, v: int) -> None:
1374+
...
1375+
@property
1376+
def y(self) -> int: # E: Overriding an attribute with a property requires defining a setter method
1377+
...
1378+
@property
1379+
def z(self) -> str: # E: Signature of "z" incompatible with A
1380+
...
1381+
@z.setter
1382+
def z(self, v: str) -> None:
1383+
...
1384+
[builtins fixtures/property.pyi]
1385+
13631386
-- Descriptors
13641387
-- -----------
13651388

test-data/unit/check-dataclasses.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,8 +1245,9 @@ class A:
12451245
@dataclass
12461246
class B(A):
12471247
@property
1248-
def foo(self) -> int: pass # E: Signature of "foo" incompatible with supertype "A"
1249-
1248+
def foo(self) -> int: pass
1249+
@foo.setter
1250+
def foo(self, v: int) -> None: pass
12501251
reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> __main__.B"
12511252

12521253
[builtins fixtures/dataclasses.pyi]

test-data/unit/check-final.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,8 +762,13 @@ class B(A):
762762
class C(A):
763763
@property # E: Cannot override final attribute "x" (previously declared in base class "A")
764764
def x(self) -> None: pass
765+
@x.setter
766+
def x(self, v: int) -> None: pass
765767
@property # E: Cannot override final attribute "y" (previously declared in base class "A")
766768
def y(self) -> None: pass
769+
@y.setter
770+
def y(self, v: int) -> None: pass
771+
767772
[builtins fixtures/property.pyi]
768773
[out]
769774

test-data/unit/check-inference.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,10 +1475,12 @@ class A:
14751475
self.x = [] # E: Need type annotation for "x" (hint: "x: List[<type>] = ...")
14761476

14771477
class B(A):
1478-
# TODO?: This error is kind of a false positive, unfortunately
14791478
@property
1480-
def x(self) -> List[int]: # E: Signature of "x" incompatible with supertype "A"
1479+
def x(self) -> List[int]:
14811480
return [123]
1481+
@x.setter
1482+
def x(self, v: int) -> None:
1483+
...
14821484
[builtins fixtures/list.pyi]
14831485

14841486
[case testInferSetInitializedToEmpty]

0 commit comments

Comments
 (0)