Skip to content

Commit

Permalink
Merge pull request #181 from kylefawcett/fix/slots
Browse files Browse the repository at this point in the history
Updated "Use __dict__ and ignore __slots__ on classes #169
  • Loading branch information
bsipocz authored Jan 27, 2024
2 parents 7c57ce1 + 5904a17 commit 57e934e
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
19 changes: 2 additions & 17 deletions sphinx_automodapi/automodsumm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions sphinx_automodapi/tests/cases/slots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test classes that put attributes in `__slots__`.
1 change: 1 addition & 0 deletions sphinx_automodapi/tests/cases/slots/input/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. automodapi:: sphinx_automodapi.tests.example_module.slots
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions sphinx_automodapi/tests/cases/slots/output/index.rst.automodapi
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. currentmodule:: sphinx_automodapi.tests.example_module.slots

.. autosummary::
:toctree: api

SlotDict
DerivedParam
DerivedSlotParam
116 changes: 116 additions & 0 deletions sphinx_automodapi/tests/example_module/slots.py
Original file line number Diff line number Diff line change
@@ -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}")
10 changes: 10 additions & 0 deletions sphinx_automodapi/tests/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 57e934e

Please sign in to comment.