i3pystatus/docs/module_docs.py
2014-10-12 16:50:50 +02:00

151 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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):
return isinstance(obj, type) \
and issubclass(obj, i3pystatus.core.settings.SettingsBase) \
and not obj.__module__.startswith("i3pystatus.core.")
def process_docstring(app, what, name, obj, options, lines):
class Setting:
doc = ""
required = False
default = sentinel = object()
empty = object()
def __init__(self, cls, setting):
if isinstance(setting, tuple):
self.name = setting[0]
self.doc = setting[1]
else:
self.name = setting
if setting in cls.required:
self.required = True
elif hasattr(cls, self.name):
default = getattr(cls, self.name)
if isinstance(default, str) and not len(default)\
or default is None:
default = self.empty
self.default = default
def __str__(self):
attrs = []
if self.required:
attrs.append("required")
if self.default not in [self.sentinel, self.empty]:
attrs.append("default: ``{default}``".format(default=self.default))
if self.default is self.empty:
attrs.append("default: *empty*")
formatted = "* **{name}** {attrsf} {doc}".format(
name=self.name,
doc=" " + self.doc if self.doc else "",
attrsf=" ({attrs})".format(attrs=", ".join(attrs)) if attrs else "")
return formatted
if is_module(obj) and obj.settings:
lines.append(".. rubric:: Settings")
lines.append("")
for setting in obj.settings:
lines.append(str(Setting(obj, setting)))
lines.append("")
def process_signature(app, what, name, obj, options, signature, return_annotation):
if is_module(obj):
return ("", return_annotation)
def get_modules(path, name):
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_module(finder, modname):
fullname = "i3pystatus.{modname}".format(modname=modname)
return (modname, finder.find_loader(fullname)[0].load_module(fullname))
def get_all(module_path, modname, basecls):
mods = []
finder = ClassFinder(basecls)
for name, module in get_modules(module_path, modname):
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, name, basecls):
modules = get_all(path, name, basecls)
contents = []
for mod in modules:
contents.append(" * :py:mod:`~{}`".format(mod[0]))
contents.append("")
for mod in modules:
contents.append(".. _{}:\n".format(mod[0].split(".")[-1]))
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, modname, basecls))
node = paragraph()
self.state.nested_parse(StringList(contents), 0, node)
return [node]
def setup(app: sphinx.application.Sphinx):
app.add_directive("autogen", AutogenDirective)
app.connect("autodoc-process-docstring", process_docstring)
app.connect("autodoc-process-signature", process_signature)