Open tmke8 opened 1 year ago
So, you want to be able to have separate final properties and their setters? Am I correct?
Hmm... I wanted to declare the implementation of the settable property final – meaning that subclasses shouldn't be allowed to override the getter or the setter. Do you think this doesn't make sense because Final
attributes aren't settable?
EDIT: so, to clarify, I wanted to make getter and setter both final, such that subclassses can override neither, but just making the getter final would also help, I guess
It will be interesting to make getters final, but allow overwrite setters/getters. Or more generally, to make only a subset of (getter, setter, deleter) final.
Usually, overwriting a subset of property accessors in a subclass is done via referencing to superclass property. But this is not currently allowed by mypy:
class A:
@property
def a(self) -> int: return 1
class B(A):
@A.a.setter # error: "Callable[[A], int]" has no attribute "setter" [attr-defined]
def a(self, value): pass
class A:
@property
def a(self) -> int: return 1
@a.setter
def a(self, value): pass
@a.deleter
def a(self): pass
class B(A):
@A.a.setter # error: overloaded function has no attribute "setter" [attr-defined]
def a(self, value): pass
Returning to completely final properties. When a function name is repeated, mypy considers that it is being overloaded. In the example of @tmke8 mypy emits error at final
, because x
is redefined later.
I tried to bypass this error via different names for setter and deleter, but ended up with another error:
from typing import final
class A:
@property
@final
def a(self) -> int: return 1
@a.setter # error: "Callable[[A], int]" has no attribute "setter" [attr-defined]
def a_setter(self, value): pass
@a_setter.deleter
def a_deleter(self): pass
a = a_deleter
del a_deleter, a_setter
class B(A):
@property # error: Cannot override final attribute "a" (previously declared in base class "A") [misc]
def a(self) -> int: return 2
@a.setter
def a(self, value): pass
Then I tried to use Final
, but this made mypy to stop considering a
as property.
from typing import final, Final, reveal_type
class A:
def a_get(self) -> int: return 1
def a_set(self, value): pass
def a_del(self): pass
a: Final[property] = property(a_get, a_set, a_del)
del a_get, a_set, a_del
obj = A()
reveal_type(obj.a) # note: Revealed type is "Any"
a.a = 1 # error: Name "a" is not defined [name-defined]
class B(A):
@property # error: Cannot override final attribute "a" (previously declared in base class "A") [misc]
# error: Signature of "a" incompatible with supertype "A" [override]
def a(self) -> int: return 2
@a.setter
def a(self, value): pass
All in all, final
poorly works with properties:
final
after property
to settable/deletable property emits error that final
is used on "overloaded" version of function.final
after setter
or deleter
does not produce errors if property got overwritten in a subclass.Also my examples shows that properties themselves are poorly supported:
[GetReturnType, SetValueType]
.
Bug Report
Consider this code:
https://mypy-play.net/?mypy=latest&python=3.11&gist=a4a1981a3db4ac546b9947bb344a5fd5
Mypy complains that
@final
is not applied to the "overload implementation", but the property is still successfully declared final.I tried moving the
@final
decorator to the setter – in that case, mypy's first error goes away, but then also the second error vanishes, meaning thatx
wasn't actually marked as final.(I also tried moving
@final
above@property
but that produces the same result.)Environment: See mypy-play link above.