Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1642.change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `@attrs.define` dropping slots declared on the slotted class itself when the attribute set is rewritten.
15 changes: 15 additions & 0 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,21 @@ def _create_slots_class(self):
# Setting __slots__ to inherited attributes wastes memory.
slot_names = [name for name in names if name not in base_names]

# Preserve slots declared on the class itself: they would otherwise
# be lost when we rewrite `__slots__` below.
own_slots = tuple(self._cls.__dict__.get("__slots__", ()))
own_new_slots = [
name
for name in own_slots
if name not in slot_names and name not in existing_slots
]
slot_names = own_new_slots + slot_names
# Remove stale descriptors for the class's own slots from the new
# class dict -- the type machinery recreates them from `__slots__`
# and keeping both raises ValueError on class creation.
for name in own_new_slots:
cd.pop(name, None)

# There are slots for attributes from current class
# that are defined in parent classes.
# As their descriptors may be overridden by a child class,
Expand Down
18 changes: 18 additions & 0 deletions tests/test_slots.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,24 @@ class C2(C1):
assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)


def test_own_slots_preserved():
"""
Slots declared on the slotted attrs class itself are preserved.
"""

@attrs.define
class A:
__slots__ = ("private",)

public: str = "foo"

a = A()
a.private = 1
assert 1 == a.private
assert "foo" == a.public
assert "private" in A.__slots__


def test_inheritance_from_slots_with_attribute_override():
"""
Inheriting from a slotted class doesn't re-create existing slots
Expand Down
Loading