| 1 | #!/usr/bin/python |
| 2 | #--------------------------------------------------------------------------- |
| 3 | # |
| 4 | # Like simplify.xsl but using Python so a few non-standard conversions can |
| 5 | # also be done. (Currently it is still about the same as simplify.xsl...) |
| 6 | # |
| 7 | #--------------------------------------------------------------------------- |
| 8 | |
| 9 | import sys |
| 10 | import os |
| 11 | import libxml2 |
| 12 | |
| 13 | |
| 14 | DEST="docs/xml/wxPython-metadata.xml" |
| 15 | SRC="docs/xml-raw" |
| 16 | |
| 17 | |
| 18 | classMap = { |
| 19 | 'wxString' : 'String', |
| 20 | 'void' : '', |
| 21 | } |
| 22 | |
| 23 | |
| 24 | def getModuleNames(): |
| 25 | """ |
| 26 | Get the list of extension modules from setup.py |
| 27 | """ |
| 28 | import setup |
| 29 | names = [e.name[1:] for e in setup.wxpExtensions] |
| 30 | return names |
| 31 | |
| 32 | |
| 33 | |
| 34 | def getAttr(node, name): |
| 35 | """ |
| 36 | Get a value by name from the <attribute> elements in the SWIG XML output |
| 37 | """ |
| 38 | path = "./attributelist/attribute[@name='%s']/@value" % name |
| 39 | n = node.xpathEval2(path) |
| 40 | if len(n): |
| 41 | return n[0].content |
| 42 | else: |
| 43 | return None |
| 44 | |
| 45 | |
| 46 | def fixType(typeStr): |
| 47 | """ |
| 48 | Fixup type string, dropping the swig pointer and other flags |
| 49 | """ |
| 50 | pos = typeStr.rfind('.') |
| 51 | if pos != -1: |
| 52 | typeStr = typeStr[pos+1:] |
| 53 | return classMap.get(typeStr, typeStr) |
| 54 | |
| 55 | |
| 56 | def processModule(newDocNode, modulename): |
| 57 | """ |
| 58 | Start processing a new XML file, create a module element and then |
| 59 | find the include elements |
| 60 | """ |
| 61 | filename = os.path.join(SRC, "%s_swig.xml" % modulename) |
| 62 | print filename |
| 63 | |
| 64 | doc = libxml2.parseFile(filename) |
| 65 | topNode = doc.getRootElement() |
| 66 | |
| 67 | # make a module element |
| 68 | name = getAttr(topNode, "module") |
| 69 | assert name == modulename # sanity check |
| 70 | |
| 71 | moduleNode = libxml2.newNode("module") |
| 72 | moduleNode.setProp("name", name) |
| 73 | newDocNode.addChild(moduleNode) |
| 74 | |
| 75 | node = topNode.children |
| 76 | while node is not None: |
| 77 | if node.name == "include": |
| 78 | processInclude(moduleNode, node) |
| 79 | node = node.next |
| 80 | |
| 81 | doc.freeDoc() |
| 82 | |
| 83 | |
| 84 | |
| 85 | def processInclude(moduleNode, includeNode): |
| 86 | """ |
| 87 | Almost everything we are interested in is inside an <include>, |
| 88 | which may also be nested. |
| 89 | """ |
| 90 | |
| 91 | # check first for imports |
| 92 | for node in includeNode.xpathEval2("import"): |
| 93 | try: |
| 94 | modNode = node.xpathEval2("module")[0] |
| 95 | name = getAttr(modNode, "name") |
| 96 | impNode = libxml2.newNode("import") |
| 97 | impNode.setProp("name", name) |
| 98 | moduleNode.addChild(impNode) |
| 99 | except IndexError: |
| 100 | pass |
| 101 | |
| 102 | # then look through the child nodes for other things we need |
| 103 | node = includeNode.children |
| 104 | while node is not None: |
| 105 | if node.name == "insert": |
| 106 | processInsert(moduleNode, node) |
| 107 | |
| 108 | elif node.name == "class": |
| 109 | processClass(moduleNode, node) |
| 110 | |
| 111 | elif node.name == "cdecl" and getAttr(node, "view") == "globalfunctionHandler": |
| 112 | func = libxml2.newNode("method") |
| 113 | func.setProp("name", getAttr(node, "sym_name")) |
| 114 | func.setProp("oldname", getAttr(node, "name")) |
| 115 | func.setProp("type", fixType(getAttr(node, "type"))) |
| 116 | doCheckOverloaded(func, node) |
| 117 | doDocStrings(func, node) |
| 118 | doParamList(func, node) |
| 119 | moduleNode.addChild(func) |
| 120 | |
| 121 | |
| 122 | elif node.name == "include": |
| 123 | processInclude(moduleNode, node) |
| 124 | |
| 125 | node = node.next |
| 126 | |
| 127 | |
| 128 | |
| 129 | def processInsert(parentNode, insertNode): |
| 130 | """ |
| 131 | Check for pythoncode |
| 132 | """ |
| 133 | if getAttr(insertNode, "section") == "python": |
| 134 | code = getAttr(insertNode, "code") |
| 135 | node = libxml2.newNode("pythoncode") |
| 136 | node.addChild(libxml2.newText(code)) |
| 137 | parentNode.addChild(node) |
| 138 | |
| 139 | |
| 140 | |
| 141 | def processClass(parentNode, classNode): |
| 142 | """ |
| 143 | Handle classes, constructors, methods, etc. |
| 144 | """ |
| 145 | # make class element |
| 146 | klass = libxml2.newNode("class") |
| 147 | name = getAttr(classNode, "sym_name") |
| 148 | oldname = getAttr(classNode, "name") |
| 149 | classMap[oldname] = name |
| 150 | klass.setProp("name", name) |
| 151 | klass.setProp("oldname", oldname) |
| 152 | klass.setProp("module", getAttr(classNode, "module")) |
| 153 | doDocStrings(klass, classNode) |
| 154 | parentNode.addChild(klass) |
| 155 | |
| 156 | # check for baseclass(es) |
| 157 | for node in classNode.xpathEval2("attributelist/baselist/base"): |
| 158 | baseclass = libxml2.newNode("baseclass") |
| 159 | basename = node.prop("name") |
| 160 | baseclass.setProp("name", classMap.get(basename, basename)) |
| 161 | klass.addChild(baseclass) |
| 162 | |
| 163 | # check for constructors/destructors |
| 164 | for type in ["constructor", "destructor"]: |
| 165 | for node in classNode.xpathEval2("%s | extend/%s" % (type, type)): |
| 166 | func = libxml2.newNode(type) |
| 167 | func.setProp("name", getAttr(node, "sym_name")) |
| 168 | if parentNode.name != "destructor": |
| 169 | doCheckOverloaded(func, node) |
| 170 | doDocStrings(func, node) |
| 171 | doParamList(func, node) |
| 172 | klass.addChild(func) |
| 173 | |
| 174 | # check for cdecl's. In class scope we are interested in methods, |
| 175 | # static methods, or properties |
| 176 | for node in classNode.xpathEval2("cdecl | extend/cdecl"): |
| 177 | view = getAttr(node, "view") |
| 178 | if view == "memberfunctionHandler": |
| 179 | func = libxml2.newNode("method") |
| 180 | func.setProp("name", getAttr(node, "sym_name")) |
| 181 | func.setProp("type", fixType(getAttr(node, "type"))) |
| 182 | doCheckOverloaded(func, node) |
| 183 | doDocStrings(func, node) |
| 184 | doParamList(func, node) |
| 185 | klass.addChild(func) |
| 186 | |
| 187 | elif view == "staticmemberfunctionHandler": |
| 188 | func = libxml2.newNode("staticmethod") |
| 189 | func.setProp("name", getAttr(node, "sym_name")) |
| 190 | func.setProp("type", fixType(getAttr(node, "type"))) |
| 191 | doCheckOverloaded(func, node) |
| 192 | doDocStrings(func, node) |
| 193 | doParamList(func, node) |
| 194 | klass.addChild(func) |
| 195 | |
| 196 | elif view == "variableHandler": |
| 197 | prop = libxml2.newNode("property") |
| 198 | prop.setProp("name", getAttr(node, "sym_name")) |
| 199 | prop.setProp("type", fixType(getAttr(node, "type"))) |
| 200 | if getAttr(node, "feature_immutable"): |
| 201 | prop.setProp("readonly", "yes") |
| 202 | else: |
| 203 | prop.setProp("readonly", "no") |
| 204 | doDocStrings(prop, node) |
| 205 | klass.addChild(prop) |
| 206 | |
| 207 | |
| 208 | |
| 209 | def doParamList(parentNode, srcNode): |
| 210 | """ |
| 211 | Convert the parameter list |
| 212 | """ |
| 213 | params = srcNode.xpathEval2("attributelist/parmlist/parm") |
| 214 | if params: |
| 215 | plist = libxml2.newNode("paramlist") |
| 216 | for p in params: |
| 217 | pnode = libxml2.newNode("param") |
| 218 | pnode.setProp("name", getAttr(p, "name")) |
| 219 | pnode.setProp("type", fixType(getAttr(p, "type"))) |
| 220 | pnode.setProp("default", getAttr(p, "value")) |
| 221 | plist.addChild(pnode) |
| 222 | parentNode.addChild(plist) |
| 223 | |
| 224 | |
| 225 | |
| 226 | def doCheckOverloaded(parentNode, srcNode): |
| 227 | """ |
| 228 | Set an attribute indicating if the srcNode is tagged as being overloaded |
| 229 | """ |
| 230 | if srcNode.xpathEval2("./attributelist/attribute[@name='sym_overloaded']"): |
| 231 | parentNode.setProp("overloaded", "yes") |
| 232 | else: |
| 233 | parentNode.setProp("overloaded", "no") |
| 234 | |
| 235 | |
| 236 | |
| 237 | def doDocStrings(parentNode, srcNode): |
| 238 | """ |
| 239 | Check for the various possible docstring attributes, and attach |
| 240 | coresponding child nodes if found. |
| 241 | """ |
| 242 | def makeDocElement(name, content): |
| 243 | node = libxml2.newNode(name) |
| 244 | node.addChild(libxml2.newText(content)) |
| 245 | return node |
| 246 | |
| 247 | autodoc = getAttr(srcNode, "python_autodoc") |
| 248 | docstr = getAttr(srcNode, "feature_docstring") |
| 249 | refdoc = getAttr(srcNode, "feature_refdoc") |
| 250 | if autodoc: |
| 251 | parentNode.addChild(makeDocElement("autodoc", autodoc)) |
| 252 | if docstr: |
| 253 | parentNode.addChild(makeDocElement("docstring", docstr)) |
| 254 | if refdoc: |
| 255 | parentNode.addChild(makeDocElement("refdoc", refdoc)) |
| 256 | |
| 257 | |
| 258 | |
| 259 | |
| 260 | |
| 261 | def main(): |
| 262 | if not os.path.exists(SRC): |
| 263 | print "Unable to find %s, please run this script from the root wxPython directory." % SRC |
| 264 | sys.exit(1) |
| 265 | |
| 266 | newDoc = libxml2.newDoc("1.0") |
| 267 | newTopNode = libxml2.newNode("wxPython-metadata") |
| 268 | newDoc.addChild(newTopNode) |
| 269 | |
| 270 | for m in getModuleNames(): |
| 271 | processModule(newTopNode, m) |
| 272 | |
| 273 | newDoc.saveFormatFile(DEST, True) |
| 274 | print "Wrote simplified metadata to", DEST |
| 275 | |
| 276 | #--------------------------------------------------------------------------- |
| 277 | |
| 278 | if __name__ == "__main__": |
| 279 | main() |