git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41113 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			395 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			395 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import sys, os, string, glob
 | |
| import re
 | |
| from docparser.wxclasses import *
 | |
| import wx
 | |
| 
 | |
| 
 | |
| outputdir = "output"
 | |
| 
 | |
| #
 | |
| # Class REs
 | |
| #
 | |
| 
 | |
| class_desc_re = """<H2>.*?</H2>(.*?)<B><FONT COLOR="#FF0000">"""
 | |
| win_styles_re = """<B><FONT COLOR="#FF0000">Window styles</FONT></B><P>(.*?)<B><FONT COLOR="#FF0000">"""
 | |
| win_styles_extra_re = """<B><FONT COLOR="#FF0000">Extra window styles</FONT></B><P>(.*?)<B><FONT COLOR="#FF0000">"""
 | |
| win_style_re = """<TR><TD VALIGN=TOP WIDTH=.*?>\s*?<FONT FACE=".*?">\s*?<B>(.*?)</B>\s*?</FONT></TD>\s*?<TD VALIGN=TOP>\s*?<FONT FACE=".*?">(.*?)</FONT></TD></TR>"""
 | |
| derived_re = """<B><FONT COLOR="#FF0000">Derived from</FONT></B><P>(.*?)<P>"""
 | |
| derived_class_re = """<A HREF=".*?">(.*?)</A>"""
 | |
| 
 | |
| #
 | |
| # Method REs
 | |
| #
 | |
| 
 | |
| # groups - header, description
 | |
| method_re = "<H3>(.*?)</H3>\s*?<P>(.*?)<HR>"
 | |
| lastmethod_re = "<H3>(.*?)</H3>\s*?<P>(.*?)\s*?<P>\s*?</FONT>"
 | |
| headings_re = "<B><FONT COLOR=\"#FF0000\">(.*?)</FONT></B><P>(.*?)"
 | |
| # groups = param name, param value 
 | |
| param_re = "<I>(.*?)</I><UL><UL>(.*?)</UL></UL>"
 | |
| # groups - return type, method name, arguments
 | |
| proto_re = "<B>(.*?)</B>.*?<B>(.*?)</B>\s*?\((.*?)\)"
 | |
| # groups - arg type, arg name
 | |
| args_re = "<B>(.*?)</B>.*?<I>(.*?)</I>"
 | |
| code_re = "<PRE>(.*?)</PRE>"
 | |
| link_re = "<A href=\"(.*?)\"><B>(.*?)</B></A><BR>"
 | |
| 
 | |
| #
 | |
| # wxPython/wxPerl note REs
 | |
| # 
 | |
| 
 | |
| wx_re = "wx[A-Z]\S+"
 | |
| wxperl_overload_re = "<B><FONT COLOR=\"#0000C8\">wxPerl note:</FONT></B> In wxPerl there are two methods instead of a single overloaded method:<P>\s*?<UL><UL>(.*?)</UL></UL>"
 | |
| wxperl_re = "<B><FONT COLOR=\"#0000C8\">wxPerl note:</FONT></B>(.*?)<P>"
 | |
| 
 | |
| wxpython_constructors_re = """<B><FONT COLOR="#0000C8">wxPython note:</FONT></B> Constructors supported by wxPython are:<P>\s*?<UL><UL>(.*?)</UL></UL>"""
 | |
| wxpython_overload_re = """<TR><TD VALIGN=TOP.*?>\s*?<FONT FACE=".*?">\s*?<B>(.*?)</B>\s*?</FONT></TD>\s*?<TD VALIGN=TOP>\s*?<FONT FACE=".*?">(.*?)</FONT></TD></TR>"""
 | |
| 
 | |
| wxpython_overloads_re = "<B><FONT COLOR=\"#0000C8\">wxPython note:</FONT></B> In place of a single overloaded method name, wxPython\s*?implements the following methods:<P>\s*?<UL><UL>(.*?)</UL></UL>"
 | |
| wxpython_re = "<B><FONT COLOR=\"#0000C8\">wxPython note:</FONT></B>(.*?)<P>"
 | |
| 
 | |
| 
 | |
| # convert wxWhatever to wx.Whatever
 | |
| def namespacify_wxClasses(contents):
 | |
|     wx_regex = re.compile(wx_re, re.MULTILINE | re.DOTALL)
 | |
|     
 | |
|     result = wx_regex.sub(wxReplaceFunc, contents)
 | |
|     return result
 | |
| 
 | |
| def wxReplaceFunc(match):
 | |
|     text = match.group()
 | |
|     if text.find("wxWidgets") == -1 and text.find("wxPython") == -1 and text.find("wxPerl") == -1:
 | |
|         text = text.replace("wx", "wx.")
 | |
|     return text
 | |
| 
 | |
| 
 | |
| 
 | |
| # Methods to de-C++itize data.
 | |
| def pythonize_text(contents):
 | |
|     """
 | |
|     Remove C++isms that definitely shouldn't be in any text.
 | |
|     """
 | |
|     contents = contents.replace("false", "False")
 | |
|     contents = contents.replace("true", "True")
 | |
|     contents = contents.replace("non-NULL", "not None")
 | |
|     contents = contents.replace("NULL", "None")
 | |
|     contents = contents.replace("const ", "")
 | |
|     contents = contents.replace("::", ".")
 | |
|     contents = contents.replace("\r\n", "\n")
 | |
|     contents = contents.replace("\r", "\n")
 | |
|     contents = contents.replace("''", "\"")
 | |
|     return namespacify_wxClasses(contents)
 | |
| 
 | |
| def pythonize_args(contents):
 | |
|     """
 | |
|     Remove C++isms from arguments (some of these terms may be used in other
 | |
|     contexts in actual documentation, so we don't remove them there).
 | |
|     """
 | |
|     contents = contents.replace("static", "")
 | |
|     contents = contents.replace("virtual void", "")
 | |
|     contents = contents.replace("virtual", "")
 | |
|     contents = contents.replace("void*", "int")
 | |
|     contents = contents.replace("void", "")
 | |
|     
 | |
|     contents = contents.replace("off_t", "long")
 | |
|     contents = contents.replace("size_t", "long")
 | |
|     contents = contents.replace("*", "")
 | |
|     contents = contents.replace("&", "")
 | |
|     contents = contents.replace("&", "")
 | |
|     contents = contents.replace("char", "string") 
 | |
|     contents = contents.replace("wxChar", "string") 
 | |
|     contents = contents.replace("wxCoord", "int")
 | |
|     contents = contents.replace("<A HREF=\"wx_wxstring.html#wxstring\">wxString</A>", "string")
 | |
|     
 | |
|     return pythonize_text(contents)
 | |
|     
 | |
| def formatMethodProtos(protos):
 | |
|     """
 | |
|     Remove C++isms in the method prototypes. 
 | |
|     """
 | |
|     for proto in protos:
 | |
|         proto[0] = pythonize_args(proto[0])
 | |
|         proto[0] = proto[0].strip()
 | |
|         
 | |
|         proto[1] = namespacify_wxClasses(proto[1])
 | |
|         for arg in proto[2]:
 | |
|             arg[0] = pythonize_args(arg[0])
 | |
|             arg[0].strip()
 | |
|             
 | |
|             # for arg names, we should be more careful about what we replace
 | |
|             arg[1] = pythonize_text(arg[1])
 | |
|             arg[1] = arg[1].replace("*", "")
 | |
|             arg[1] = arg[1].replace("&", "")
 | |
|     
 | |
|     return protos
 | |
| 
 | |
| 
 | |
| 
 | |
| # functions for getting data from methods 
 | |
| def getMethodWxPythonOverrides(text, isConstructor=False):
 | |
|     overloads_re = wxpython_overloads_re
 | |
|     if isConstructor:
 | |
|         overloads_re = wxpython_constructors_re
 | |
|     overload_regex = re.compile(overloads_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = overload_regex.search(text, 0)
 | |
|     note = ""
 | |
|     start = -1
 | |
|     end = -1
 | |
|     overrides = []
 | |
|     if match:
 | |
|         def getWxPythonOverridesFromMatch(match):
 | |
|             return [namespacify_wxClasses(match.group(1)), pythonize_text(match.group(2))]
 | |
|             
 | |
|         start = match.start()
 | |
|         end = match.end()
 | |
|         overrides, returntext = findAllMatches(wxpython_overload_re, match.group(1), getWxPythonOverridesFromMatch)
 | |
|         
 | |
|     returntext = text
 | |
|     
 | |
|     if start != -1 and end != -1:
 | |
|         #print "note is: " + text[start:end]
 | |
|         returntext = text.replace(text[start:end], "")
 | |
|         
 | |
|     return overrides, returntext
 | |
| 
 | |
| def getMethodWxPythonNote(text):
 | |
|     python_regex = re.compile(wxpython_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = python_regex.search(text)
 | |
|     start = -1
 | |
|     end = -1
 | |
|     note = ""
 | |
|     if match:
 | |
|         start = match.start()
 | |
|         end = match.end()
 | |
|         note = match.group(1)
 | |
|             
 | |
|     returntext = text
 | |
|     
 | |
|     if start != -1 and end != -1:
 | |
|         #print "note is: " + text[start:end]
 | |
|         returntext = text.replace(text[start:end], "")
 | |
|             
 | |
|     return note, returntext
 | |
|     
 | |
| def findAllMatches(re_string, text, handler, start=0):
 | |
|     """
 | |
|     findAllMatches finds matches for a given regex, then runs the handler function
 | |
|     on each match, and returns a list of objects, along with a version of the 
 | |
|     text with the area matches were found stripped.
 | |
|     Note the stripping of text is not generally usable yet, it assumes matches
 | |
|     are in continuous blocks, which is true of the wx docs.
 | |
|     """
 | |
|     regex = re.compile(re_string, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = regex.search(text, start)
 | |
|     results = []
 | |
|     
 | |
|     startpoint = -1
 | |
|     endpoint = -1
 | |
|     
 | |
|     if match:
 | |
|         startpoint = match.start()
 | |
|     
 | |
|     while match:
 | |
|         start = match.end()
 | |
|         results.append(handler(match))
 | |
|         endpoint = match.end()
 | |
|         match = regex.search(text, start)
 | |
|         
 | |
|     returntext = text
 | |
|     if startpoint != -1 and endpoint != -1:
 | |
|         returntext = text.replace(text[startpoint:endpoint], "")
 | |
| 
 | |
|     return results, returntext
 | |
| 
 | |
| def getMethodParams(text):
 | |
|     paramstart = text.find("<B><FONT COLOR=\"#FF0000\">Parameters</FONT></B><P>")
 | |
|     params, returntext = findAllMatches(param_re, text, getMethodParamsFromMatch, paramstart)
 | |
|     
 | |
|     return params, returntext
 | |
|     
 | |
| def getMethodParamsFromMatch(match):
 | |
|     return [match.group(1).strip(), pythonize_text(match.group(2)).strip()]
 | |
| 
 | |
| def getPrototypeFromMatch(match):
 | |
|     return [match.group(1), match.group(2), getProtoArgs(match.group(3))]
 | |
| 
 | |
| def getProtoArgsFromMatch(match):
 | |
|     return [match.group(1), match.group(2)]
 | |
| 
 | |
| 
 | |
| 
 | |
| # These methods parse the docs, finding matches and then using the FromMatch
 | |
| # functions to parse the data. After that, the results are "Pythonized"
 | |
| # by removing C++isms.
 | |
| def getMethodProtos(text):
 | |
|     protos, returntext = findAllMatches(proto_re, text, getPrototypeFromMatch)
 | |
|     return formatMethodProtos(protos), returntext
 | |
|     
 | |
| def getProtoArgs(text):
 | |
|     args, returntext = findAllMatches(args_re, text, getProtoArgsFromMatch)
 | |
|     return args
 | |
|     
 | |
| def getMethodDesc(text):
 | |
|     heading_text = "<B><FONT COLOR=\"#FF0000\">"
 | |
|     return_text = text
 | |
|     end = text.find(heading_text)
 | |
|     if end != -1:
 | |
|         return_text = text[0:end]
 | |
|         
 | |
|     return pythonize_text(return_text)
 | |
|     
 | |
| 
 | |
| def removeWxPerlNotes(text):
 | |
|     perl_overload_regex = re.compile(wxperl_overload_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     result = perl_overload_regex.sub("", text)
 | |
|     
 | |
|     perl_regex = re.compile(wxperl_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     result = perl_regex.sub("", result)
 | |
|     
 | |
|     return result
 | |
|     
 | |
| def removeCPPCode(text):
 | |
|     code_regex = re.compile(code_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     
 | |
|     result = code_regex.sub("", text)
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def getMethod(match, parent):
 | |
|     name = match.group(1)
 | |
|     if name.find("::") != -1:
 | |
|         name = name.split("::")[1]
 | |
|     name = namespacify_wxClasses(name).strip()
 | |
|     start = match.end()
 | |
|     protos, remainder = getMethodProtos(match.group(2))
 | |
|     
 | |
|     isConstructor = False
 | |
|     #print "name: %s, parent name: %s" % (name, parent.name)
 | |
|     if name == parent.name.replace("wx", "wx."):
 | |
|         isConstructor = True
 | |
|     overrides, remainder = getMethodWxPythonOverrides(remainder, isConstructor)
 | |
|     
 | |
|     note, remainder = getMethodWxPythonNote(remainder)
 | |
|     params, remainder = getMethodParams(remainder)
 | |
|     desc = getMethodDesc(remainder)
 | |
|     method = wxMethod(name, parent, protos, params, desc)
 | |
|     method.pythonNote = note
 | |
|     method.pythonOverrides = overrides
 | |
|     if len(method.pythonOverrides) > 0:
 | |
|         print "has overrides!\n\n\n\n"
 | |
|     return method
 | |
| 
 | |
| def getClassDerivedFrom(text):
 | |
| 
 | |
|     def getDerivedClassesFromMatch(match):
 | |
|         return namespacify_wxClasses(match.group(1))
 | |
| 
 | |
|     derived_classes = []
 | |
|     derived_regex = re.compile(derived_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = derived_regex.search(text)
 | |
|     if match:
 | |
|         derived_classes, returntext = findAllMatches(derived_class_re, match.group(1), getDerivedClassesFromMatch)
 | |
|         
 | |
|     return derived_classes
 | |
|     
 | |
| def getClassDescription(text):
 | |
|     
 | |
|     def getClassDescriptionFromMatch(match):
 | |
|         return match.group(1)
 | |
|     
 | |
|     desc, returntext = findAllMatches(class_desc_re, text, getClassDescriptionFromMatch)
 | |
|     
 | |
|     return pythonize_text(desc[0])
 | |
|     
 | |
| def getClassStyles(text, extraStyles=False):
 | |
|     styles_re = win_styles_re
 | |
|     if extraStyles:
 | |
|         styles_re = win_styles_extra_re
 | |
|     styles_regex = re.compile(styles_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = styles_regex.search(text)
 | |
|     
 | |
|     styles = []
 | |
|     if match:
 | |
|         def getClassStyleFromMatch(match):
 | |
|             return [namespacify_wxClasses(match.group(1)), pythonize_text(match.group(2))]
 | |
|             
 | |
|         styles, remainder = findAllMatches(win_style_re, match.group(1), getClassStyleFromMatch)
 | |
|         
 | |
|     return styles
 | |
| 
 | |
| # Main functions - these drive the process.
 | |
| def getClassMethods(doc, parent):
 | |
|     contents = open(doc, "rb").read()
 | |
|     
 | |
|     # get rid of some particularly tricky parts before parsing
 | |
|     contents = contents.replace("<B>const</B>", "")
 | |
|     contents = removeWxPerlNotes(contents)
 | |
|     contents = removeCPPCode(contents)
 | |
|     
 | |
|     method_regex = re.compile(method_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = method_regex.search(contents)
 | |
|     start = 0
 | |
|     methods = {}
 | |
|     while match:
 | |
|         start = match.end()
 | |
|         newmethod = getMethod(match, parent)
 | |
|         basename = parent.name.replace("wx", "")
 | |
|         isConstructor = (basename == newmethod.name.replace("wx.", ""))
 | |
|         if isConstructor or eval("newmethod.name in dir(wx.%s)" % basename):
 | |
|             print "Adding %s.%s" % (parent.name, newmethod.name)
 | |
|             methods[newmethod.name] = newmethod
 | |
|         match = method_regex.search(contents, start)
 | |
|     
 | |
|     lastmethod_regex = re.compile(lastmethod_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     match = lastmethod_regex.search(contents, start)
 | |
|     if match: 
 | |
|         newmethod = getMethod(match, parent)
 | |
|         basename = parent.name.replace("wx", "")
 | |
|         isConstructor = (basename == newmethod.name.replace("wx.", ""))
 | |
|         if isConstructor or eval("newmethod.name in dir(wx.%s)" % basename):
 | |
|             print "Adding %s.%s" % (parent.name, newmethod.name)
 | |
|             methods[newmethod.name] = newmethod
 | |
|     
 | |
|     for name in methods:
 | |
|         if name[0:3] == "Get":
 | |
|             propname = name[3:]
 | |
|             basename = parent.name.replace("wx", "")
 | |
|             if not propname in eval("dir(wx.%s)" % basename):
 | |
|                 parent.props.append(propname)
 | |
|             else:
 | |
|                 parent.propConflicts.append(parent.name + "." + propname)
 | |
|     # get rid of the destructor and operator methods
 | |
|     ignore_methods = ["~" + namespacify_wxClasses(parent.name), "operator ==", 
 | |
|                         "operator <<", "operator >>", "operator =", 
 | |
|                         "operator !=", "operator*", "operator++" ]
 | |
|     for method in ignore_methods:
 | |
|         if method in methods:
 | |
|             methods.pop(method)
 | |
|             
 | |
|     return methods
 | |
|         
 | |
| def getClasses(doc):
 | |
|     global docspath
 | |
|     contents = open(doc, "rb").read()
 | |
|     link_regex = re.compile(link_re, re.MULTILINE | re.DOTALL | re.IGNORECASE)
 | |
|     start = contents.find("<H2>Alphabetical class reference</H2>")
 | |
|     result = link_regex.search(contents, start)
 | |
|     classes = {}
 | |
|     while result:
 | |
|         start = result.end()
 | |
|         name = result.group(2).strip()
 | |
|         classpage = result.group(1).split("#")[0]
 | |
|         basename = name.replace("wx", "")
 | |
|         if basename in dir(wx):
 | |
|             classfile = os.path.join(os.path.dirname(doc), classpage)
 | |
|             classtext = open(classfile, "rb").read()
 | |
|             derivedClasses = getClassDerivedFrom(classtext)
 | |
|             description = getClassDescription(classtext)
 | |
|             styles = getClassStyles(classtext)
 | |
|             extra_styles = getClassStyles(classtext, extraStyles=True)
 | |
|             classes[name] = wxClass(name, description, derivedClasses, styles, extra_styles)
 | |
|             classes[name].methods = getClassMethods(classfile, classes[name])
 | |
|         result = link_regex.search(contents, start)
 | |
| 
 | |
|     return classes
 |