Proposal: allow chaining @final decorator when previous decorators return a non-function

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.

1 Like

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.

Yes, I think it was always intended to allow final properties. Feel free to send a PR clarifying the text and adding a test for this behavior.

1 Like

It could also be interpreted as that you should the @final on the method itself, instead of the property, so like:

class Base:
    @property
    @final
    def p(self) -> int:
        return 0

I noticed that this typing PR is still open. There are some unresolved questions:

  • Should we specify that @property and @final may appear in any order?
  • What if only a getter but not a setter is marked @final?
>>> 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.

3 Likes