diff --git a/docs/conf.py b/docs/conf.py index bf4993c..b920005 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,10 +23,11 @@ import mock MOCK_MODULES = [ "alsaaudio", "netifaces", "psutil", - "lxml", "lxml.html", "lxml.cssselect", + "lxml.html", "lxml.cssselect", "lxml", "praw", - "gi.repository", "dbus", - "pywapi", "basiciw" + "gi.repository", "dbus.mainloop.glib", "dbus", + "pywapi", "basiciw", + "i3pystatus.pulseaudio.pulse" ] for mod_name in MOCK_MODULES: diff --git a/docs/i3pystatus.rst b/docs/i3pystatus.rst index 4a6dbd5..b4f0e98 100644 --- a/docs/i3pystatus.rst +++ b/docs/i3pystatus.rst @@ -1,10 +1,15 @@ Module reference ================ -.. The contents of this file are automatically extended in-memory by - the module_docs extension (see docs/module_docs.py). - Do not change the whitespace after the following directive. - Sincerely, your past self. +.. autogen:: i3pystatus Module -.. note:: List of all modules; + .. note:: List of all modules: +.. _mailbackends: + +Mail Backends +------------- + +.. autogen:: i3pystatus.mail SettingsBase + + .. nothin' diff --git a/docs/module_docs.py b/docs/module_docs.py index 58d8199..e065400 100644 --- a/docs/module_docs.py +++ b/docs/module_docs.py @@ -1,9 +1,17 @@ import pkgutil +import importlib import sphinx.application +from docutils.parsers.rst import Directive +from docutils.nodes import paragraph +from docutils.statemachine import StringList import i3pystatus.core.settings +import i3pystatus.core.modules +from i3pystatus.core.imputil import ClassFinder + +IGNORE_MODULES = ("__main__", "core") def is_module(obj): @@ -12,7 +20,6 @@ def is_module(obj): and not obj.__module__.startswith("i3pystatus.core.") - def process_docstring(app, what, name, obj, options, lines): class Setting: doc = "" @@ -51,8 +58,7 @@ def process_docstring(app, what, name, obj, options, lines): return formatted - - if is_module(obj): + if is_module(obj) and obj.settings: lines.append(".. rubric:: Settings") lines.append("") @@ -65,29 +71,76 @@ def process_signature(app, what, name, obj, options, signature, return_annotatio return ("", return_annotation) -def source_read(app, docname, source): - ignore_modules = ("__main__", "mkdocs", "core") +def get_modules(path): + modules = [] + for finder, modname, is_package in pkgutil.iter_modules(path): + if modname not in IGNORE_MODULES: + modules.append(get_module(finder, modname)) + return modules - def get_modules(path): - modules = [] - for finder, modname, is_pkg in pkgutil.iter_modules(path): - if modname not in ignore_modules: - modules.append("i3pystatus." + modname) - return modules - if docname == "i3pystatus": - modules = sorted(get_modules(i3pystatus.__path__)) +def get_module(finder, modname): + fullname = "i3pystatus.{modname}".format(modname=modname) + return (modname, finder.find_loader(fullname)[0].load_module(fullname)) - for mod in modules: - # sphinx seems to discard .append()ed items - source[0] += " * :py:class:`~{}`\n".format(mod) - for mod in modules: - source[0] += (".. automodule:: " + mod + "\n" + - " :members:\n\n") +def get_all(module_path, basecls): + mods = [] + + finder = ClassFinder(basecls) + + for name, module in get_modules(module_path): + classes = finder.get_matching_classes(module) + found = [] + for cls in classes: + if cls.__name__ not in found: + found.append(cls.__name__) + mods.append((module.__name__, cls.__name__)) + + return sorted(mods, key=lambda module: module[0]) + + +def generate_automodules(path, basecls): + modules = get_all(path, basecls) + + contents = [] + + for mod in modules: + contents.append(" * :py:mod:`~{}`".format(mod[0])) + contents.append("") + + for mod in modules: + contents.append(".. automodule:: {}".format(mod[0])) + contents.append(" :members: {}\n".format(mod[1])) + + return contents + + +class AutogenDirective(Directive): + required_arguments = 2 + has_content = True + + def run(self): + # Raise an error if the directive does not have contents. + self.assert_has_content() + + modname = self.arguments[0] + modpath = importlib.import_module(modname).__path__ + basecls = getattr(i3pystatus.core.modules, self.arguments[1]) + + contents = [] + for e in self.content: + contents.append(e) + contents.append("") + contents.extend(generate_automodules(modpath, basecls)) + + node = paragraph() + self.state.nested_parse(StringList(contents), 0, node) + return [node] def setup(app: sphinx.application.Sphinx): - app.connect("source-read", source_read) + + app.add_directive("autogen", AutogenDirective) app.connect("autodoc-process-docstring", process_docstring) app.connect("autodoc-process-signature", process_signature) diff --git a/i3pystatus/mail/__init__.py b/i3pystatus/mail/__init__.py index f16282e..c028e39 100644 --- a/i3pystatus/mail/__init__.py +++ b/i3pystatus/mail/__init__.py @@ -17,7 +17,7 @@ class Mail(IntervalModule): """ Generic mail checker - The `backends` setting determines the backends to use. + The `backends` setting determines the backends to use. For available backends see :ref:`mailbackends` """ _endstring = """!!i3pystatus.mail!!"""