render.py 4.61 KB
Newer Older
Robert McGibbon's avatar
Robert McGibbon committed
1
2
3
4
5
"""
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.
"""
Robert McGibbon's avatar
Robert McGibbon committed
6
from os.path import dirname, join, splitext, basename
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
7
8
9
10
from glob import glob
import inspect

import jinja2
Robert McGibbon's avatar
Robert McGibbon committed
11
import simtk.openmm
Robert McGibbon's avatar
Robert McGibbon committed
12
import simtk.openmm.app
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
13

Robert McGibbon's avatar
Robert McGibbon committed
14
15


Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
16
17
18
19
def fullname(klass):
    return klass.__module__ + '.' + klass.__name__


20
def library_template_variables():
Robert McGibbon's avatar
Robert McGibbon committed
21
22
23
24
25
26
27
28
29
30
    """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.
    """
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
31
32
    data = {
        'integrators': [],
33
        'forces': [],
Robert McGibbon's avatar
Robert McGibbon committed
34
        'library_extras': [],
35
        'units': [],
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
36
37
    }

Robert McGibbon's avatar
Robert McGibbon committed
38
39
    mm_klasses = inspect.getmembers(simtk.openmm, predicate=inspect.isclass)

Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
40
41
42

    # gather all Force subclasses
    for name, klass in mm_klasses:
Robert McGibbon's avatar
Robert McGibbon committed
43
        if issubclass(klass, simtk.openmm.openmm.Force):
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
44
45
46
47
            data['forces'].append(fullname(klass))

    # gather all Integrator subclasses
    for _, klass in mm_klasses:
Robert McGibbon's avatar
Robert McGibbon committed
48
        if issubclass(klass, simtk.openmm.openmm.Integrator):
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
49
50
            data['integrators'].append(fullname(klass))

51
    # gather all extra subclasses in simtk.openmm.openmm
Robert McGibbon's avatar
Robert McGibbon committed
52
53

    # core classes that are already included in library.rst.jinja2
54
55
56
57
    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'])
Robert McGibbon's avatar
Robert McGibbon committed
58
59

    # these classes are useless and not worth documenting.
60
61
62
63
    exclude.extend([
        'simtk.openmm.openmm.SwigPyIterator',
        'simtk.openmm.openmm.OpenMMException'])

Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
64
65
    for _, klass in mm_klasses:
        full = fullname(klass)
66
67
68
        if full not in exclude and not klass.__name__[0].islower():
            data['library_extras'].append(full)

69
70
71
72
73
    # gather units related classes
    unit_klasses = inspect.getmembers(simtk.unit, predicate=inspect.isclass)
    for name, klass in unit_klasses:
        data['units'].append(fullname(klass))

74
    return data
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
75
76


77
def app_template_variables():
Robert McGibbon's avatar
Robert McGibbon committed
78
79
80
81
82
83
84
85
86
87
    """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.
    """
88
89
90
91
92
    data = {
        'reporters': [],
        'fileclasses': [],
        'app_extras': [],
    }
Robert McGibbon's avatar
Robert McGibbon committed
93
    app_klasses = inspect.getmembers(simtk.openmm.app, predicate=inspect.isclass)
94
95
96
97
98
99
100
101

    # 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:
102
        if 'File' in name or 'CharmmParameterSet' in name:
103
104
105
106
            data['fileclasses'].append(fullname(klass))

    # gather all extra subclasses in simtk.openmm.app
    exclude = ['simtk.openmm.app.topology.Topology',
107
108
109
               'simtk.openmm.app.topology.Chain',
               'simtk.openmm.app.topology.Residue',
               'simtk.openmm.app.topology.Atom',
110
111
112
113
114
115
116
117
118
119
               '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)
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
120
121
122
123
124
125
126
127

    return data


def main():
    here = dirname(__file__)
    templateLoader = jinja2.FileSystemLoader(here)
    templateEnv = jinja2.Environment(loader=templateLoader)
128
129
    data = library_template_variables()
    data.update(app_template_variables())
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
130

Robert McGibbon's avatar
Robert McGibbon committed
131
    for template_fn in map(basename, glob(join(here, '*.jinja2'))):
Robert McGibbon's avatar
Sphinx  
Robert McGibbon committed
132
133
134
135
136
137
138
139
140
141
142
        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()