Commit 133b80e1 authored by Peter Eastman's avatar Peter Eastman
Browse files

Python API generator no longer requires xpath library

parent 3eff31f2
...@@ -11,20 +11,31 @@ import sys, os ...@@ -11,20 +11,31 @@ import sys, os
import time import time
import getopt import getopt
import re import re
import xml.dom.minidom as minidom import xml.etree.ElementTree as etree
import xpath
# #
INDENT = " "; INDENT = " ";
def trimToSingleSpace(text):
if text is None or len(text) == 0:
return ""
t = text.strip()
if len(t) == 0:
return t
if text[0].isspace():
t = " %s" % t
if text[-1].isspace():
t = "%s " % t
return t
def getText(subNodePath, node): def getText(subNodePath, node):
xPath="%s/text() | %s/ref/text()" % (subNodePath, subNodePath)
subNodes = xpath.find(xPath, node)
s="" s=""
for n in xpath.find(xPath, node): for n in node.findall(subNodePath):
s="%s%s" % (s, n.data) s = "%s%s" % (s, trimToSingleSpace(n.text))
for r in n.findall("ref"):
s = "%s%s%s" % (s, trimToSingleSpace(r.text), trimToSingleSpace(r.tail))
s = "%s%s" % (s, trimToSingleSpace(n.tail))
return s.strip() return s.strip()
OPENMM_RE_PATTERN=re.compile("(.*)OpenMM:[a-zA-Z:]*:(.*)") OPENMM_RE_PATTERN=re.compile("(.*)OpenMM:[a-zA-Z:]*:(.*)")
...@@ -34,20 +45,28 @@ def stripOpenmmPrefix(name, rePattern=OPENMM_RE_PATTERN): ...@@ -34,20 +45,28 @@ def stripOpenmmPrefix(name, rePattern=OPENMM_RE_PATTERN):
rValue.strip() rValue.strip()
return rValue return rValue
def findNodes(parent, path, **args):
nodes = []
for node in parent.findall(path):
match = True
for arg in args:
if arg not in node.attrib or node.attrib[arg] != args[arg]:
match = False
if match:
nodes.append(node)
return nodes
def getClassMethodList(classNode, skipMethods): def getClassMethodList(classNode, skipMethods):
className = getText("compoundname", classNode) className = getText("compoundname", classNode)
shortClassName=stripOpenmmPrefix(className) shortClassName=stripOpenmmPrefix(className)
xPath1 = "sectiondef[@kind='public-static-func']/memberdef[@kind='function' and @prot='public']"
xPath2 = "sectiondef[@kind='public-func']/memberdef[@kind='function' and @prot='public']"
methodList=[] methodList=[]
for memberNode in xpath.find(xPath1, classNode) \ for section in findNodes(classNode, "sectiondef", kind="public-static-func")+findNodes(classNode, "sectiondef", kind="public-func"):
+xpath.find(xPath2, classNode): for memberNode in findNodes(section, "memberdef", kind="function", prot="public"):
methDefinition = getText("definition", memberNode) methDefinition = getText("definition", memberNode)
shortMethDefinition=stripOpenmmPrefix(methDefinition) shortMethDefinition=stripOpenmmPrefix(methDefinition)
methName=shortMethDefinition.split()[-1] methName=shortMethDefinition.split()[-1]
if (shortClassName, methName) in skipMethods: continue if (shortClassName, methName) in skipMethods: continue
numParams=len(xpath.find('param', memberNode)) numParams=len(findNodes(memberNode, 'param'))
if (shortClassName, methName, numParams) in skipMethods: continue if (shortClassName, methName, numParams) in skipMethods: continue
for catchString in ['Factory', 'Impl', 'Info', 'Kernel']: for catchString in ['Factory', 'Impl', 'Info', 'Kernel']:
if shortClassName.endswith(catchString): if shortClassName.endswith(catchString):
...@@ -91,7 +110,7 @@ class SwigInputBuilder: ...@@ -91,7 +110,7 @@ class SwigInputBuilder:
items[2]=int(items[2]) items[2]=int(items[2])
self.skipMethods.append(tuple(items)) self.skipMethods.append(tuple(items))
self.doc = minidom.parse(inputFilename) self.doc = etree.parse(inputFilename)
if outputFilename: if outputFilename:
self.fOut = open(outputFilename, 'w') self.fOut = open(outputFilename, 'w')
...@@ -119,14 +138,13 @@ class SwigInputBuilder: ...@@ -119,14 +138,13 @@ class SwigInputBuilder:
def _getNodeByID(self, id): def _getNodeByID(self, id):
if id not in self.nodeByID: if id not in self.nodeByID:
xPath = "/doxygen/compounddef[@id='%s']" % id for node in findNodes(self.doc.getroot(), "compounddef", id=id):
self.nodeByID[id] = xpath.find(xPath, self.doc)[0] self.nodeByID[id] = node
return self.nodeByID[id] return self.nodeByID[id]
def _buildOrderedClassNodes(self): def _buildOrderedClassNodes(self):
orderedClassNodes=[] orderedClassNodes=[]
xPath = "/doxygen/compounddef[@kind='class' and @prot='public']" for node in findNodes(self.doc.getroot(), "compounddef", kind="class", prot="public"):
for node in xpath.find(xPath, self.doc):
self._findBaseNodes(node, orderedClassNodes) self._findBaseNodes(node, orderedClassNodes)
return orderedClassNodes return orderedClassNodes
...@@ -135,10 +153,9 @@ class SwigInputBuilder: ...@@ -135,10 +153,9 @@ class SwigInputBuilder:
nodeName = getText("compoundname", node) nodeName = getText("compoundname", node)
if (nodeName.split("::")[-1],) in self.skipMethods: if (nodeName.split("::")[-1],) in self.skipMethods:
return return
xPath = "basecompoundref[@prot='public']" for baseNodePnt in findNodes(node, "basecompoundref", prot="public"):
for baseNodePnt in xpath.find(xPath, node): baseNodeID = baseNodePnt.attrib["refid"]
baseNodeID=baseNodePnt.getAttribute("refid") baseNode = self._getNodeByID(baseNodeID)
baseNode=self._getNodeByID(baseNodeID)
self._findBaseNodes(baseNode, excludedClassNodes) self._findBaseNodes(baseNode, excludedClassNodes)
excludedClassNodes.append(node) excludedClassNodes.append(node)
...@@ -146,17 +163,15 @@ class SwigInputBuilder: ...@@ -146,17 +163,15 @@ class SwigInputBuilder:
def writeForceSubclassList(self): def writeForceSubclassList(self):
self.fOut.write("\n/* Force subclasses */\n\n") self.fOut.write("\n/* Force subclasses */\n\n")
forceSubclassList=[] forceSubclassList=[]
xPath = "/doxygen/compounddef[@kind='class' and @prot='public']" for classNode in findNodes(self.doc.getroot(), "compounddef", kind="class", prot="public"):
for classNode in xpath.find(xPath, self.doc):
className = getText("compoundname", classNode) className = getText("compoundname", classNode)
shortClassName=stripOpenmmPrefix(className) shortClassName=stripOpenmmPrefix(className)
if (className.split("::")[-1],) in self.skipMethods: if (className.split("::")[-1],) in self.skipMethods:
continue continue
#print className #print className
#print classNode.toxml() #print classNode.toxml()
xPath = "basecompoundref[@prot='public']" for baseNodePnt in findNodes(classNode, "basecompoundref", prot="public"):
for baseNodePnt in xpath.find(xPath, classNode): baseNodeID=baseNodePnt.attrib["refid"]
baseNodeID=baseNodePnt.getAttribute("refid")
baseNode=self._getNodeByID(baseNodeID) baseNode=self._getNodeByID(baseNodeID)
baseName = getText("compoundname", baseNode) baseName = getText("compoundname", baseNode)
if baseName == 'OpenMM::Force': if baseName == 'OpenMM::Force':
...@@ -173,10 +188,9 @@ class SwigInputBuilder: ...@@ -173,10 +188,9 @@ class SwigInputBuilder:
def writeGlobalConstants(self): def writeGlobalConstants(self):
self.fOut.write("/* Global Constants */\n\n") self.fOut.write("/* Global Constants */\n\n")
xPath = "/doxygen/compounddef[@kind='namespace' and compoundname='OpenMM']" node = (x for x in findNodes(self.doc.getroot(), "compounddef", kind="namespace") if x.findtext("compoundname") == "OpenMM").next()
node = xpath.find(xPath, self.doc)[0] for section in findNodes(node, "sectiondef", kind="var"):
xPath="sectiondef[@kind='var']/memberdef[@kind='variable' and @mutable='no' and @prot='public' and @static='yes']" for memberNode in findNodes(section, "memberdef", kind="variable", mutable="no", prot="public", static="yes"):
for memberNode in xpath.find(xPath, node):
vDef = stripOpenmmPrefix(getText("definition", memberNode)) vDef = stripOpenmmPrefix(getText("definition", memberNode))
iDef = getText("initializer", memberNode) iDef = getText("initializer", memberNode)
self.fOut.write("static %s = %s;\n" % (vDef, iDef)) self.fOut.write("static %s = %s;\n" % (vDef, iDef))
...@@ -211,8 +225,7 @@ class SwigInputBuilder: ...@@ -211,8 +225,7 @@ class SwigInputBuilder:
self.fOut.write(" : public %s" % self.fOut.write(" : public %s" %
self.configModule.MISSING_BASE_CLASSES[className]) self.configModule.MISSING_BASE_CLASSES[className])
xPath = "basecompoundref[@prot='public']" for baseNodePnt in findNodes(classNode, "basecompoundref", prot="public"):
for baseNodePnt in xpath.find(xPath, classNode):
baseName = stripOpenmmPrefix(getText(".", baseNodePnt)) baseName = stripOpenmmPrefix(getText(".", baseNodePnt))
self.fOut.write(" : public %s" % baseName) self.fOut.write(" : public %s" % baseName)
self.fOut.write(" {\n") self.fOut.write(" {\n")
...@@ -223,8 +236,10 @@ class SwigInputBuilder: ...@@ -223,8 +236,10 @@ class SwigInputBuilder:
self.fOut.write("\n") self.fOut.write("\n")
def writeEnumerations(self, classNode): def writeEnumerations(self, classNode):
xPath = "sectiondef[@kind='public-type']/memberdef[@kind='enum' and @prot='public']" enumNodes = []
enumNodes=xpath.find(xPath, classNode) for section in findNodes(classNode, "sectiondef", kind="public-type"):
for node in findNodes(section, "memberdef", kind="enum", prot="public"):
enumNodes.append(node)
for enumNode in enumNodes: for enumNode in enumNodes:
className = getText("compoundname", classNode) className = getText("compoundname", classNode)
shortClassName=stripOpenmmPrefix(className) shortClassName=stripOpenmmPrefix(className)
...@@ -235,7 +250,7 @@ class SwigInputBuilder: ...@@ -235,7 +250,7 @@ class SwigInputBuilder:
self._enumByClassname[shortClassName]=[enumName] self._enumByClassname[shortClassName]=[enumName]
self.fOut.write("%senum %s {" % (INDENT, enumName)) self.fOut.write("%senum %s {" % (INDENT, enumName))
argSep="\n" argSep="\n"
for valueNode in xpath.find("enumvalue[@prot='public']", enumNode): for valueNode in findNodes(enumNode, "enumvalue", prot="public"):
vName = getText("name", valueNode) vName = getText("name", valueNode)
vInit = getText("initializer", valueNode) vInit = getText("initializer", valueNode)
self.fOut.write("%s%s%s = %s" % (argSep, 2*INDENT, vName, vInit)) self.fOut.write("%s%s%s = %s" % (argSep, 2*INDENT, vName, vInit))
...@@ -288,7 +303,7 @@ class SwigInputBuilder: ...@@ -288,7 +303,7 @@ class SwigInputBuilder:
self.fOut.write('%%feature("autodoc", "%s") %s;\n' % self.fOut.write('%%feature("autodoc", "%s") %s;\n' %
(self.configModule.DOC_STRINGS[key], methName)) (self.configModule.DOC_STRINGS[key], methName))
paramList=xpath.find('param', memberNode) paramList=findNodes(memberNode, 'param')
for pNode in paramList: for pNode in paramList:
try: try:
pType = getText('type', pNode) pType = getText('type', pNode)
...@@ -315,7 +330,7 @@ class SwigInputBuilder: ...@@ -315,7 +330,7 @@ class SwigInputBuilder:
pExceptions = " %s" % getText('exceptions', memberNode) pExceptions = " %s" % getText('exceptions', memberNode)
except IndexError: except IndexError:
pExceptions = "" pExceptions = ""
if memberNode.getAttribute("virt").strip()!='non-virtual': if memberNode.attrib["virt"].strip()!='non-virtual':
if 'virtual' not in shortMethDefinition.split(): if 'virtual' not in shortMethDefinition.split():
shortMethDefinition="virtual %s" % shortMethDefinition shortMethDefinition="virtual %s" % shortMethDefinition
if( len(templateType) > 0 ): if( len(templateType) > 0 ):
...@@ -356,7 +371,7 @@ class SwigInputBuilder: ...@@ -356,7 +371,7 @@ class SwigInputBuilder:
(shortClassName, memberNode, (shortClassName, memberNode,
shortMethDefinition, methName, shortMethDefinition, methName,
isConstructors, isDestructor, templateType, templateName) = items isConstructors, isDestructor, templateType, templateName) = items
paramList=xpath.find('param', memberNode) paramList=findNodes(memberNode, 'param')
#write pythonprepend blocks #write pythonprepend blocks
mArgsstring = getText("argsstring", memberNode) mArgsstring = getText("argsstring", memberNode)
...@@ -458,7 +473,7 @@ class SwigInputBuilder: ...@@ -458,7 +473,7 @@ class SwigInputBuilder:
(shortClassName, methName, (shortClassName, methName,
pName, unitType)) pName, unitType))
outputIndex+=1 outputIndex+=1
if memberNode.getAttribute("const")=="yes": if memberNode.attrib["const"]=="yes":
constString=' const' constString=' const'
else: else:
constString='' constString=''
...@@ -474,7 +489,7 @@ class SwigInputBuilder: ...@@ -474,7 +489,7 @@ class SwigInputBuilder:
shortMethDefinition, methName, shortMethDefinition, methName,
isConstructors, isDestructor, templateType, templateName ) = items isConstructors, isDestructor, templateType, templateName ) = items
if self.fOutDocstring: if self.fOutDocstring:
for dNode in xpath.find('detaileddescription', memberNode): for dNode in findNodes(memberNode, 'detaileddescription'):
dString="" dString=""
try: try:
description=getText('para', dNode) description=getText('para', dNode)
...@@ -483,7 +498,7 @@ class SwigInputBuilder: ...@@ -483,7 +498,7 @@ class SwigInputBuilder:
dString=description dString=description
except IndexError: except IndexError:
pass pass
for pNode in xpath.find('para/parameterlist/parameteritem', dNode): for pNode in findNodes(dNode, 'para/parameterlist/parameteritem'):
argName = getText('parameternamelist/parametername', pNode) argName = getText('parameternamelist/parametername', pNode)
argDoc = getText('parameterdescription/para', pNode) argDoc = getText('parameterdescription/para', pNode)
dString="%s\n %s -- %s" % (dString, argName, argDoc) dString="%s\n %s -- %s" % (dString, argName, argDoc)
......
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