diff --git a/CHANGES.rst b/CHANGES.rst index f8bb685..0e98011 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changes in sphinx-automodapi 0.17.0 (unreleased) ------------------- +- Fixes issue where ``__slots__`` hides class variables. [#181] + - Minimum supported Python version is now 3.8. [#177] 0.16.0 (2023-08-17) diff --git a/sphinx_automodapi/automodsumm.py b/sphinx_automodapi/automodsumm.py index ec99ce8..ecdd3fe 100644 --- a/sphinx_automodapi/automodsumm.py +++ b/sphinx_automodapi/automodsumm.py @@ -83,7 +83,6 @@ class members that are inherited from a base class. This value can be .. _sphinx.ext.inheritance_diagram: http://sphinx-doc.org/latest/ext/inheritance.html """ -import abc import inspect import os import re @@ -555,25 +554,11 @@ def get_members_class(obj, typ, include_public=[], items = [] # using dir gets all of the attributes, including the elements - # from the base class, otherwise use __slots__ or __dict__ + # from the base class, otherwise use __dict__ if include_base: names = dir(obj) else: - # Classes deriving from an ABC using the `abc` module will - # have an empty `__slots__` attribute in Python 3, unless - # other slots were declared along the inheritance chain. If - # the ABC-derived class has empty slots, we'll use the - # class `__dict__` instead. - declares_slots = ( - hasattr(obj, '__slots__') and - not (issubclass(type(obj), abc.ABCMeta) and - len(obj.__slots__) == 0) - ) - - if declares_slots: - names = tuple(getattr(obj, '__slots__')) - else: - names = getattr(obj, '__dict__').keys() + names = getattr(obj, '__dict__').keys() for name in names: try: diff --git a/sphinx_automodapi/tests/cases/slots/README.md b/sphinx_automodapi/tests/cases/slots/README.md new file mode 100644 index 0000000..1b95d31 --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/README.md @@ -0,0 +1 @@ +Test classes that put attributes in `__slots__`. \ No newline at end of file diff --git a/sphinx_automodapi/tests/cases/slots/input/index.rst b/sphinx_automodapi/tests/cases/slots/input/index.rst new file mode 100644 index 0000000..fdf9586 --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/input/index.rst @@ -0,0 +1 @@ +.. automodapi:: sphinx_automodapi.tests.example_module.slots diff --git a/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedParam.rst b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedParam.rst new file mode 100644 index 0000000..bf65bf4 --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedParam.rst @@ -0,0 +1,17 @@ +DerivedParam +============ + +.. currentmodule:: sphinx_automodapi.tests.example_module.slots + +.. autoclass:: DerivedParam + :show-inheritance: + + .. rubric:: Methods Summary + + .. autosummary:: + + ~DerivedParam.derived_from_slot_class_method + + .. rubric:: Methods Documentation + + .. automethod:: derived_from_slot_class_method diff --git a/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedSlotParam.rst b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedSlotParam.rst new file mode 100644 index 0000000..3a2438c --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.DerivedSlotParam.rst @@ -0,0 +1,27 @@ +DerivedSlotParam +================ + +.. currentmodule:: sphinx_automodapi.tests.example_module.slots + +.. autoclass:: DerivedSlotParam + :show-inheritance: + + .. rubric:: Attributes Summary + + .. autosummary:: + + ~DerivedSlotParam.extra_attr + + .. rubric:: Methods Summary + + .. autosummary:: + + ~DerivedSlotParam.derived_from_slot_class_method + + .. rubric:: Attributes Documentation + + .. autoattribute:: extra_attr + + .. rubric:: Methods Documentation + + .. automethod:: derived_from_slot_class_method diff --git a/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.SlotDict.rst b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.SlotDict.rst new file mode 100644 index 0000000..ccdf348 --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/output/api/sphinx_automodapi.tests.example_module.slots.SlotDict.rst @@ -0,0 +1,29 @@ +SlotDict +======== + +.. currentmodule:: sphinx_automodapi.tests.example_module.slots + +.. autoclass:: SlotDict + :show-inheritance: + + .. rubric:: Attributes Summary + + .. autosummary:: + + ~SlotDict.class_attr + ~SlotDict.instance_attr + + .. rubric:: Methods Summary + + .. autosummary:: + + ~SlotDict.my_method + + .. rubric:: Attributes Documentation + + .. autoattribute:: class_attr + .. autoattribute:: instance_attr + + .. rubric:: Methods Documentation + + .. automethod:: my_method diff --git a/sphinx_automodapi/tests/cases/slots/output/index.rst.automodapi b/sphinx_automodapi/tests/cases/slots/output/index.rst.automodapi new file mode 100644 index 0000000..487ab9f --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/output/index.rst.automodapi @@ -0,0 +1,19 @@ + +sphinx_automodapi.tests.example_module.slots Module +--------------------------------------------------- + +.. automodule:: sphinx_automodapi.tests.example_module.slots + +Classes +^^^^^^^ + +.. automodsumm:: sphinx_automodapi.tests.example_module.slots + :classes-only: + :toctree: api + +Class Inheritance Diagram +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automod-diagram:: sphinx_automodapi.tests.example_module.slots + :private-bases: + :parts: 1 diff --git a/sphinx_automodapi/tests/cases/slots/output/index.rst.automodsumm b/sphinx_automodapi/tests/cases/slots/output/index.rst.automodsumm new file mode 100644 index 0000000..83b7aa7 --- /dev/null +++ b/sphinx_automodapi/tests/cases/slots/output/index.rst.automodsumm @@ -0,0 +1,8 @@ +.. currentmodule:: sphinx_automodapi.tests.example_module.slots + +.. autosummary:: + :toctree: api + + SlotDict + DerivedParam + DerivedSlotParam diff --git a/sphinx_automodapi/tests/example_module/slots.py b/sphinx_automodapi/tests/example_module/slots.py new file mode 100644 index 0000000..ee9e0fd --- /dev/null +++ b/sphinx_automodapi/tests/example_module/slots.py @@ -0,0 +1,116 @@ +"""Test classes containing __slots__ + +Instance attributes named in ``__slots__`` can be introspected and are listed +in the Attributes section of the class documentation. Class attributes are +listed in the same section of the generated docs so docstrings should be used +to distinguish class attributes vs instance attributes. Regular instance +attributes are dynamically inserted into ``__dict__`` and cannot be reliably +introspected so they're not included in the documentation. +""" +from __future__ import annotations + +__all__ = ['SlotDict', 'DerivedParam', 'DerivedSlotParam',] + + +class SlotDict(object): + """ + A class that uses __slots__ and __dict__ for its attribute namespace. + """ + __slots__ = { + "instance_attr": "instance attribute docstring can be added here", + "__dict__": None, # Allows additional instance attributes to be added + } + + class_attr = "class attribute value" + """(class attr) this is a class attribute.""" + + def __init__(self, param: str, other_param: str): + """ + Initializes a SlotDict object. + + Parameters + ---------- + param : str + A parameter + other_param : str + Another parameter + """ + + self.instance_attr = param + """Instance attributes declared in slots can also define their docstring + here + """ + + if other_param is not None: + self.other_attr = other_param + """This instance attribute is dynamic (not declared in a slot) so + it's not included in the docs + """ + + def my_method(self): + """ + Prints the SlotDict parameters. + """ + print(f"instance_attr: {self.instance_attr}") + print(f"other_attr: {self.other_attr}") + + +class DerivedParam(SlotDict): + """ + Extends SlotDict by adding an extra parameter + """ + def __init__(self, param: str, other_param: str, extra_param: str): + """ + Initializes a DerivedParam object. + + Parameters + ---------- + param : str + A parameter + other_param : str + Another parameter + extra_param : str + An extra parameter + """ + super(DerivedParam, self).__init__(param, other_param) + self.extra_attr = extra_param + + def derived_from_slot_class_method(self): + """ + Prints the DerivedParam parameters. + """ + print(f"instance_attr: {self.instance_attr}") + print(f"other_attr: {self.other_attr}") + print(f"extra_attr: {self.extra_attr}") + + +class DerivedSlotParam(SlotDict): + """ + Extends SlotDict by adding a slot parameter + """ + + __slots__ = ('extra_attr',) + + def __init__(self, param: str, other_param: str, extra_param: str): + """ + Initializes a DerivedSlotParam object. + + Parameters + ---------- + param : str + A parameter + other_param : str + Another parameter + extra_param : str + An extra parameter + """ + super(DerivedSlotParam, self).__init__(param, other_param) + self.extra_attr = extra_param + + def derived_from_slot_class_method(self): + """ + Prints the DerivedSlotParam parameters. + """ + print(f"instance_attr: {self.instance_attr}") + print(f"other_attr: {self.other_attr}") + print(f"extra_attr: {self.extra_attr}") diff --git a/sphinx_automodapi/tests/test_cases.py b/sphinx_automodapi/tests/test_cases.py index 63b0171..0ffa4c8 100644 --- a/sphinx_automodapi/tests/test_cases.py +++ b/sphinx_automodapi/tests/test_cases.py @@ -140,3 +140,13 @@ def test_duplicated_warning(tmpdir): os.chdir(start_dir) assert status == 0 + + +def test_slots_example(): + """Basic tests for slots example module""" + from sphinx_automodapi.tests.example_module.slots import ( + SlotDict, DerivedParam, DerivedSlotParam + ) + SlotDict('param', 'other_param').my_method() + DerivedParam('param', 'other_param', 'extra_param').derived_from_slot_class_method() + DerivedSlotParam('param', 'other_param', 'extra_param').derived_from_slot_class_method()