"""
This extension automatically generates AutoSummaries for modules/classes.
This extension can be enabled by adding ``"nengo_sphinx_theme.ext.autoautosummary"``
to the ``extensions`` list in ``conf.py``.
"""
import inspect
from docutils.parsers.rst import directives
import sphinx.ext.autosummary as autosummary
# We import nengo_sphinx_theme here to test the issue that
# `patch_autosummary_import_by_name` fixes.
import nengo_sphinx_theme # pylint: disable=unused-import
# should be ignored
a_test_attribute = None
[docs]def a_test_function():
"""This is a test function."""
[docs]class TestClass:
"""This is a test class."""
# should be ignored
an_attribute = None
def __init__(self):
"""This is the init method."""
[docs] def a_method(self):
"""This is a method."""
[docs] @staticmethod
def a_static_method():
"""This is a static method."""
[docs] def _a_private_method(self):
"""A private method."""
[docs] def _another_private_method(self):
"""This method will be manually added."""
[docs]class AutoAutoSummary(autosummary.Autosummary):
"""
Automatically generates a summary for a class or module.
For classes this adds a summary for all methods.
For modules this adds a summary for all classes/functions.
"""
option_spec = {
"nosignatures": directives.unchanged,
"exclude-members": directives.unchanged,
}
required_arguments = 1
def get_members(self, obj):
if inspect.isclass(obj):
module_name = obj.__module__
def filter(x):
return inspect.isroutine(x)
elif inspect.ismodule(obj):
module_name = obj.__name__
def filter(x):
return inspect.isclass(x) or inspect.isfunction(x)
else:
raise TypeError(
"AutoAutoSummary only works with classes or modules (got %s)"
% type(obj)
)
excluded = [
x.strip() for x in self.options.get("exclude-members", "").split(",")
]
items = []
# note: we use __dict__ because it preserves the order of attribute definitions
# (in python >= 3.6)
for name in obj.__dict__:
if not (name.startswith("_") or name in excluded):
attr = getattr(obj, name)
if filter(attr) and attr.__module__ == module_name:
items.append(name)
return items
def run(self):
clazz = str(self.arguments[0])
(module_name, obj_name) = clazz.rsplit(".", 1)
mod = __import__(module_name, globals(), locals(), [obj_name])
obj = getattr(mod, obj_name)
new_content = ["%s.%s" % (clazz, item) for item in self.get_members(obj)]
if inspect.isclass(obj):
# add the class itself
new_content = [clazz] + new_content
self.content = new_content + self.content.data
return super(AutoAutoSummary, self).run()
[docs]def patch_autosummary_import_by_name():
"""Monkeypatch a function in autosummary to disallow module cycles"""
orig_f = autosummary.import_by_name
def import_by_name(name, prefixes):
# Filter out problematic prefixes
prefixes = [p for p in prefixes if p is None or not name.startswith(p)]
return orig_f(name, prefixes)
autosummary.import_by_name = import_by_name
def setup(app):
patch_autosummary_import_by_name()
app.add_directive("autoautosummary", AutoAutoSummary)