Commit c3a74403 authored by peastman's avatar peastman
Browse files

Merge pull request #1238 from rmcgibbo/sphinx

New Sphinx API Docs
parents 461fcdf0 84884d29
"""
The function of this script is to render the Jinja2 templates in the current
directory into input files for sphinx. It introspects the OpenMM Python module
to find all of the classes and formats them for inclusion into the templates.
"""
from os.path import dirname, join, splitext, basename
from glob import glob
import inspect
import jinja2
import simtk.openmm
import simtk.openmm.app
def fullname(klass):
return klass.__module__ + '.' + klass.__name__
def library_template_variables():
"""Create the data structure available to the Jinja2 renderer when
filling in the templates.
This function extracts all of classes in ``simtk.openmm.openmm`` and returns
a dictionary with them grouped into three lists, the integrators, the forces,
and the remainder (library_extras).
A couple core classes are skipped, because they're included manually in the
template.
"""
data = {
'integrators': [],
'forces': [],
'library_extras': [],
}
mm_klasses = inspect.getmembers(simtk.openmm, predicate=inspect.isclass)
# gather all Force subclasses
for name, klass in mm_klasses:
if issubclass(klass, simtk.openmm.openmm.Force):
data['forces'].append(fullname(klass))
# gather all Integrator subclasses
for _, klass in mm_klasses:
if issubclass(klass, simtk.openmm.openmm.Integrator):
data['integrators'].append(fullname(klass))
# gather all extra subclasses in simtk.openmm.openmm
# core classes that are already included in library.rst.jinja2
exclude = ['simtk.openmm.openmm.Platform', 'simtk.openmm.openmm.Context',
'simtk.openmm.openmm.System', 'simtk.openmm.openmm.State']
exclude.extend(data['forces'])
exclude.extend(data['integrators'])
# these classes are useless and not worth documenting.
exclude.extend([
'simtk.openmm.openmm.SwigPyIterator',
'simtk.openmm.openmm.OpenMMException'])
for _, klass in mm_klasses:
full = fullname(klass)
if full not in exclude and not klass.__name__[0].islower():
data['library_extras'].append(full)
return data
def app_template_variables():
"""Create the data structure available to the Jinja2 renderer when
filling in the templates.
This function extracts all of classes in ``simtk.openmm.app`` and returns
a dictionary with them grouped into three lists, the reporters, the
classes with the word "File" in the name, and the remainder.
Four classes are skipped (see exclude), because they're included manually
in the template.
"""
data = {
'reporters': [],
'fileclasses': [],
'app_extras': [],
}
app_klasses = inspect.getmembers(simtk.openmm.app, predicate=inspect.isclass)
# gather all Reporters
for name, klass in app_klasses:
if name.endswith('Reporter'):
data['reporters'].append(fullname(klass))
# gather all classes with "File" in the name
for name, klass in app_klasses:
if 'File' in name:
data['fileclasses'].append(fullname(klass))
# gather all extra subclasses in simtk.openmm.app
exclude = ['simtk.openmm.app.topology.Topology',
'simtk.openmm.app.modeller.Modeller',
'simtk.openmm.app.forcefield.ForceField',
'simtk.openmm.app.simulation.Simulation']
exclude.extend(data['reporters'])
exclude.extend(data['fileclasses'])
for _, klass in app_klasses:
full = fullname(klass)
if full not in exclude and not klass.__name__[0].islower():
data['app_extras'].append(full)
return data
def main():
here = dirname(__file__)
templateLoader = jinja2.FileSystemLoader(here)
templateEnv = jinja2.Environment(loader=templateLoader)
data = library_template_variables()
data.update(app_template_variables())
for template_fn in map(basename, glob(join(here, '*.jinja2'))):
output_fn = splitext(template_fn)[0]
print('Rendering %s to %s...' % (template_fn, output_fn))
template = templateEnv.get_template(template_fn)
output_text = template.render(data)
with open(output_fn, 'w') as f:
f.write(output_text)
if __name__ == '__main__':
main()
This diff is collapsed.
......@@ -20,7 +20,6 @@ set(STAGING_OUTPUT_FILES "") # Will contain all required package files
file(GLOB STAGING_INPUT_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST.in"
"${CMAKE_CURRENT_SOURCE_DIR}/README.txt"
"${CMAKE_CURRENT_SOURCE_DIR}/filterPythonFiles.py"
)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py ${OPENMM_PYTHON_STAGING_DIR}/setup.py)
......
from __future__ import print_function
import re
import sys
import textwrap
from inspect import cleandoc
from numpydoc.docscrape import NumpyDocString
# Doxygen does a bad job of generating documentation based on docstrings.
# This script is run as a filter
# on each file, and converts the docstrings into Doxygen style comments
# so we get better documentation.
input = open(sys.argv[1])
while True:
line = input.readline()
if len(line) == 0:
break
stripped = line.lstrip()
if stripped.startswith('def') or stripped.startswith('class'):
prefix = line[:len(line)-len(stripped)]
split = stripped.split()
if split[0] == 'class' and split[1][0].islower():
# Classes that start with a lowercase letter were defined by SWIG. We want to hide them.
print("%s## @private" % prefix, end='')
if split[1][0] == '_' and split[1][1] != '_':
# Names starting with a single _ are assumed to be private.
print("%s## @private" % prefix, end='')
# We're at the start of a class or function definition. Find all lines that contain the declaration.
declaration = line
while len(line) > 0 and line.find(':') == -1:
line = input.readline()
declaration += line
# Now look for a docstring.
docstringlines = []
line = input.readline()
l = line.strip()
if l.startswith('"""') or l.startswith("'''"):
if l.count('"""') == 2 or l.count("'''") == 2:
docstringlines.append(l[3:-3])
else:
docstringlines.append(l[3:])
line = input.readline()
l = line.strip()
while l.find('"""') == -1 and l.find("'''") == -1:
docstringlines.append(line.rstrip())
line = input.readline()
l = line.strip()
if line.rstrip().endswith('"""') or line.rstrip().endswith("'''"):
# last line
docstringlines.append(line.rstrip()[:-3])
docstring = NumpyDocString(cleandoc('\n'.join(docstringlines)))
# print(docstringlines)
# Print out the docstring in Doxygen syntax, followed by the declaration.
for line in docstring['Summary']:
print('{prefix}## {line}'.format(prefix=prefix, line=line))
if len(docstring['Extended Summary']) > 0:
print('{prefix}##'.format(prefix=prefix))
for line in docstring['Extended Summary']:
print('{prefix}## {line}'.format(prefix=prefix, line=line.strip()))
print('{prefix}##'.format(prefix=prefix))
for name, type, descr in docstring['Parameters']:
print('{prefix}## @param {name} ({type}) {descr}'.format(prefix=prefix, type=type, name=name, descr=' '.join(descr)))
for name, type, descr in docstring['Returns']:
if type == '':
type = name
print('{prefix}## @return ({type}) {descr}'.format(prefix=prefix, type=type, name=name, descr=' '.join(descr)))
print(declaration, end='')
if len(docstringlines) == 0:
print(line, end='')
else:
print(line, end='')
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment