[Conformance]fix Feature Request: support __slots__ #630#2425
[Conformance]fix Feature Request: support __slots__ #630#2425asukaminato0721 wants to merge 5 commits intofacebook:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements __slots__-aware restrictions for instance attribute writes/deletes (fixing #630) by extracting literal slot names across the MRO and falling back to prior behavior when slot information is incomplete, and updates the dataclass slots conformance test to assert the new diagnostics.
Changes:
- Enforce
__slots__restrictions for instance attribute set/delete by computing literal slot-name sets across the MRO. - Fall back to existing (more permissive) behavior when slot extraction is not fully reliable (missing
__slots__,__dict__in slots, or non-literal slot definitions). - Promote the dataclass slots conformance case from a bug-marked test to an expected-pass test with explicit error annotations.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
pyrefly/lib/alt/attr.rs |
Adds slot-name extraction and applies it to attribute writes/deletes during attribute lookup. |
pyrefly/lib/test/dataclasses.rs |
Updates the dataclass slots conformance test expectations to require missing-attribute errors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | AttributeBase1::Quantified(_, cls) | ||
| | AttributeBase1::SuperInstance(cls, _) => Some(cls.clone()), |
There was a problem hiding this comment.
class_for_slots_restriction treats AttributeBase1::SuperInstance(cls, _) as cls.clone(), but cls is the start-lookup class for the super() proxy, not the underlying instance’s actual class (see how get_super_attribute uses SuperObj::Instance(obj)/SuperObj::Class(obj)). This will enforce __slots__ against the wrong class and can incorrectly reject super().attr = ... / del super().attr when attr is a slot on the real instance type but not on the start-lookup class. Use the SuperObj to derive the instance ClassType (and likely skip slots restriction for SuperObj::Class).
| | AttributeBase1::Quantified(_, cls) | |
| | AttributeBase1::SuperInstance(cls, _) => Some(cls.clone()), | |
| | AttributeBase1::Quantified(_, cls) => Some(cls.clone()), | |
| AttributeBase1::SuperInstance(_, super_obj) => match super_obj { | |
| // For `super()` bound to an instance, enforce slots based on the | |
| // underlying instance's actual class, not the start-lookup class. | |
| SuperObj::Instance(instance_cls) => Some(instance_cls.clone()), | |
| // For `super()` bound to a class, do not apply instance slots | |
| // restriction. | |
| SuperObj::Class(_) => None, | |
| }, |
| self.x = 3 | ||
| # should error: y is not in slots | ||
| self.y = 3 | ||
| self.y = 3 # E: Object of class `DC3` has no attribute `y` |
There was a problem hiding this comment.
This change enforces __slots__ restrictions on attribute writes/deletes, but the updated conformance test only covers direct self.y = .... Consider adding a small regression case for super() attribute writes/deletes on a slots class (e.g., super().y = ... where y is/ isn’t in the instance’s slots) to ensure SuperInstance handling stays correct.
| self.y = 3 # E: Object of class `DC3` has no attribute `y` | |
| self.y = 3 # E: Object of class `DC3` has no attribute `y` | |
| class DC2Child(DC2): | |
| def set_attr_via_super(self) -> None: | |
| super().__init__() | |
| # should error: y is not in slots of DC2 | |
| super().y = 3 # E: Object of class `DC2` has no attribute `y` | |
| def del_attr_via_super(self) -> None: | |
| # should error: y is not in slots of DC2 | |
| del super().y # E: Object of class `DC2` has no attribute `y` | |
| class DC3Child(DC3): | |
| def set_attr_via_super(self) -> None: | |
| super().__init__() | |
| # should error: y is not in slots of DC3 | |
| super().y = 3 # E: Object of class `DC3` has no attribute `y` | |
| def del_attr_via_super(self) -> None: | |
| # should error: y is not in slots of DC3 | |
| del super().y # E: Object of class `DC3` has no attribute `y` |
This comment has been minimized.
This comment has been minimized.
|
Thank you for the PR! |
get it... + ERROR bidict/_orderedbase.py:78:9-17: Object of class `Node` has no attribute `prv` [missing-attribute]
+ ERROR bidict/_orderedbase.py:86:9-21: Object of class `Node` has no attribute `prv` [missing-attribute]
+ ERROR bidict/_orderedbase.py:90:24-36: Object of class `Node` has no attribute `prv` [missing-attribute] |
|
Diff from mypy_primer, showing the effect of this PR on open source code: manticore (https://github.com/trailofbits/manticore)
+ ERROR manticore/platforms/evm.py:170:9-18: Object of class `Transaction` has no attribute `sort` [missing-attribute]
core (https://github.com/home-assistant/core)
+ ERROR homeassistant/components/bluetooth/manager.py:93:9-20: Object of class `HomeAssistantBluetoothManager` has no attribute `_debug` [missing-attribute]
+ ERROR homeassistant/components/bluetooth/manager.py:182:9-26: Object of class `HomeAssistantBluetoothManager` has no attribute `_all_history` [missing-attribute]
+ ERROR homeassistant/components/bluetooth/manager.py:182:28-53: Object of class `HomeAssistantBluetoothManager` has no attribute `_connectable_history` [missing-attribute]
+ ERROR homeassistant/helpers/llm.py:384:9-22: Object of class `MergedAPI` has no attribute `llm_apis` [missing-attribute]
+ ERROR homeassistant/helpers/llm.py:457:9-28: Object of class `AssistAPI` has no attribute `cached_slugify` [missing-attribute]
discord.py (https://github.com/Rapptz/discord.py)
+ ERROR discord/abc.py:574:9-25: Object of class `GuildChannel` has no attribute `_overwrites` [missing-attribute]
+ ERROR discord/message.py:1479:9-20: Object of class `PartialMessage` has no attribute `pinned` [missing-attribute]
+ ERROR discord/message.py:1508:9-20: Object of class `PartialMessage` has no attribute `pinned` [missing-attribute]
static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/frame.py:6498:13-31: Object of class `IndexBase` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/frame.py:6505:13-33: Object of class `IndexBase` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/index.py:1763:9-17: Object of class `_IndexGOMixin` has no attribute `_map` [missing-attribute]
+ ERROR static_frame/core/index.py:1764:9-20: Object of class `_IndexGOMixin` has no attribute `_labels` [missing-attribute]
+ ERROR static_frame/core/index.py:1765:9-23: Object of class `_IndexGOMixin` has no attribute `_positions` [missing-attribute]
+ ERROR static_frame/core/index.py:1766:9-21: Object of class `_IndexGOMixin` has no attribute `_recache` [missing-attribute]
+ ERROR static_frame/core/index.py:1767:9-18: Object of class `_IndexGOMixin` has no attribute `_name` [missing-attribute]
+ ERROR static_frame/core/index.py:1768:9-28: Object of class `_IndexGOMixin` has no attribute `_labels_mutable` [missing-attribute]
+ ERROR static_frame/core/index.py:1769:9-34: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1770:9-37: Object of class `_IndexGOMixin` has no attribute `_positions_mutable_count` [missing-attribute]
+ ERROR static_frame/core/index.py:1771:9-27: Object of class `_IndexGOMixin` has no attribute `_argsort_cache` [missing-attribute]
+ ERROR static_frame/core/index.py:1772:9-25: Object of class `_IndexGOMixin` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/index.py:1786:9-29: Object of class `_IndexGOMixin` has no attribute `_labels_mutable` [missing-attribute]
+ ERROR static_frame/core/index.py:1788:13-39: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1790:13-39: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1798:9-38: Object of class `_IndexGOMixin` has no attribute `_positions_mutable_count` [missing-attribute]
+ ERROR static_frame/core/index.py:1804:13-39: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1809:9-21: Object of class `_IndexGOMixin` has no attribute `_labels` [missing-attribute]
+ ERROR static_frame/core/index.py:1812:9-24: Object of class `_IndexGOMixin` has no attribute `_positions` [missing-attribute]
+ ERROR static_frame/core/index.py:1813:9-22: Object of class `_IndexGOMixin` has no attribute `_recache` [missing-attribute]
+ ERROR static_frame/core/index.py:1816:9-28: Object of class `_IndexGOMixin` has no attribute `_argsort_cache` [missing-attribute]
+ ERROR static_frame/core/index.py:1878:13-39: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1882:13-39: Object of class `_IndexGOMixin` has no attribute `_labels_mutable_dtype` [missing-attribute]
+ ERROR static_frame/core/index.py:1888:9-26: Object of class `_IndexGOMixin` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/index.py:1899:13-22: Object of class `_IndexGOMixin` has no attribute `_map` [missing-attribute]
+ ERROR static_frame/core/index.py:1901:9-38: Object of class `_IndexGOMixin` has no attribute `_positions_mutable_count` [missing-attribute]
+ ERROR static_frame/core/index.py:1902:9-22: Object of class `_IndexGOMixin` has no attribute `_recache` [missing-attribute]
+ ERROR static_frame/core/index_base.py:267:13-30: Object of class `IndexBase` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/index_datetime.py:242:9-26: Object of class `_IndexDatetimeGOMixin` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/index_datetime.py:250:9-38: Object of class `_IndexDatetimeGOMixin` has no attribute `_positions_mutable_count` [missing-attribute]
+ ERROR static_frame/core/index_datetime.py:251:9-22: Object of class `_IndexDatetimeGOMixin` has no attribute `_recache` [missing-attribute]
+ ERROR static_frame/core/series.py:2450:9-27: Object of class `IndexBase` has no attribute `_sort_status` [missing-attribute]
+ ERROR static_frame/core/yarn.py:1109:9-33: Object of class `IndexBase` has no attribute `_sort_status` [missing-attribute]
|
Summary
Fixes #630
Enforced
__slots__restrictions for instance attribute writes/deletes by extracting literal slot names across the MRO; if any class lacks__slots__, includes__dict__, or has non-literal slots, we fall back to existing behavior to avoid false positives.Test Plan
Promoted the dataclass slots conformance test to a normal passing test (removed the bug marker).