[ty] Fix false positive for Final attribute assignment in init by saada · Pull Request #21158 · astral-sh/ruff (original) (raw)

@saada saada mentioned this pull request

Oct 31, 2025

sharkdp

@saada

Resolves astral-sh#1409

Allow Final instance attributes to be initialized in init methods, as specified by the Python typing spec. Previously, ty incorrectly prevented this initialization.

Changes:

@saada

Per feedback from sharkdp and AlexWaygood, this allows Final instance attributes to be initialized in init methods as specified in PEP 591.

The implementation adds an is_in_init() helper closure and modifies the existing invalid_assignment_to_final closure to check for init context. This centralizes the logic and avoids duplication across the three call sites.

Known limitations (require flow-sensitive analysis for future work):

These limitations are documented in the test file with TODO markers.

All 278 mdtest tests pass.

Refs astral-sh#1409

@saada

Implements detection for PEP 591 violation when a Final attribute with a class-level value is reassigned in init.

For example, this now correctly errors:

class Test:
    attr: Final[int] = 10  # Has value
    def __init__(self):
        self.attr = 20  # ERROR

While still allowing initialization when no value exists:

class Test:
    attr: Final[int]  # Just annotation
    def __init__(self):
        self.attr = 20  # OK

Implementation uses the semantic index to check if the class-level declaration is an AnnotatedAssignment with a value field.

@saada

…ance suite

Per Carl's feedback, the typing conformance suite requires that multiple assignments to Final attributes in init are allowed. This matches the behavior of mypy and pyright.

Our implementation already correctly allows this - this commit just updates the documentation and comments to clarify that this is intentional behavior, not a limitation.

Changes:

@saada

carljm

@saada

The implementation now checks:

  1. We're in a function named init
  2. The function is a method (has class context)
  3. The object type matches the class we're in (handles both NominalInstance and Self types)

This prevents false negatives where Final attributes could be assigned from:

All 280 mdtest tests pass.

Refs astral-sh#1409

@carljm

@carljm

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters

[ Show hidden characters]({{ revealButtonHref }})