Allow harmless overriding of `Final` attributes for documentation purposes

Documentation tools like sphinx use docstrings attached to type hinted attributes. Sometimes it is desirable to repeat/extend the documentation of certain inherited attributes (but not document all inherited members).

I propose to allow respecifying Final-attributes for documentation purposes if the type hint matches exactly the previously defined type hint on the parent class, since in this case we are not overriding, but simply repeating the statement.

[mypy-playground]

from typing import Final

class Base:
    input_size: Final[int]
    hidden_size: Final[int]
    
    def __init__(self, input_size: int, hidden_size: int) -> None:
        self.input_size = input_size
        self.hidden_size = hidden_size

class Custom(Base):
    input_size: Final[int]  # ❌ Cannot override final attribute
    """Documentation"""
    hidden_size: Final[int]  # ❌ Cannot override final attribute
    """Documentation"""
    
    def __init__(self, input_size: int, hidden_size: int) -> None:
        super().__init__(input_size, hidden_size)
3 Likes

An attribute that is marked Final tells a type checker to enforce that:

  1. The attribute cannot be set or deleted (except for its initial assignment).
  2. The attribute cannot be overridden by subclasses.
  3. The class that defines the Final attribute assigns a value to it exactly once — in either the class body or within the __init__ method.

What you’re proposing is to suspend (or modify) check 2 under certain conditions, but you haven’t addressed check 3. I can imagine adding more special cases to skip check 3 under certain conditions, but I’m not in favor of complicating the type system — effectively adding more special cases to an already-complex set of checks — just to accommodate a nonstandard way of providing docstrings.

From my perspective, the class that defines a Final attribute should provide its documentation and not delegate this responsibility to subclasses.

7 Likes