diff --git a/src/attr/_make.py b/src/attr/_make.py index 32e42976e..c8e357bf6 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -10,6 +10,7 @@ import itertools import linecache import sys +import textwrap import types import unicodedata import weakref @@ -893,14 +894,14 @@ def _create_slots_class(self): base_names = set(self._base_names) - names = self._attr_names + names = dict.fromkeys(self._attr_names) if ( self._weakref_slot and "__weakref__" not in getattr(self._cls, "__slots__", ()) and "__weakref__" not in names and not weakref_inherited ): - names += ("__weakref__",) + names["__weakref__"] = "" cached_properties = { name: cached_prop.func @@ -915,13 +916,16 @@ def _create_slots_class(self): class_annotations = _get_annotations(self._cls) for name, func in cached_properties.items(): # Add cached properties to names for slotting. - names += (name,) # Clear out function from class to avoid clashing. del cd[name] additional_closure_functions_to_update.append(func) annotation = inspect.signature(func).return_annotation if annotation is not inspect.Parameter.empty: class_annotations[name] = annotation + doclines = [] + if func.__doc__ is not None: + doclines.extend(textwrap.dedent(func.__doc__).splitlines()) + names[name] = "\n".join(doclines) original_getattr = cd.get("__getattr__") if original_getattr is not None: @@ -949,7 +953,7 @@ def _create_slots_class(self): if self._cache_hash: slot_names.append(_HASH_CACHE_FIELD) - cd["__slots__"] = tuple(slot_names) + cd["__slots__"] = {slot: names.get(slot) for slot in slot_names} cd["__qualname__"] = self._cls.__qualname__ @@ -987,6 +991,10 @@ def _create_slots_class(self): else: if match: cell.cell_contents = cls + # Add the cached properties back to the __dict__ again -- + # they won't be used, not being in the *instance* __dict__, but + # Sphinx checks __dict__ directly, and will therefore see these. + cd.update(cached_properties) return cls def add_repr(self, ns): diff --git a/tests/test_slots.py b/tests/test_slots.py index a74c32b03..d9be34066 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -1034,7 +1034,7 @@ def f(self): return self.x * 2 assert B(1).f == 2 - assert B.__slots__ == () + assert list(B.__slots__) == [] def test_slots_sub_class_with_actual_slot(): @@ -1055,7 +1055,7 @@ class B(A): f: int = attr.ib() assert B(1, 2).f == 2 - assert B.__slots__ == () + assert list(B.__slots__) == [] def test_slots_cached_property_is_not_called_at_construction():