Source code for logilab.common.sphinxutils

# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-common.
#
# logilab-common is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option) any
# later version.
#
# logilab-common is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
"""Sphinx utils

ModuleGenerator: Generate a file that lists all the modules of a list of
packages in order to pull all the docstring.
This should not be used in a makefile to systematically generate sphinx
documentation!

Typical usage:

>>> from logilab.common.sphinxutils import ModuleGenerator
>>> mgen = ModuleGenerator('logilab common', '/home/adim/src/logilab/common')
>>> mgen.generate('api_logilab_common.rst', exclude_dirs=('test',))
"""

import sys
import os.path as osp
import inspect

from logilab.common import STD_BLACKLIST
from logilab.common.shellutils import globfind
from logilab.common.modutils import modpath_from_file


[docs]def module_members(module): members = [] for name, value in inspect.getmembers(module): if getattr(value, "__module__", None) == module.__name__: members.append((name, value)) return sorted(members)
[docs]def class_members(klass): return sorted( [ name for name in vars(klass) if name not in ("__doc__", "__module__", "__dict__", "__weakref__") ] )
[docs]class ModuleGenerator: file_header = """.. -*- coding: utf-8 -*-\n\n%s\n""" module_def = """ :mod:`%s` =======%s .. automodule:: %s :members: %s """ class_def = """ .. autoclass:: %s :members: %s """ def __init__(self, project_title, code_dir): self.title = project_title self.code_dir = osp.abspath(code_dir)
[docs] def generate(self, dest_file, exclude_dirs=STD_BLACKLIST): """make the module file""" self.fn = open(dest_file, "w") num = len(self.title) + 6 title = "=" * num + f"\n {self.title} API\n" + "=" * num self.fn.write(self.file_header % title) self.gen_modules(exclude_dirs=exclude_dirs) self.fn.close()
[docs] def gen_modules(self, exclude_dirs): """generate all modules""" for module in self.find_modules(exclude_dirs): modname = module.__name__ classes = [] modmembers = [] for objname, obj in module_members(module): if inspect.isclass(obj): classmembers = class_members(obj) classes.append((objname, classmembers)) else: modmembers.append(objname) self.fn.write( self.module_def % (modname, "=" * len(modname), modname, ", ".join(modmembers)) ) for klass, members in classes: self.fn.write(self.class_def % (klass, ", ".join(members)))
[docs] def find_modules(self, exclude_dirs): basepath = osp.dirname(self.code_dir) basedir = osp.basename(basepath) + osp.sep if basedir not in sys.path: sys.path.insert(1, basedir) for filepath in globfind(self.code_dir, "*.py", exclude_dirs): if osp.basename(filepath) in ("setup.py", "__pkginfo__.py"): continue dotted_path = modpath_from_file(filepath) module = type(".".join(dotted_path), (), {}) # mock it yield module
if __name__ == "__main__": # example : title, code_dir, outfile = sys.argv[1:] generator = ModuleGenerator(title, code_dir) # XXX modnames = ['logilab'] generator.generate(outfile, ("test", "tests", "examples", "data", "doc", ".hg", "migration"))