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 from i3pystatus.core.color import ColorRangeModule IGNORE_MODULES = ("__main__", "core", "tools") def is_module(obj): return (isinstance(obj, type) and issubclass(obj, i3pystatus.core.settings.SettingsBase) and not obj.__module__.startswith("i3pystatus.core.")) def fail_on_missing_dependency_hints(obj, lines): # We can automatically check in some cases if we forgot something if issubclass(obj, ColorRangeModule): if all("colour" not in line for line in lines): raise ValueError(">>> Module <{}> uses ColorRangeModule and should document it <<<\n" "> Requires the PyPI package ``colour``".format(obj.__name__)) def check_settings_consistency(obj, settings): errs = [] for setting in settings: if not setting.required and setting.default is setting.sentinel: errs.append("<" + setting.name + ">") if errs: raise ValueError(">>> Module <{}> has non-required setting(s) {} with no default! <<<\n" .format(obj.__name__, ", ".join(errs))) 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 self.name 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: fail_on_missing_dependency_hints(obj, lines) if issubclass(obj, i3pystatus.core.modules.Module): mod = obj.__module__ if mod.startswith("i3pystatus."): mod = mod[len("i3pystatus."):] lines[0:0] = [ ".. raw:: html", "", "
" + mod + "
" +
"(class " + name + "
)" +
"" + name + "