Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated "Use __dict__ and ignore __slots__ on classes #169 #181

Merged
merged 8 commits into from
Jan 27, 2024
Merged
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
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()