Skip to content

Commit 57e934e

Browse files
authored
Merge pull request #181 from kylefawcett/fix/slots
Updated "Use __dict__ and ignore __slots__ on classes #169
2 parents 7c57ce1 + 5904a17 commit 57e934e

11 files changed

+232
-17
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Changes in sphinx-automodapi
44
0.17.0 (unreleased)
55
-------------------
66

7+
- Fixes issue where ``__slots__`` hides class variables. [#181]
8+
79
- Minimum supported Python version is now 3.8. [#177]
810

911
0.16.0 (2023-08-17)

sphinx_automodapi/automodsumm.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ class members that are inherited from a base class. This value can be
8383
.. _sphinx.ext.inheritance_diagram: http://sphinx-doc.org/latest/ext/inheritance.html
8484
"""
8585

86-
import abc
8786
import inspect
8887
import os
8988
import re
@@ -555,25 +554,11 @@ def get_members_class(obj, typ, include_public=[],
555554
items = []
556555

557556
# using dir gets all of the attributes, including the elements
558-
# from the base class, otherwise use __slots__ or __dict__
557+
# from the base class, otherwise use __dict__
559558
if include_base:
560559
names = dir(obj)
561560
else:
562-
# Classes deriving from an ABC using the `abc` module will
563-
# have an empty `__slots__` attribute in Python 3, unless
564-
# other slots were declared along the inheritance chain. If
565-
# the ABC-derived class has empty slots, we'll use the
566-
# class `__dict__` instead.
567-
declares_slots = (
568-
hasattr(obj, '__slots__') and
569-
not (issubclass(type(obj), abc.ABCMeta) and
570-
len(obj.__slots__) == 0)
571-
)
572-
573-
if declares_slots:
574-
names = tuple(getattr(obj, '__slots__'))
575-
else:
576-
names = getattr(obj, '__dict__').keys()
561+
names = getattr(obj, '__dict__').keys()
577562

578563
for name in names:
579564
try:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Test classes that put attributes in `__slots__`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.. automodapi:: sphinx_automodapi.tests.example_module.slots
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
DerivedParam
2+
============
3+
4+
.. currentmodule:: sphinx_automodapi.tests.example_module.slots
5+
6+
.. autoclass:: DerivedParam
7+
:show-inheritance:
8+
9+
.. rubric:: Methods Summary
10+
11+
.. autosummary::
12+
13+
~DerivedParam.derived_from_slot_class_method
14+
15+
.. rubric:: Methods Documentation
16+
17+
.. automethod:: derived_from_slot_class_method
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
DerivedSlotParam
2+
================
3+
4+
.. currentmodule:: sphinx_automodapi.tests.example_module.slots
5+
6+
.. autoclass:: DerivedSlotParam
7+
:show-inheritance:
8+
9+
.. rubric:: Attributes Summary
10+
11+
.. autosummary::
12+
13+
~DerivedSlotParam.extra_attr
14+
15+
.. rubric:: Methods Summary
16+
17+
.. autosummary::
18+
19+
~DerivedSlotParam.derived_from_slot_class_method
20+
21+
.. rubric:: Attributes Documentation
22+
23+
.. autoattribute:: extra_attr
24+
25+
.. rubric:: Methods Documentation
26+
27+
.. automethod:: derived_from_slot_class_method
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
SlotDict
2+
========
3+
4+
.. currentmodule:: sphinx_automodapi.tests.example_module.slots
5+
6+
.. autoclass:: SlotDict
7+
:show-inheritance:
8+
9+
.. rubric:: Attributes Summary
10+
11+
.. autosummary::
12+
13+
~SlotDict.class_attr
14+
~SlotDict.instance_attr
15+
16+
.. rubric:: Methods Summary
17+
18+
.. autosummary::
19+
20+
~SlotDict.my_method
21+
22+
.. rubric:: Attributes Documentation
23+
24+
.. autoattribute:: class_attr
25+
.. autoattribute:: instance_attr
26+
27+
.. rubric:: Methods Documentation
28+
29+
.. automethod:: my_method
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
sphinx_automodapi.tests.example_module.slots Module
3+
---------------------------------------------------
4+
5+
.. automodule:: sphinx_automodapi.tests.example_module.slots
6+
7+
Classes
8+
^^^^^^^
9+
10+
.. automodsumm:: sphinx_automodapi.tests.example_module.slots
11+
:classes-only:
12+
:toctree: api
13+
14+
Class Inheritance Diagram
15+
^^^^^^^^^^^^^^^^^^^^^^^^^
16+
17+
.. automod-diagram:: sphinx_automodapi.tests.example_module.slots
18+
:private-bases:
19+
:parts: 1
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.. currentmodule:: sphinx_automodapi.tests.example_module.slots
2+
3+
.. autosummary::
4+
:toctree: api
5+
6+
SlotDict
7+
DerivedParam
8+
DerivedSlotParam
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""Test classes containing __slots__
2+
3+
Instance attributes named in ``__slots__`` can be introspected and are listed
4+
in the Attributes section of the class documentation. Class attributes are
5+
listed in the same section of the generated docs so docstrings should be used
6+
to distinguish class attributes vs instance attributes. Regular instance
7+
attributes are dynamically inserted into ``__dict__`` and cannot be reliably
8+
introspected so they're not included in the documentation.
9+
"""
10+
from __future__ import annotations
11+
12+
__all__ = ['SlotDict', 'DerivedParam', 'DerivedSlotParam',]
13+
14+
15+
class SlotDict(object):
16+
"""
17+
A class that uses __slots__ and __dict__ for its attribute namespace.
18+
"""
19+
__slots__ = {
20+
"instance_attr": "instance attribute docstring can be added here",
21+
"__dict__": None, # Allows additional instance attributes to be added
22+
}
23+
24+
class_attr = "class attribute value"
25+
"""(class attr) this is a class attribute."""
26+
27+
def __init__(self, param: str, other_param: str):
28+
"""
29+
Initializes a SlotDict object.
30+
31+
Parameters
32+
----------
33+
param : str
34+
A parameter
35+
other_param : str
36+
Another parameter
37+
"""
38+
39+
self.instance_attr = param
40+
"""Instance attributes declared in slots can also define their docstring
41+
here
42+
"""
43+
44+
if other_param is not None:
45+
self.other_attr = other_param
46+
"""This instance attribute is dynamic (not declared in a slot) so
47+
it's not included in the docs
48+
"""
49+
50+
def my_method(self):
51+
"""
52+
Prints the SlotDict parameters.
53+
"""
54+
print(f"instance_attr: {self.instance_attr}")
55+
print(f"other_attr: {self.other_attr}")
56+
57+
58+
class DerivedParam(SlotDict):
59+
"""
60+
Extends SlotDict by adding an extra parameter
61+
"""
62+
def __init__(self, param: str, other_param: str, extra_param: str):
63+
"""
64+
Initializes a DerivedParam object.
65+
66+
Parameters
67+
----------
68+
param : str
69+
A parameter
70+
other_param : str
71+
Another parameter
72+
extra_param : str
73+
An extra parameter
74+
"""
75+
super(DerivedParam, self).__init__(param, other_param)
76+
self.extra_attr = extra_param
77+
78+
def derived_from_slot_class_method(self):
79+
"""
80+
Prints the DerivedParam parameters.
81+
"""
82+
print(f"instance_attr: {self.instance_attr}")
83+
print(f"other_attr: {self.other_attr}")
84+
print(f"extra_attr: {self.extra_attr}")
85+
86+
87+
class DerivedSlotParam(SlotDict):
88+
"""
89+
Extends SlotDict by adding a slot parameter
90+
"""
91+
92+
__slots__ = ('extra_attr',)
93+
94+
def __init__(self, param: str, other_param: str, extra_param: str):
95+
"""
96+
Initializes a DerivedSlotParam object.
97+
98+
Parameters
99+
----------
100+
param : str
101+
A parameter
102+
other_param : str
103+
Another parameter
104+
extra_param : str
105+
An extra parameter
106+
"""
107+
super(DerivedSlotParam, self).__init__(param, other_param)
108+
self.extra_attr = extra_param
109+
110+
def derived_from_slot_class_method(self):
111+
"""
112+
Prints the DerivedSlotParam parameters.
113+
"""
114+
print(f"instance_attr: {self.instance_attr}")
115+
print(f"other_attr: {self.other_attr}")
116+
print(f"extra_attr: {self.extra_attr}")

sphinx_automodapi/tests/test_cases.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,13 @@ def test_duplicated_warning(tmpdir):
140140
os.chdir(start_dir)
141141

142142
assert status == 0
143+
144+
145+
def test_slots_example():
146+
"""Basic tests for slots example module"""
147+
from sphinx_automodapi.tests.example_module.slots import (
148+
SlotDict, DerivedParam, DerivedSlotParam
149+
)
150+
SlotDict('param', 'other_param').my_method()
151+
DerivedParam('param', 'other_param', 'extra_param').derived_from_slot_class_method()
152+
DerivedSlotParam('param', 'other_param', 'extra_param').derived_from_slot_class_method()

0 commit comments

Comments
 (0)