summaryrefslogtreecommitdiff
path: root/sphinxutils.py
diff options
context:
space:
mode:
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>2009-07-31 19:07:43 +0200
committerAdrien Di Mascio <Adrien.DiMascio@logilab.fr>2009-07-31 19:07:43 +0200
commitfb2ee10b7d82d8e93c5d980f33bbb312fc36eb72 (patch)
tree0664f54506718d2f93beb192e9b65e2fe2993799 /sphinxutils.py
parentdaca0d1543e91bec9cdcef0f68e28f70271c1779 (diff)
downloadlogilab-common-fb2ee10b7d82d8e93c5d980f33bbb312fc36eb72.tar.gz
[sphinx] simplify sphinxutils and make it generate ..autoclass directives
Diffstat (limited to 'sphinxutils.py')
-rw-r--r--sphinxutils.py161
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'))