Added infrastructure for using Sphinx to document the new Python module

This includes adding the sphinx_cmd option to SCons, which can be set to the
Python 3 version of sphinx-build as necessary.
This commit is contained in:
Ray Speth 2012-09-06 19:58:13 +00:00
parent 42e07afedc
commit 0c904bebfa
10 changed files with 35 additions and 633 deletions

View file

@ -359,6 +359,10 @@ opts.AddVariables(
'sphinx_docs',
"""Build HTML documentation for the Python module using Sphinx""",
False),
PathVariable(
'sphinx_cmd',
"""Command to use for building the Sphinx documentation""",
'sphinx-build', PathVariable.PathAccept),
BoolVariable(
'with_h298modify_capability',
"""Enable changing the 298K heats of formation directly via

View file

@ -19,6 +19,6 @@ if localenv['sphinx_docs']:
sphinxdocs = build(localenv.Command('${SPHINXBUILD}/html/index.html',
'sphinx/conf.py',
'sphinx-build -b html -d ${SPHINXBUILD}/doctrees ${SPHINXSRC} ${SPHINXBUILD}/html'))
'${sphinx_cmd} -b html -d ${SPHINXBUILD}/doctrees ${SPHINXSRC} ${SPHINXBUILD}/html'))
localenv.AlwaysBuild(sphinxdocs)

View file

@ -16,7 +16,12 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../../interfaces/python'))
if sys.version_info.major == 3:
sys.path.insert(0, os.path.abspath('../../build/python3'))
else:
sys.path.insert(0, os.path.abspath('../../build/python2'))
sys.path.insert(0, os.path.abspath('../../interfaces/python'))
sys.path.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('./exts'))
@ -31,7 +36,7 @@ extensions = ['sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.autosummary',
'mathjax',
'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
@ -85,6 +90,9 @@ release = '2.1a1'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
if sys.version_info.major == 3:
exclude_patterns.append('python/*')
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

View file

@ -0,0 +1,9 @@
Python Module Documentation
===========================
Contents:
.. toctree::
:maxdepth: 2
thermo

View file

@ -0,0 +1,8 @@
Thermodynamic Properties
========================
These classes are used to describe the thermodynamic state of a system.
.. autoclass:: cantera.ThermoPhase
.. autoclass:: cantera.InterfacePhase
.. autoclass:: cantera.PureFluid

View file

@ -1,29 +0,0 @@
doxylink 1.2
Sphinx extension for linking to Doxygen documentation.
Copyright (c) 2011, Matt Williams.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,446 +0,0 @@
# -*- coding: utf-8 -*-
import os
import xml.etree.ElementTree as ET
import urlparse
import re
import itertools
from docutils import nodes, utils
from sphinx.util.nodes import split_explicit_title
from sphinx.util.console import bold, standout
from parsing import normalise, ParseException
def find_url(doc, symbol):
"""
Return the URL for a given symbol.
This is where the magic happens.
This function could be a lot more clever. At present it required the passed symbol to be almost exactly the same as the entries in the Doxygen tag file.
.. todo::
Maybe print a list of all possible matches as a warning (but still only return the first)
:Parameters:
doc : xml.etree.ElementTree
The XML DOM object
symbol : string
The symbol to lookup in the file. E.g. something like 'PolyVox::Array' or 'tidyUpMemory'
:return: String representing the filename part of the URL
"""
#First check for an exact match with a top-level object (namespaces, objects etc.)
#env = inliner.document.settings.env
matches = []
for compound in doc.findall('.//compound'):
if compound.find('name').text == symbol:
matches += [{'file':compound.find('filename').text, 'kind':compound.get('kind')}]
if len(matches) > 1:
pass
#env.warn(env.docname, 'There were multiple matches for `%s`: %s' % (symbol, matches))
if len(matches) == 1:
return matches[0]
#Strip off first namespace bit of the compound name so that 'ArraySizes' can match 'PolyVox::ArraySizes'
for compound in doc.findall('.//compound'):
symbol_list = compound.find('name').text.split('::', 1)
if len(symbol_list) == 2:
reducedsymbol = symbol_list[1]
if reducedsymbol == symbol:
return {'file':compound.find('filename').text, 'kind':compound.get('kind')}
#Now split the symbol by '::'. Find an exact match for the first part and then a member match for the second
#So PolyVox::Array::operator[] becomes like {namespace: "PolyVox::Array", endsymbol: "operator[]"}
symbol_list = symbol.rsplit('::', 1)
if len(symbol_list) == 2:
namespace = symbol_list[0]
endsymbol = symbol_list[1]
for compound in doc.findall('.//compound'):
if compound.find('name').text == namespace:
for member in compound.findall('member'):
#If this compound object contains the matching member then return it
if member.find('name').text == endsymbol:
return {'file':(member.findtext('anchorfile') or compound.findtext('filename')) + '#' + member.find('anchor').text, 'kind':member.get('kind')}
#Then we'll look at unqualified members
for member in doc.findall('.//member'):
if member.find('name').text == symbol:
return {'file':(member.findtext('anchorfile') or compound.findtext('filename')) + '#' + member.find('anchor').text, 'kind':member.get('kind')}
return None
def parse_tag_file(doc):
"""
Takes in an XML tree from a Doxygen tag file and returns a dictionary that looks something like:
.. code-block:: python
{'PolyVox': {'file': 'namespace_poly_vox.html',
'kind': 'namespace'},
'PolyVox::Array': {'file': 'class_poly_vox_1_1_array.html',
'kind': 'class'},
'PolyVox::Array1DDouble': {'file': 'namespace_poly_vox.html#a7a1f5fd5c4f7fbb4258a495d707b5c13',
'kind': 'typedef'},
'PolyVox::Array1DFloat': {'file': 'namespace_poly_vox.html#a879a120e49733eba1905c33f8a7f131b',
'kind': 'typedef'},
'PolyVox::Array1DInt16': {'file': 'namespace_poly_vox.html#aa1463ece448c6ebed55ab429d6ae3e43',
'kind': 'typedef'},
'QScriptContext::throwError': {'arglist': {'( Error error, const QString & text )': 'qscriptcontext.html#throwError',
'( const QString & text )': 'qscriptcontext.html#throwError-2'},
'kind': 'function'},
'QScriptContext::toString': {'arglist': {'()': 'qscriptcontext.html#toString'},
'kind': 'function'}}
Note the different form for functions. This is required to allow for 'overloading by argument type'.
To access a filename for a symbol you do:
.. code-block:: python
symbol_mapping = mapping[symbol]
if symbol_mapping['kind'] == 'function':
url = symbol_mapping['arglist'][argument_string]
else:
url = symbol_mapping['file']
:Parameters:
doc : xml.etree.ElementTree
The XML DOM object
:return: a dictionary mapping fully qualified symbols to files
"""
mapping = {}
function_list = [] #This is a list of function to be parsed and inserted into mapping at the end of the function.
for compound in doc.findall("./compound"):
compound_kind = compound.get('kind')
if compound_kind != 'namespace' and compound_kind != 'class':
continue #Skip everything that isn't a namespace or class
compound_name = compound.findtext('name')
compound_filename = compound.findtext('filename')
#If it's a compound we can simply add it
mapping[compound_name] = {'kind' : compound_kind, 'file' : compound_filename}
for member in compound.findall('member'):
#If the member doesn't have an <anchorfile> element, use the parent compounds <filename> instead
#This is the way it is in the qt.tag and is perhaps an artefact of old Doxygen
anchorfile = member.findtext('anchorfile') or compound_filename
member_symbol = join(compound_name, '::', member.findtext('name'))
member_kind = member.get('kind')
arglist_text = member.findtext('./arglist') #If it has an <arglist> then we assume it's a function. Empty <arglist> returns '', not None. Things like typedefs and enums can have empty arglists
if arglist_text and member_kind != 'variable' and member_kind != 'typedef' and member_kind != 'enumeration':
function_list.append((member_symbol, arglist_text, member_kind, join(anchorfile,'#',member.findtext('anchor'))))
else:
mapping[member_symbol] = {'kind' : member.get('kind'), 'file' : join(anchorfile,'#',member.findtext('anchor'))}
for old_tuple, normalised_tuple in zip(function_list, itertools.imap(normalise, (member_tuple[1] for member_tuple in function_list))):
member_symbol = old_tuple[0]
original_arglist = old_tuple[1]
kind = old_tuple[2]
anchor_link = old_tuple[3]
normalised_arglist = normalised_tuple[1]
if normalised_tuple[1] is not None: #This is a 'flag' for a ParseException having happened
if mapping.get(member_symbol):
mapping[member_symbol]['arglist'][normalised_arglist] = anchor_link
else:
mapping[member_symbol] = {'kind' : kind, 'arglist' : {normalised_arglist : anchor_link}}
else:
print('Skipping %s %s%s. Error reported from parser was: %s' % (old_tuple[2], old_tuple[0], old_tuple[1], normalised_tuple[0]))
#from pprint import pprint; pprint(mapping)
return mapping
def find_url2(mapping, symbol):
"""
Return the URL for a given symbol.
This is where the magic happens.
.. todo::
Maybe print a list of all possible matches as a warning (but still only return the first)
:Parameters:
mapping : dictionary
A dictionary of the form returned by :py:func:`parse_tag_file`
symbol : string
The symbol to lookup in the file. E.g. something like 'PolyVox::Array' or 'tidyUpMemory'
:return: String representing the filename part of the URL
:raises:
LookupError
Raised if the symbol could not be matched in the file
"""
#print "\n\nSearching for", symbol
try:
symbol, normalised_arglist = normalise(symbol)
except ParseException as error:
raise LookupError(error)
#print symbol, normalised_arglist
#If we have an exact match then return it.
if mapping.get(symbol):
#print ('Exact match')
return return_from_mapping(mapping[symbol], normalised_arglist)
#If the user didn't pass in any arguments, i.e. `arguments == ''` then they don't care which version of the overloaded funtion they get.
#First we check for any mapping entries which even slightly match the requested symbol
#endswith_list = {}
#for item, data in mapping.items():
# if item.endswith(symbol):
#print symbol + ' : ' + item
# endswith_list[item] = data
# mapping[item]['file']
#If we only find one then we return it.
#if len(endswith_list) is 1:
# return endswith_list.values()[0]['file']
#print("Still", len(endswith_list), 'possible matches')
piecewise_list = find_url_piecewise(mapping, symbol)
#If there is only one match, return it.
if len(piecewise_list) is 1:
return return_from_mapping(piecewise_list.values()[0], normalised_arglist)
#print("Still", len(piecewise_list), 'possible matches')
#If there is more than one item in piecewise_list then there is an ambiguity
#Often this is due to the symbol matching the name of the constructor as well as the class name itself
classes_list = find_url_classes(piecewise_list, symbol)
#If there is only one by here we return it.
if len(classes_list) is 1:
return classes_list.values()[0]
#print("Still", len(classes_list), 'possible matches')
#If we exhaused the list by requiring classes, use the list from before the filter.
if len(classes_list) == 0:
classes_list = piecewise_list
no_templates_list = find_url_remove_templates(classes_list, symbol)
if len(no_templates_list) is 1:
return return_from_mapping(no_templates_list.values()[0], normalised_arglist)
#print("Still", len(no_templates_list), 'possible matches')
#If not found by now, just return the first one in the list
if len(no_templates_list) != 0:
#TODO return a warning here?
return return_from_mapping(no_templates_list.values()[0], normalised_arglist)
#Else return None if the list is empty
else:
LookupError('Could not find a match')
def return_from_mapping(mapping_entry, normalised_arglist=''):
"""
Return a mapping to a single URL in the form. This is needed since mapping entries for functions are more complicated due to function overriding.
If the mapping to be returned is not a function, this will simply return the mapping entry intact. If the entry is a function it will attempt to get the right version based on the function signature.
:Parameters:
mapping_entry : dict
should be a single entry from the large mapping file corresponding to a single symbol. If the symbol is a function, then ``mappingentry['arglist']`` will be a dictionary mapping normalised signatures to URLs
normalised_arglist : string
the normalised form of the arglist that the user has requested. This can be empty in which case the function will return just the first element of ``mappingentry['arglist']``. This parameter is ignored if ``mappingentry['kind'] != 'function'``
:return: dictionary something like:
.. code-block:: python
{'kind' : 'function', 'file' : 'something.html#foo'}
"""
#If it's a function we need to grab the right signature from the arglist.
if mapping_entry['kind'] == 'function':
#If the user has requested a specific function through specifying an arglist then get the right anchor
if normalised_arglist:
filename = mapping_entry['arglist'].get(normalised_arglist)
if not filename: #If we didn't get the filename because it's not in the mapping then we will just return a random one?
#TODO return a warning here!
filename = mapping_entry['arglist'].values()[0]
else:
#Otherwise just return the first entry (if they don't care they get whatever comes first)
filename = mapping_entry['arglist'].values()[0]
return {'kind' : 'function', 'file' : filename}
elif mapping_entry.get('arglist'):
#This arglist should only be one entry long and that entry should have '' as its key
return {'kind' : mapping_entry['kind'], 'file' : mapping_entry['arglist']['']}
#If it's not a function, then return it raw
return mapping_entry
def find_url_piecewise(mapping, symbol):
"""
Match the requested symbol reverse piecewise (split on ``::``) against the tag names to ensure they match exactly (modulo ambiguity)
So, if in the mapping there is ``PolyVox::Volume::FloatVolume`` and ``PolyVox::Volume`` they would be split into:
.. code-block:: python
['PolyVox', 'Volume', 'FloatVolume'] and ['PolyVox', 'Volume']
and reversed:
.. code-block:: python
['FloatVolume', 'Volume', 'PolyVox'] and ['Volume', 'PolyVox']
and truncated to the shorter of the two:
.. code-block:: python
['FloatVolume', 'Volume'] and ['Volume', 'PolyVox']
If we're searching for the ``PolyVox::Volume`` symbol we would compare:
.. code-block:: python
['Volume', 'PolyVox'] to ['FloatVolume', 'Volume', 'PolyVox'].
That doesn't match so we look at the next in the mapping:
.. code-block:: python
['Volume', 'PolyVox'] to ['Volume', 'PolyVox'].
Good, so we add it to the list
"""
piecewise_list = {}
for item, data in mapping.items():
split_symbol = symbol.split('::')
split_item = item.split('::')
split_symbol.reverse()
split_item.reverse()
min_length = min(len(split_symbol), len(split_item))
split_symbol = split_symbol[:min_length]
split_item = split_item[:min_length]
#print split_symbol, split_item
if split_symbol == split_item:
#print symbol + ' : ' + item
piecewise_list[item] = data
return piecewise_list
def find_url_classes(mapping, symbol):
"""Prefer classes over names of constructors"""
classes_list = {}
for item, data in mapping.items():
if data['kind'] == 'class':
#print symbol + ' : ' + item
classes_list[item] = data
return classes_list
def find_url_remove_templates(mapping, symbol):
"""Now, to disambiguate between ``PolyVox::Array< 1, ElementType >::operator[]`` and ``PolyVox::Array::operator[]`` matching ``operator[]``, we will ignore templated (as in C++ templates) tag names by removing names containing ``<``"""
no_templates_list = {}
for item, data in mapping.items():
if '<' not in item:
#print symbol + ' : ' + item
no_templates_list[item] = data
return no_templates_list
def join(*args):
return ''.join(args)
def create_role(app, tag_filename, rootdir):
#Tidy up the root directory path
if not rootdir.endswith(('/', '\\')):
rootdir = join(rootdir, os.sep)
try:
tag_file = ET.parse(tag_filename)
cache_name = os.path.basename(tag_filename)
app.info(bold('Checking tag file cache for %s: ' % cache_name), nonl=True)
if not hasattr(app.env, 'doxylink_cache'):
# no cache present at all, initialise it
app.info('No cache at all, rebuilding...')
mapping = parse_tag_file(tag_file)
app.env.doxylink_cache = { cache_name : {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}}
elif not app.env.doxylink_cache.get(cache_name):
# Main cache is there but the specific sub-cache for this tag file is not
app.info('Sub cache is missing, rebuilding...')
mapping = parse_tag_file(tag_file)
app.env.doxylink_cache[cache_name] = {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}
elif app.env.doxylink_cache[cache_name]['mtime'] < os.path.getmtime(tag_filename):
# tag file has been modified since sub-cache creation
app.info('Sub-cache is out of date, rebuilding...')
mapping = parse_tag_file(tag_file)
app.env.doxylink_cache[cache_name] = {'mapping' : mapping, 'mtime' : os.path.getmtime(tag_filename)}
else:
#The cache is up to date
app.info('Sub-cache is up-to-date')
except IOError:
tag_file = None
app.warn(standout('Could not open tag file %s. Make sure your `doxylink` config variable is set correctly.' % tag_filename))
def find_doxygen_link(name, rawtext, text, lineno, inliner, options={}, content=[]):
text = utils.unescape(text)
# from :name:`title <part>`
has_explicit_title, title, part = split_explicit_title(text)
warning_messages = []
if tag_file:
url = find_url(tag_file, part)
try:
url = find_url2(app.env.doxylink_cache[cache_name]['mapping'], part)
except LookupError as error:
warning_messages.append('Error while parsing `%s`. Is not a well-formed C++ function call or symbol. If this is not the case, it is a doxylink bug so please report it. Error reported was: %s' % (part, error))
if url:
#If it's an absolute path then the link will work regardless of the document directory
#Also check if it is a URL (i.e. it has a 'scheme' like 'http' or 'file')
if os.path.isabs(rootdir) or urlparse.urlparse(rootdir).scheme:
full_url = join(rootdir, url['file'])
#But otherwise we need to add the relative path of the current document to the root source directory to the link
else:
relative_path_to_docsrc = os.path.relpath(app.env.srcdir, os.path.dirname(inliner.document.current_source))
full_url = join(relative_path_to_docsrc, '/', rootdir, url['file']) #We always use the '/' here rather than os.sep since this is a web link avoids problems like documentation/.\../library/doc/ (mixed slashes)
if url['kind'] == 'function' and app.config.add_function_parentheses and not normalise(title)[1]:
title = join(title, '()')
pnode = nodes.reference(title, title, internal=False, refuri=full_url)
return [pnode], []
#By here, no match was found
warning_messages.append('Could not find match for `%s` in `%s` tag file' % (part, tag_filename))
else:
warning_messages.append('Could not find match for `%s` because tag file not found' % (part))
pnode = nodes.inline(rawsource=title, text=title)
return [pnode], [inliner.reporter.warning(message, line=lineno) for message in warning_messages]
return find_doxygen_link
def setup_doxylink_roles(app):
for name, [tag_filename, rootdir] in app.config.doxylink.iteritems():
app.add_role(name, create_role(app, tag_filename, rootdir))
def setup(app):
app.add_config_value('doxylink', {}, 'env')
app.connect('builder-inited', setup_doxylink_roles)

View file

@ -1,153 +0,0 @@
#import multiprocessing
import itertools
from pyparsing import Word, Literal, alphas, nums, alphanums, OneOrMore, Optional, SkipTo, ParseException, Group, ZeroOrMore, Suppress, Combine, delimitedList, quotedString, nestedExpr, ParseResults, oneOf
# define punctuation - reuse of expressions helps packratting work better
LPAR,RPAR,LBRACK,RBRACK,COMMA,EQ = map(Literal,"()[],=")
#Qualifier to go in front of type in the argument list (unsigned const int foo)
qualifier = OneOrMore(oneOf('const unsigned typename struct enum'))
def turn_parseresults_to_list(s, loc, toks):
return ParseResults(normalise_templates(toks[0].asList()))
def normalise_templates(toks, isinstance=isinstance, basestring=basestring):
s_list = ['<']
s_list_append = s_list.append #lookup append func once, instead of many times
for tok in toks:
if isinstance(tok, basestring): #See if it's a string
s_list_append(' ' + tok)
else:
#If it's not a string
s_list_append(normalise_templates(tok))
s_list_append(' >')
return ''.join(s_list)
#Skip pairs of brackets.
angle_bracket_pair = nestedExpr(opener='<',closer='>').setParseAction(turn_parseresults_to_list)
#TODO Fix for nesting brackets
parentheses_pair = LPAR + SkipTo(RPAR) + RPAR
square_bracket_pair = LBRACK + SkipTo(RBRACK) + RBRACK
#The raw type of the input, i.e. 'int' in (unsigned const int * foo)
#TODO I guess this should be a delimited list (by '::') of name and angle brackets
input_type = Combine(Word(alphanums + ':_') + Optional(angle_bracket_pair + Optional(Word(alphanums + ':_'))))
#A number. e.g. -1, 3.6 or 5
number = Word('-.' + nums)
#The name of the argument. We will ignore this but it must be matched anyway.
input_name = OneOrMore(Word(alphanums + '_') | angle_bracket_pair | parentheses_pair | square_bracket_pair)
#Grab the '&', '*' or '**' type bit in (const QString & foo, int ** bar)
pointer_or_reference = oneOf('* &')
#The '=QString()' or '=false' bit in (int foo = 4, bool bar = false)
default_value = Literal('=') + OneOrMore(number | quotedString | input_type | parentheses_pair | angle_bracket_pair | square_bracket_pair | Word('|&^'))
#A combination building up the interesting bit -- the argument type, e.g. 'const QString &', 'int' or 'char*'
argument_type = Optional(qualifier, default='')("qualifier") + \
input_type("input_type") + \
Optional(pointer_or_reference, default='')("pointer_or_reference1") + \
Optional('const')('const_pointer_or_reference') + \
Optional(pointer_or_reference, default='')("pointer_or_reference2")
#Argument + variable name + default
argument = Group(argument_type('argument_type') + Optional(input_name) + Optional(default_value))
#List of arguments in parentheses with an optional 'const' on the end
arglist = LPAR + delimitedList(argument)('arg_list') + Optional(COMMA + '...')('var_args') + RPAR
def normalise(symbol):
"""
Takes a c++ symbol or funtion and splits it into symbol and a normalised argument list.
:Parameters:
symbol : string
A C++ symbol or function definition like ``PolyVox::Volume``, ``Volume::printAll() const``
:return:
a tuple consisting of two strings: ``(qualified function name or symbol, normalised argument list)``
"""
try:
bracket_location = symbol.index('(')
#Split the input string into everything before the opening bracket and everything else
function_name = symbol[:bracket_location]
arglist_input_string = symbol[bracket_location:]
except ValueError:
#If there's no brackets, then there's no function signature. This means the passed in symbol is just a type name
return symbol, ''
#This is a very common signature so we'll make a special case for it. It requires no parsing anyway
if arglist_input_string.startswith('()'):
if arglist_input_string in ('()', '()=0'):
return function_name, arglist_input_string
elif arglist_input_string in ('() const ', '() const', '() const =0'):
return function_name, '() const'
#By now we're left with something like "(blah, blah)", "(blah, blah) const" or "(blah, blah) const =0"
try:
closing_bracket_location = arglist_input_string.rindex(')')
arglist_suffix = arglist_input_string[closing_bracket_location+1:]
arglist_input_string = arglist_input_string[:closing_bracket_location+1]
except ValueError:
#This shouldn't happen.
print 'Could not find closing bracket in %s' % arglist_input_string
raise
try:
result = arglist.parseString(arglist_input_string)
except ParseException as error:
#print symbol
#print pe
return str(error), None
else:
#Will be a list or normalised string arguments
#e.g. ['OBMol&', 'vector< int >&', 'OBBitVec&', 'OBBitVec&', 'int', 'int']
normalised_arg_list = []
#Cycle through all the matched arguments
for arg in result.arg_list:
#Here is where we build up our normalised form of the argument
argument_string_list = ['']
if arg.qualifier:
argument_string_list.append(''.join((arg.qualifier,' ')))
argument_string_list.append(arg.input_type)
#Functions can have a funny combination of *, & and const between the type and the name so build up a list of theose here:
const_pointer_ref_list = []
const_pointer_ref_list.append(arg.pointer_or_reference1)
if arg.const_pointer_or_reference:
const_pointer_ref_list.append(''.join((' ', arg.const_pointer_or_reference, ' ')))
# same here
const_pointer_ref_list.append(arg.pointer_or_reference2)
#And combine them into a single normalised string and add them to the argument list
argument_string_list.extend(const_pointer_ref_list)
#Finally we join our argument string and add it to our list
normalised_arg_list.append(''.join(argument_string_list))
#If the function contains a variable number of arguments (int foo, ...) then add them on.
if result.var_args:
normalised_arg_list.append('...')
#Combine all the arguments and put parentheses around it
normalised_arg_list_string = ''.join(['(', ', '.join(normalised_arg_list), ')'])
#Add a const onto the end
if 'const' in arglist_suffix:
normalised_arg_list_string += ' const'
return function_name, normalised_arg_list_string
#TODO Maybe this should raise an exception?
return None
def normalise_list(list_of_symbols):
#normalise_pool = multiprocessing.Pool(multiprocessing.cpu_count() * 2)
#results = normalise_pool.map(normalise, list_of_symbols)
#normalise_pool.terminate()
results = itertools.imap(normalise, list_of_symbols)
return results

View file

@ -27,6 +27,7 @@ Documentation
language-interfaces
cti/index
python/index
cython/index
matlab/index
cxx-guide/index

View file

@ -1,8 +1,8 @@
.. Cantera documentation master file, created by
sphinx-quickstart on Mon Mar 12 11:43:09 2012.
Python Module Documentation
===========================
Python Module Documentation (Legacy)
====================================
Contents: