For example:
class Foo:
def __init__(self) -> None:
self.x = 42
class Bar(Foo):
def __init__(self) -> None:
self.y = 9000
@property
def x(self) -> int:
print('got x!!!')
return self.y
@x.setter
def x(self, value: int) -> None:
print('set x!!!')
self.y = value
This should be allowed as it doesn't violate LSP as long as x implements both a getter and a setter of the correct type.
Hm, interesting, this is already allowed for structural subtypes:
class Foo(Protocol):
x: int
class Bar:
def __init__(self) -> None:
self.y = 9000
@property
def x(self) -> int:
print('got x!!!')
return self.y
@x.setter
def x(self, value: int) -> None:
print('set x!!!')
self.y = value
x: Foo = Bar() # OK
I think this is a bug, it should be allowed for nominal classes as well.
Yes, this is a bug. I was under the impression that this was already working.
Technically it does violate LSP; there should be a warning about the missing @x.deleter.
Yes, but if you add a deleter it still complains.
I don't think mypy should actually care about whether a deleter is present though, since deleting attributes is rare and if you do it your code is unlikely to be type-safe anyway.
True, but overriding an attribute with a property is also not so common, so a reminder that it's not complete might be useful.
There is a different error message when using property() as a function:
class Base:
prop: bool
class Sub(Base):
def _get_prop(self) -> bool:
return False
def _set_prop(self, value: bool) -> None:
pass
prop = property(_get_prop, _set_prop)
foo.py:9: error: Incompatible types in assignment (expression has type "property", base class "Base" defined the type as "bool")
In mypy==0.720, if I slightly modify the example in https://github.com/python/mypy/issues/4125#issuecomment-337048971 (above) based on the documentation's guidance that:
Explicitly including a protocol as a base class is also a way of documenting that your class implements a particular protocol, and it forces mypy to verify that your class implementation is actually compatible with the protocol.
...I get the following behavior:
class Foo(Protocol):
x: int
class Bar(Foo):
def __init__(self) -> None:
self.y = 9000
@property # Signature of "x" incompatible with supertype "Foo"
def x(self) -> int: # Signature of "x" incompatible with supertype "Foo"
print('got x!!!')
return self.y
@x.setter
def x(self, value: int) -> None:
print('set x!!!')
self.y = value
x: Foo = Bar()
I would expect this to be OK for the same reasons that the original protocol example (no inheritance) is OK.
Setting priority to high, see duplicate issue https://github.com/python/mypy/issues/6759#issuecomment-521695300 for motivation.
I ran into a similar issue. Mypy seems to disallow overriding even a property, if not defined using a decorator:
# Annotations in here doesn't seem to matter.
def _not_implemented(*args, **kwargs):
raise NotImplementedError
class AbstractClass:
foo = property(_not_implemented, _not_implemented)
class MyClass(AbstractClass):
# Here the mypy reports:
# error: Signature of "foo" incompatible with supertype "AbstractClass" [override]
@property
def foo(self) -> str:
return 'Foo'
@foo.setter
def foo(self, value: str) -> None:
pass
If AbstractClass.foo is defined with property as a decorator, it works fine.
Most helpful comment
Setting priority to high, see duplicate issue https://github.com/python/mypy/issues/6759#issuecomment-521695300 for motivation.