summaryrefslogtreecommitdiff
path: root/doc/sphinxext/autosummary_generate.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/sphinxext/autosummary_generate.py')
-rwxr-xr-xdoc/sphinxext/autosummary_generate.py189
1 files changed, 189 insertions, 0 deletions
diff --git a/doc/sphinxext/autosummary_generate.py b/doc/sphinxext/autosummary_generate.py
new file mode 100755
index 000000000..9e2c6c771
--- /dev/null
+++ b/doc/sphinxext/autosummary_generate.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+r"""
+autosummary_generate.py OPTIONS FILES
+
+Generate automatic RST source files for items referred to in
+autosummary:: directives.
+
+Each generated RST file contains a single auto*:: directive which
+extracts the docstring of the referred item.
+
+Example Makefile rule::
+
+ generate:
+ ./ext/autosummary_generate.py -o source/generated source/*.rst
+
+"""
+import glob, re, inspect, os, optparse
+from autosummary import import_by_name
+
+try:
+ from phantom_import import import_phantom_module
+except ImportError:
+ import_phantom_module = lambda x: x
+
+def main():
+ p = optparse.OptionParser(__doc__.strip())
+ p.add_option("-p", "--phantom", action="store", type="string",
+ dest="phantom", default=None,
+ help="Phantom import modules from a file")
+ p.add_option("-o", "--output-dir", action="store", type="string",
+ dest="output_dir", default=None,
+ help=("Write all output files to the given directory (instead "
+ "of writing them as specified in the autosummary:: "
+ "directives)"))
+ options, args = p.parse_args()
+
+ if len(args) == 0:
+ p.error("wrong number of arguments")
+
+ if options.phantom and os.path.isfile(options.phantom):
+ import_phantom_module(options.phantom)
+
+ # read
+ names = {}
+ for name, loc in get_documented(args).items():
+ for (filename, sec_title, keyword, toctree) in loc:
+ if toctree is not None:
+ path = os.path.join(os.path.dirname(filename), toctree)
+ names[name] = os.path.abspath(path)
+
+ # write
+ for name, path in sorted(names.items()):
+ if options.output_dir is not None:
+ path = options.output_dir
+
+ if not os.path.isdir(path):
+ os.makedirs(path)
+
+ try:
+ obj, name = import_by_name(name)
+ except ImportError, e:
+ print "Failed to import '%s': %s" % (name, e)
+ continue
+
+ fn = os.path.join(path, '%s.rst' % name)
+
+ if os.path.exists(fn):
+ # skip
+ continue
+
+ f = open(fn, 'w')
+
+ try:
+ f.write('%s\n%s\n\n' % (name, '='*len(name)))
+
+ if inspect.isclass(obj):
+ if issubclass(obj, Exception):
+ f.write(format_modulemember(name, 'autoexception'))
+ else:
+ f.write(format_modulemember(name, 'autoclass'))
+ elif inspect.ismodule(obj):
+ f.write(format_modulemember(name, 'automodule'))
+ elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj):
+ f.write(format_classmember(name, 'automethod'))
+ elif callable(obj):
+ f.write(format_modulemember(name, 'autofunction'))
+ elif hasattr(obj, '__get__'):
+ f.write(format_classmember(name, 'autoattribute'))
+ else:
+ f.write(format_modulemember(name, 'autofunction'))
+ finally:
+ f.close()
+
+def format_modulemember(name, directive):
+ parts = name.split('.')
+ mod, name = '.'.join(parts[:-1]), parts[-1]
+ return ".. currentmodule:: %s\n\n.. %s:: %s\n" % (mod, directive, name)
+
+def format_classmember(name, directive):
+ parts = name.split('.')
+ mod, name = '.'.join(parts[:-2]), '.'.join(parts[-2:])
+ return ".. currentmodule:: %s\n\n.. %s:: %s\n" % (mod, directive, name)
+
+def get_documented(filenames):
+ """
+ Find out what items are documented in source/*.rst
+
+ Returns
+ -------
+ documented : dict of list of (filename, title, keyword, toctree)
+ Dictionary whose keys are documented names of objects.
+ The value is a list of locations where the object was documented.
+ Each location is a tuple of filename, the current section title,
+ the name of the directive, and the value of the :toctree: argument
+ (if present) of the directive.
+
+ """
+ title_underline_re = re.compile("^[-=*_^#]{3,}\s*$")
+ autodoc_re = re.compile(".. auto(function|method|attribute|class|exception|module)::\s*([A-Za-z0-9_.]+)\s*$")
+ autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*')
+ module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$')
+ autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*')
+ toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
+
+ documented = {}
+
+ for filename in filenames:
+ current_title = []
+ last_line = None
+ toctree = None
+ current_module = None
+ in_autosummary = False
+
+ f = open(filename, 'r')
+ for line in f:
+ try:
+ if in_autosummary:
+ m = toctree_arg_re.match(line)
+ if m:
+ toctree = m.group(1)
+ continue
+
+ if line.strip().startswith(':'):
+ continue # skip options
+
+ m = autosummary_item_re.match(line)
+ if m:
+ name = m.group(1).strip()
+ if current_module and not name.startswith(current_module + '.'):
+ name = "%s.%s" % (current_module, name)
+ documented.setdefault(name, []).append(
+ (filename, current_title, 'autosummary', toctree))
+ continue
+ if line.strip() == '':
+ continue
+ in_autosummary = False
+
+ m = autosummary_re.match(line)
+ if m:
+ in_autosummary = True
+ continue
+
+ m = autodoc_re.search(line)
+ if m:
+ name = m.group(2).strip()
+ if current_module and not name.startswith(current_module + '.'):
+ name = "%s.%s" % (current_module, name)
+ if m.group(1) == "module":
+ current_module = name
+ documented.setdefault(name, []).append(
+ (filename, current_title, "auto" + m.group(1), None))
+ continue
+
+ m = title_underline_re.match(line)
+ if m and last_line:
+ current_title = last_line.strip()
+ continue
+
+ m = module_re.match(line)
+ if m:
+ current_module = m.group(2)
+ continue
+ finally:
+ last_line = line
+
+ return documented
+
+if __name__ == "__main__":
+ main()