ruff
e1ce434f - [ty] Infer nested classes as read-only attributes (#29346)

Commit
2 days ago
[ty] Infer nested classes as read-only attributes (#29346) ty now accepts `Child.Meta` as an override when it inherits from `Base.Meta`. Previously, ty rejected the override because the two class objects are distinct. ```python class Base: class Meta: ... class Child(Base): class Meta(Base.Meta): ... # OK class Other(Base): class Meta: ... # Error: unrelated to Base.Meta ``` `Base.Meta` can still safely reveal the singleton class type of `Meta`, since the expression `Base` has a singleton class type that doesn't include subclasses. But a caller reading `obj.Meta` could get `Child.Meta`, so ty allows for subclasses in the inferred type. ```python reveal_type(Base.Meta) # <class 'Meta'> def inspect(obj: Base, cls: type[Base]) -> None: reveal_type(obj.Meta) # type[Meta] reveal_type(cls.Meta) # type[Meta] ``` The nested class declaration is read-only. If `obj` is a `Child`, assigning `Base.Meta` would replace its more specific `Child.Meta`; making `Base.Meta` read-only allows `Child` to safely narrow its type. ```python def update(obj: Base) -> None: obj.Meta = Base.Meta # Error del obj.Meta # Error ``` A subclass can explicitly declare a writable instance attribute when its instances need to replace `Meta`. ```python class Configurable(Base): def __init__(self) -> None: self.Meta: type[Base.Meta] = Base.Meta def configure(obj: Configurable) -> None: obj.Meta = Child.Meta # OK ``` The same read-only rule applies to nested classes declared on protocols and metaclasses. ### Django We have a special-case for Django in main allowing inner `Meta` class overrides on Django models. This PR doesn't remove that special case. Two abstract Django models can each declare an unrelated `Meta`. A model inheriting from both uses the first base’s `Meta` options by default. Ordinary override checking would report a conflict between those classes, so ty keeps the Django exception for this case. [Django’s `Meta` inheritance rules](https://docs.djangoproject.com/en/5.2/topics/db/models/#meta-inheritance) Closes https://github.com/astral-sh/ty/issues/4711. ## Test plan Compared Rust type layouts on aarch64 with debug assertions enabled and disabled. Widening `TypeQualifiers` to `u16` fit in existing padding in `TypeAndQualifiers`, `PlaceAndQualifiers`, and `ProtocolMemberData`. All three kept their original size. ## Ecosystem report The public comparison found 168 stable diagnostic changes on `c2d14afc02318e6fdf041a6983447aa7fb6008a6`. All follow from the new nested-class behavior. [Full ecosystem report](https://github.com/astral-sh/ruff/pull/29346#issuecomment-6172170919) - Spark gained two default warnings and Alerta gained eight opt-in warnings. Both use a nested class accessed through an instance as a base, so ty can no longer determine the exact method resolution order. - Three diagnostics disappeared in Spack and Freqtrade. - 155 existing override errors now display the inferred class-object types.
Author
Parents
Loading