[ty] Fix false positive for Final attribute assignment in init by saada · Pull Request #21158 · astral-sh/ruff (original) (raw)
saada mentioned this pull request
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:
- Modified attribute assignment validation to permit Final assignments when inside init methods
- Updated tests to reflect correct behavior
- Added test coverage for issue astral-sh#1409 with Self annotations
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):
- Reassignment to class-level assigned Finals in init
- Multiple assignments to same Final within init
These limitations are documented in the test file with TODO markers.
All 278 mdtest tests pass.
Refs astral-sh#1409
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 # ERRORWhile still allowing initialization when no value exists:
class Test:
attr: Final[int] # Just annotation
def __init__(self):
self.attr = 20 # OKImplementation uses the semantic index to check if the class-level declaration is an AnnotatedAssignment with a value field.
…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:
- Updated test documentation to remove TODOs and clarify allowed behavior
- Updated code comments to reflect conformance suite requirements
- Added test case with conditional parameter per Carl's example
- Remove unnecessary 'OK' comments from test assertions
- Fix false negatives: verify init is a method of the class being mutated
- Add test cases for invalid scenarios:
- Standalone functions named init
- Assignment from another class's init
- Assignment to non-self parameter (marked as TODO - requires parameter tracking)
The implementation now checks:
- We're in a function named init
- The function is a method (has class context)
- 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:
- Standalone functions named init
- Other classes' init methods
All 280 mdtest tests pass.
Refs astral-sh#1409
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 }})