[Doc] Add the ability to pseudo-autodocument MATLAB code to the SConscript file
This commit is contained in:
parent
a03b7d6123
commit
f4041d6fb2
8 changed files with 200 additions and 10 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -35,4 +35,6 @@ config.log
|
|||
coverage/
|
||||
coverage.info
|
||||
doc/sphinx/cython/examples
|
||||
doc/sphinx/matlab/examples
|
||||
doc/sphinx/matlab/examples/*.rst
|
||||
doc/sphinx/matlab/tutorials/*.rst
|
||||
doc/sphinx/matlab/code-docs/*.rst
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ if 'clean' in COMMAND_LINE_TARGETS:
|
|||
removeFile('include/cantera/base/config.h')
|
||||
removeFile('ext/f2c_libs/arith.h')
|
||||
removeDirectory('doc/sphinx/matlab/examples')
|
||||
removeDirectory('doc/sphinx/matlab/tutorials')
|
||||
removeDirectory('doc/sphinx/matlab/code-docs')
|
||||
removeDirectory('doc/sphinx/cython/examples')
|
||||
for name in os.listdir('.'):
|
||||
if name.endswith('.msi'):
|
||||
|
|
|
|||
166
doc/SConscript
166
doc/SConscript
|
|
@ -4,6 +4,70 @@ Import('env', 'build', 'install')
|
|||
|
||||
localenv = env.Clone()
|
||||
|
||||
from collections import namedtuple
|
||||
Page = namedtuple('Page', ['name', 'title', 'objects'])
|
||||
|
||||
# Set up functions to pseudo-autodoc the MATLAB toolbox
|
||||
def extract_matlab_docstring(mfile):
|
||||
"""
|
||||
Return the docstring from mfile, assuming that it consists of the
|
||||
first uninterrupted comment block.
|
||||
"""
|
||||
docstring = ".. mat:function:: "
|
||||
with open(mfile, 'r') as in_file:
|
||||
# The function name is read from the first line
|
||||
docstring += get_function_name(in_file.readline()) + '\n'
|
||||
|
||||
# By convention, the second line (called H1 in the MATLAB documentation)
|
||||
# is read by various MATLAB functions, so it should be in the format
|
||||
# MATLAB expects - FUNCTIONNAME Summary. We read in this line and
|
||||
# add the summary to the docstring. If the line doesn't match the
|
||||
# format, just write it to the docstring as is.
|
||||
line = in_file.readline()
|
||||
try:
|
||||
docstring += ' ' + line.split(' ')[1] + '\n'
|
||||
except IndexError:
|
||||
docstring += line + '\n'
|
||||
|
||||
# Skip the next line, which is a duplicate of the first. It is here
|
||||
# because MATLAB doesn't show the function definition in its help.
|
||||
in_file.readline()
|
||||
|
||||
# For the rest of the lines in the file, get the line if it is
|
||||
# in the first unbroken comment section and add it to the docstring.
|
||||
for line in in_file.readlines():
|
||||
try:
|
||||
if line.lstrip().startswith('%'):
|
||||
docstring += ' '*4 + line.lstrip()[2:-1] + '\n'
|
||||
else:
|
||||
break
|
||||
except IndexError:
|
||||
docstring += '\n'
|
||||
|
||||
return docstring + '\n'
|
||||
|
||||
def get_function_name(str):
|
||||
"""
|
||||
Return the function or classdef signature, assuming that
|
||||
the string starts with either 'function ' or 'classdef '.
|
||||
"""
|
||||
if str.startswith('function '):
|
||||
sig = str[len('function '):]
|
||||
elif str.startswith('classdef '):
|
||||
sig = str[len('classdef '):]
|
||||
else:
|
||||
print "Unknown function declaration in MATLAB document", str
|
||||
|
||||
# Split the function signature on the equals sign, if it exists.
|
||||
# We don't care about what comes before the equals sign, since
|
||||
# if a function returns, the docs will tell us. If there is no
|
||||
# =, return the whole signature.
|
||||
if '=' in sig:
|
||||
idx = sig.index('=')
|
||||
return sig[idx+2:]
|
||||
else:
|
||||
return sig
|
||||
|
||||
if localenv['doxygen_docs']:
|
||||
if localenv['graphvizdir']:
|
||||
localenv.Append(PATH=localenv['graphvizdir'])
|
||||
|
|
@ -36,14 +100,112 @@ if localenv['sphinx_docs']:
|
|||
build(b)
|
||||
localenv.Depends(sphinxdocs, b)
|
||||
|
||||
# Create a list of MATLAB classes to document. This uses the NamedTuple
|
||||
# structure defined at the top of the file. The @Data and @Utilities
|
||||
# classes are fake classes for the purposes of documentation only. Each
|
||||
# Page represents one html page of the documentation.
|
||||
pages = [
|
||||
Page('importing', 'Importing Phase Objects',
|
||||
['@Solution', '@Mixture',]
|
||||
),
|
||||
Page('thermodynamics', 'Thermodynamic Properties',
|
||||
['@ThermoPhase']
|
||||
),
|
||||
Page('kinetics', 'Chemical Kinetics', ['@Kinetics']),
|
||||
Page('transport', 'Transport Properties', ['@Transport']),
|
||||
Page('zero-dim', 'Zero-Dimensional Reactor Networks',
|
||||
['@Func', '@Reactor', '@ReactorNet', '@FlowDevice', '@Wall']
|
||||
),
|
||||
Page('one-dim', 'One-Dimensional Reacting Flows',
|
||||
['1D/@Domain1D', '1D/@Stack']
|
||||
),
|
||||
Page('data', 'Built-In Thermochemical Data',
|
||||
['@Data']
|
||||
),
|
||||
Page('utilities', 'Utility Functions',
|
||||
['@Utilities', '@XML_Node']
|
||||
),
|
||||
Page('interface', 'Interfaces', ['@Interface']),
|
||||
]
|
||||
|
||||
# Create a dictionary of extra files associated with each class. These
|
||||
# files are listed relative to the top directory interfaces/matlab/cantera
|
||||
extra = {
|
||||
'@Solution': ['IdealGasMix.m', 'importPhase.m',],
|
||||
'@Func': ['gaussian.m', 'polynom.m'],
|
||||
'@Reactor': ['ConstPressureReactor.m',
|
||||
'FlowReactor.m', 'IdealGasConstPressureReactor.m',
|
||||
'IdealGasReactor.m', 'Reservoir.m'],
|
||||
'@FlowDevice': ['MassFlowController.m', 'Valve.m'],
|
||||
'1D/@Domain1D': ['1D/AxiStagnFlow.m', '1D/AxisymmetricFlow.m',
|
||||
'1D/Inlet.m', '1D/Outlet.m', '1D/OutletRes.m',
|
||||
'1D/Surface.m', '1D/SymmPlane.m'],
|
||||
'1D/@Stack': ['1D/FreeFlame.m', '1D/npflame_init.m'],
|
||||
'@Interface': ['importEdge.m', 'importInterface.m'],
|
||||
'@Data': ['air.m', 'constants.m', 'gasconstant.m', 'GRI30.m',
|
||||
'Hydrogen.m', 'Methane.m', 'Nitrogen.m', 'oneatm.m',
|
||||
'Oxygen.m', 'Water.m'],
|
||||
'@Utilities': ['adddir.m', 'ck2cti.m', 'cleanup.m', 'geterr.m',]
|
||||
}
|
||||
|
||||
# These files do not need to be documented in the MATLAB classes because they
|
||||
# are generics that are overloaded per-class. Since the loop checks for these
|
||||
# strings in each file name, hndl.m is the same as *hndl.m* (to use globbing
|
||||
# notation).
|
||||
nodoc_matlab_files = ['set.m', 'clear.m', 'display.m', 'hndl.m', 'private', 'subsref.m']
|
||||
|
||||
# Loop through the pages list to document each class
|
||||
for page in pages:
|
||||
tempenv = env.Clone()
|
||||
|
||||
# Set the title header
|
||||
title = page.title
|
||||
tempenv['title'] = '='*len(title) + '\n' + title + '\n' + '='*len(title)
|
||||
doc = ''
|
||||
|
||||
# The base directory of the MATLAB toolbox relative to the sphinx build directory
|
||||
base = '../interfaces/matlab/toolbox'
|
||||
for obj in page.objects:
|
||||
all_files = []
|
||||
# Set the subheader based on the class name
|
||||
doc += obj.split('@')[1] + '\n' + '-'*len(obj.split('@')[1]) + '\n\n'
|
||||
if os.path.isdir(pjoin(base,obj)):
|
||||
class_files = os.listdir(pjoin(base,obj))
|
||||
class_files = [name for name in class_files if not any(x in name for x in nodoc_matlab_files)]
|
||||
all_files = [class_files.pop(class_files.index(obj.split('@')[1]+'.m'))]
|
||||
extra_files = extra.get(obj,[])
|
||||
all_files += sorted(class_files + extra_files)
|
||||
class_files.insert(0,all_files[0])
|
||||
else:
|
||||
all_files = extra.get(obj,[])
|
||||
for file in all_files:
|
||||
if file in class_files:
|
||||
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,obj,file)))
|
||||
else:
|
||||
doc += extract_matlab_docstring(os.path.relpath(pjoin(base,file)))
|
||||
|
||||
tempenv['matlab_docstrings'] = doc
|
||||
# Substitute the docstrings into the proper file. Since the docs change
|
||||
# every time the source is changed, we don't want to have to commit the
|
||||
# change in the rst file as well as the source - too much code churn. So
|
||||
# we use a template and a SubstFile directive.
|
||||
c = tempenv.SubstFile('#doc/sphinx/matlab/code-docs/%s.rst' % page.name,
|
||||
'#doc/sphinx/matlab/matlab-template.rst.in')
|
||||
build(c)
|
||||
localenv.Depends(sphinxdocs, c)
|
||||
|
||||
# Matlab examples: create individual documentation pages with the source
|
||||
# for each example
|
||||
for f in mglob(env, '#samples/matlab', 'm'):
|
||||
tmpenv = env.Clone()
|
||||
tmpenv['script_name'] = f.name
|
||||
tmpenv['script_path'] = '../../../../samples/matlab/%s' % f.name
|
||||
b = tmpenv.SubstFile('#doc/sphinx/matlab/examples/%s.rst' % f.name[:-2],
|
||||
'#doc/sphinx/matlab/example-script.rst.in')
|
||||
if f.name.startswith('tut'):
|
||||
b = tmpenv.SubstFile('#doc/sphinx/matlab/tutorials/%s.rst' % f.name[:-2],
|
||||
'#doc/sphinx/matlab/example-script.rst.in')
|
||||
else:
|
||||
b = tmpenv.SubstFile('#doc/sphinx/matlab/examples/%s.rst' % f.name[:-2],
|
||||
'#doc/sphinx/matlab/example-script.rst.in')
|
||||
build(b)
|
||||
localenv.Depends(sphinxdocs, b)
|
||||
|
||||
|
|
|
|||
|
|
@ -522,6 +522,7 @@ Optional Programs
|
|||
* `Pygments <http://pygments.org/>`_ (install with ``easy_install -U pygments``)
|
||||
* `pyparsing <http://sourceforge.net/projects/pyparsing/>`_ (install with ``easy_install -U pyparsing``)
|
||||
* `doxylink <http://pypi.python.org/pypi/sphinxcontrib-doxylink/>`_ (install with ``easy_install sphinxcontrib-doxylink``)
|
||||
* `matlabdomain <https://pypi.python.org/pypi/sphinxcontrib-matlabdomain>`_ (install with ``easy_install sphinxcontrib-matlabdomain``)
|
||||
|
||||
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,19 @@ sys.path.append(os.path.abspath('./exts'))
|
|||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
|
||||
# sphinxcontrib.matlab has been added to add the MATLAB domain for the
|
||||
# documentation of the MATLAB functions. It is a new requirement to build the
|
||||
# documentation in 2.2. It should be loaded before sphinx.ext.autodoc because
|
||||
# loading it after gives errors when autodocumenting the Python interface.
|
||||
extensions = [
|
||||
'sphinxcontrib.matlab',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.autosummary',
|
||||
'mathjax',
|
||||
'sphinxcontrib.doxylink']
|
||||
'sphinxcontrib.doxylink',
|
||||
]
|
||||
|
||||
# @todo: Sphinx version 1.1 adds support for MathJax, so we can remove the
|
||||
# custom extension for that once that version becomes more standard
|
||||
|
|
@ -51,6 +59,10 @@ doxylink = {
|
|||
'../../doxygen/html/')
|
||||
}
|
||||
|
||||
# Ensure that the primary domain is the Python domain, since we've added the
|
||||
# MATLAB domain with sphinxcontrib.matlab
|
||||
primary_domain = 'py'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
.. _sec-cython-examples:
|
||||
|
||||
.. py:currentmodule:: cantera
|
||||
.. _sec-matlab-examples:
|
||||
|
||||
Index of Examples
|
||||
=================
|
||||
|
|
@ -12,7 +10,7 @@ Tutorials
|
|||
.. toctree::
|
||||
:glob:
|
||||
|
||||
examples/tut*
|
||||
tutorials/*
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
@ -20,4 +18,4 @@ Examples
|
|||
.. toctree::
|
||||
:glob:
|
||||
|
||||
examples/[!tut]*
|
||||
examples/*
|
||||
|
|
|
|||
|
|
@ -7,4 +7,13 @@ Matlab Interface User's Guide
|
|||
:maxdepth: 2
|
||||
|
||||
input-tutorial
|
||||
code-docs/importing
|
||||
code-docs/interface
|
||||
code-docs/thermodynamics
|
||||
code-docs/kinetics
|
||||
code-docs/transport
|
||||
code-docs/zero-dim
|
||||
code-docs/one-dim
|
||||
code-docs/data
|
||||
code-docs/utilities
|
||||
examples
|
||||
|
|
|
|||
4
doc/sphinx/matlab/matlab-template.rst.in
Normal file
4
doc/sphinx/matlab/matlab-template.rst.in
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
@title@
|
||||
|
||||
@matlab_docstrings@
|
||||
Loading…
Add table
Reference in a new issue