Currently, the typing spec for @final (link) states:
It is an error to use `@final` on a non-method function.
I think this restriction makes sense, as we have capital-F Final for attributes, including property descriptors.
I propose that we also allow the @final decorator to be applied on top of decorators that return non-functions. For example, a decorator that returns a descriptor, like @property:
from typing import final
class Base:
@final
@property
def p(self) -> int:
return 0
class Derived(Base):
@property
def p(self) -> bool: # Expected error: p can not be overridden
return True
For these cases, it’s not possible to use the Final annotation, since there’s nowhere to put that annotation.
I may have read/written too hastily. I see that the spec also clearly states:
The method decorator version may be used with all of instance methods, class methods, static methods, and properties.
And indeed Mypy already implements the behavior I am proposing for chained decorators. If it’s agreeable, I am happy to do (a) add additional language, an example to the spec and (b) add a conformance test.
>>> from typing import final
>>> class A:
... @final
... @property
... def p(self): ...
...
... @property
... @final
... def q(self): ...
...
>>> A.q.fget.__final__
True
>>> A.p.fget.__final__
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
A.p.fget.__final__
AttributeError: 'function' object has no attribute '__final__'. Did you mean: '__init__'?
As you can see here, placing the @final first can be problematic for runtime introspection, so I think that we should l disallow that in favor of directly marking the fget method as @final.
As far as I’m aware, it’s only possible to override all of fget/fset/fdel in a subclass, not just one of them. And if you were to ignore setter from the base class and override the property to only have a getter, then that would violate Barbara Liskov’s substitution principle.
So I don’t see a sound way to make a partially-final @property work.