#!/usr/bin/env python import sys import io import pkgutil from collections import namedtuple import textwrap import i3pystatus import i3pystatus.mail from .core.imputil import ClassFinder IGNORE = ("__main__", "mkdocs") MODULE_FORMAT = """ {heading} {name} {doc} {settings} {endstring}\n""" class Module: name = "" doc = "" endstring = "" def __init__(self, cls, neighbours, module_name, module, heading): self.settings = [] self.cls = cls self.heading = heading if neighbours == 1: self.name = module_name else: self.name = "{module}.{cls}".format(module=module_name, cls=self.cls.__name__) self.doc = self.cls.__doc__ or module.__doc__ or "" if hasattr(self.cls, "_endstring"): self.endstring = self.cls._endstring self.read_settings() def read_settings(self): for setting in self.cls.settings: self.settings.append(Setting(self.cls, setting)) def format_settings(self): return "\n".join(map(str, self.settings)) def __str__(self): return MODULE_FORMAT.format( name=self.name, doc=textwrap.dedent(self.doc), settings=self.format_settings(), heading=self.heading, endstring=self.endstring ) class Setting: doc = "" required = False default = sentinel = 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): self.default = getattr(cls, self.name) def __str__(self): attrs = [] if self.required: attrs.append("required") if self.default is not self.sentinel: attrs.append("default: `{default}`".format(default=self.default)) formatted = "* `{name}` ".format(name=self.name) if self.doc or attrs: formatted += "— " if self.doc: formatted += self.doc if attrs: formatted += " ({attrs})".format(attrs=", ".join(attrs)) return formatted def get_modules(path): modules = [] for finder, modname, ispkg in pkgutil.iter_modules(path): if modname not in IGNORE: 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, heading, finder=None): mods = [] if not finder: finder = ClassFinder(i3pystatus.Module) for name, module in get_modules(module_path): classes = finder.search_module(module) for cls in classes: mods.append(Module(cls, neighbours=len(classes), module_name=name, module=module, heading=heading)) return sorted(mods, key=lambda module: module.name) def generate_doc_for_module(module_path, heading="###", finder=None): return "".join(map(str, get_all(module_path, heading, finder))) with open("README.tpl.md", "r") as template: tpl = template.read() tpl = tpl.replace("!!module_doc!!", generate_doc_for_module(i3pystatus.__path__)) finder = ClassFinder(baseclass=i3pystatus.mail.Backend) tpl = tpl.replace("!!i3pystatus.mail!!", generate_doc_for_module(i3pystatus.mail.__path__, "###", finder).replace("\n", "\n> ")) with open("README.md", "w") as output: output.write(tpl + "\n")