diff options
author | Adrien Di Mascio <Adrien.DiMascio@logilab.fr> | 2009-07-31 19:07:43 +0200 |
---|---|---|
committer | Adrien Di Mascio <Adrien.DiMascio@logilab.fr> | 2009-07-31 19:07:43 +0200 |
commit | fb2ee10b7d82d8e93c5d980f33bbb312fc36eb72 (patch) | |
tree | 0664f54506718d2f93beb192e9b65e2fe2993799 /sphinxutils.py | |
parent | daca0d1543e91bec9cdcef0f68e28f70271c1779 (diff) | |
download | logilab-common-fb2ee10b7d82d8e93c5d980f33bbb312fc36eb72.tar.gz |
[sphinx] simplify sphinxutils and make it generate ..autoclass directives
Diffstat (limited to 'sphinxutils.py')
-rw-r--r-- | sphinxutils.py | 161 |
1 files changed, 77 insertions, 84 deletions
diff --git a/sphinxutils.py b/sphinxutils.py index 295d407..5dd0424 100644 --- a/sphinxutils.py +++ b/sphinxutils.py @@ -5,108 +5,101 @@ Sphinx utils: 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 os, 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 load_module_from_file, modpath_from_file + +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) + +def class_members(klass): + return sorted([name for name in vars(klass) + if name not in ('__doc__', '__module__', + '__dict__', '__weakref__')]) class ModuleGenerator: + file_header = """.. -*- coding: utf-8 -*-\n\n%s\n""" + module_def = """ +:mod:`%s` +=======%s - file_header = """.. -*- coding: utf-8 -*- \n\n%s\n""" - def __init__(self, project_title, code_dir, dest_file): - self.main_dir = code_dir - self.dest_file = dest_file - self.set_docdir() - self.mod_names = [] - self.title = project_title +.. automodule:: %s + :members: %s +""" + class_def = """ - def set_docdir(self, subfolder =""): - """set the directory for the destination path""" - self.output_fn = osp.join(self.main_dir, subfolder, self.dest_file) +.. autoclass:: %s + :members: %s - def make(self, mod_names, exclude_dirs): +""" + + def __init__(self, project_title, code_dir): + self.title = project_title + self.code_dir = osp.abspath(code_dir) + + def generate(self, dest_file, exclude_dirs=STD_BLACKLIST): """make the module file""" - self.mod_names = [osp.join(self.main_dir, name) for name in mod_names] - self.exclude_dirs = exclude_dirs - self.fn = open(self.output_fn, 'w') + self.fn = open(dest_file, 'w') num = len(self.title) + 6 title = "=" * num + "\n %s API\n" % self.title + "=" * num self.fn.write(self.file_header % title) - self.find_modules() - self.gen_modules() + self.gen_modules(exclude_dirs=exclude_dirs) self.fn.close() - def gen_modules(self): + def gen_modules(self, exclude_dirs): """generate all modules""" - for mod_name in self.find_modules(): - mod_entry = """ -:mod:`%s` -%s + 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))) + + 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 + try: + module = load_module_from_file(filepath) + except: # module might be broken or magic + dotted_path = modpath_from_file(filepath) + module = type('.'.join(dotted_path), (), {}) # mock it + yield module -.. automodule:: %s - :members: -""" % (mod_name, '='*(len(':mod:``' + mod_name)), mod_name) - self.fn.write(mod_entry) - - def find_modules(self): - """find all python modules to be documented""" - modules = [] - for mod_name in self.mod_names: - for root, dirs, files in os.walk(mod_name): - if not self.keep_module(root): - continue - for name in files: - if name == "__init__.py": - self._handle_module(root, mod_name, modules) - elif (name.endswith(".py") and name != "__pkginfo__.py" - and "__init__.py" in files): - filename = osp.join(root, name.split('.py')[0]) - self._handle_module(filename, mod_name, modules) - return modules - - def _handle_module(self, filename, modname, modules): - """handle a module""" - if self.format_mod_name(filename, modname) not in modules: - modules.append(self.format_mod_name(filename, modname)) - - def format_mod_name(self, path, mod_name): - mod_root = mod_name.split('/')[-1] - mod_end = path.split(mod_root)[-1] - return mod_root + mod_end.replace('/', '.') - - def keep_module(self, mod_end): - """Filter modules in order to exclude specific package directories""" - for dir in self.exclude_dirs: - if mod_end.find(dir) != -1: - return False - return True - - -def generate_modules_file(args): - """generate all module files""" - # TODO : use lgc options - if len(args) != 3: - - print """ -Two arguments required: - generate_modules [project-title] [code-dir] [file-out] - -[project-title] : title of the project to be documented -[code-dir] : full path to the code directory -[file-out] : rest file containing the list of modules for Sphinx -""" - sys.exit() - project_title = args[0] - code_dir = args[1] - destfile = args[2] - mg = ModuleGenerator(project_title, code_dir, destfile) - return mg if __name__ == '__main__': # example : - mg = generate_modules_file(sys.argv[1:]) - modnames = ['logilab'] - exclude = ('test', 'tests', 'examples', 'data', 'doc', '.hg', 'migration') - mg.make(mod_names, exclude) - + title, code_dir, outfile = sys.argv[1:] + generator = ModuleGenerator(title, code_dir) + # XXX modnames = ['logilab'] + generator.make(outfile, ('test', 'tests', 'examples', + 'data', 'doc', '.hg', 'migration')) |