mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-05 19:40:12 +01:00
* Implement wx.CallAfter and wx.CallLater this way for the initial test cases. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@70010 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
450 lines
16 KiB
Python
450 lines
16 KiB
Python
#---------------------------------------------------------------------------
|
|
# Name: etgtools/pi_generator.py
|
|
# Author: Robin Dunn
|
|
#
|
|
# Created: 18-Oct-2011
|
|
# Copyright: (c) 2011 by Total Control Software
|
|
# License: wxWindows License
|
|
#---------------------------------------------------------------------------
|
|
|
|
"""
|
|
This generator will create "Python Interface" files, which define a skeleton
|
|
verison of the classes, functions, attributes, docstrings, etc. as Python
|
|
code. This is useful for enabling some introspcetion of things located in
|
|
extension modules where there is less information available for
|
|
introspection. The .pi files are used by WingIDE for assisting with code
|
|
completion, displaying docstrings in the source assistant panel, etc.
|
|
"""
|
|
|
|
import sys, os, re
|
|
import extractors
|
|
import generators
|
|
from generators import nci
|
|
from tweaker_tools import removeWxPrefix, magicMethods
|
|
from cStringIO import StringIO
|
|
|
|
|
|
phoenixRoot = os.path.abspath(os.path.split(__file__)[0]+'/..')
|
|
header = """\
|
|
#---------------------------------------------------------------------------
|
|
# This file is generated by wxPython's PI generator. Do not edit by hand.
|
|
#
|
|
# (The *.pi files are used by WingIDE to provide more information than it is
|
|
# able to glean from introspection of extension types and methods.)
|
|
#
|
|
# Copyright: (c) 2011 by Total Control Software
|
|
# License: wxWindows License
|
|
#---------------------------------------------------------------------------
|
|
|
|
"""
|
|
#---------------------------------------------------------------------------
|
|
|
|
class PiWrapperGenerator(generators.WrapperGeneratorBase):
|
|
|
|
def generate(self, module, destFile=None):
|
|
stream = StringIO()
|
|
|
|
# generate SIP code from the module and its objects
|
|
self.generateModule(module, stream)
|
|
|
|
# Write the contents of the stream to the destination file
|
|
if not destFile:
|
|
name = module.module + '.pi'
|
|
if name.startswith('_'):
|
|
name = name[1:]
|
|
destFile = os.path.join(phoenixRoot, 'wx', name)
|
|
|
|
if not os.path.exists(destFile):
|
|
# create the file and write the header
|
|
f = file(destFile, 'wt')
|
|
f.write(header)
|
|
f.close()
|
|
|
|
self.writeSection(destFile, module.name, stream.getvalue())
|
|
|
|
|
|
def writeSection(self, destFile, sectionName, sectionText):
|
|
"""
|
|
Read all the lines from destFile, remove those currently between
|
|
begin/end markers for sectionName (if any), and write the lines back
|
|
to the file with the new text in sectionText.
|
|
"""
|
|
sectionBeginLine = -1
|
|
sectionEndLine = -1
|
|
sectionBeginMarker = '#-- begin-' + sectionName
|
|
sectionEndMarker = '#-- end-' + sectionName
|
|
|
|
lines = file(destFile, 'rt').readlines()
|
|
for idx, line in enumerate(lines):
|
|
if line.startswith(sectionBeginMarker):
|
|
sectionBeginLine = idx
|
|
if line.startswith(sectionEndMarker):
|
|
sectionEndLine = idx
|
|
|
|
if sectionBeginLine == -1:
|
|
# not there already, add to the end
|
|
lines.append(sectionBeginMarker + '\n')
|
|
lines.append(sectionText)
|
|
lines.append(sectionEndMarker + '\n')
|
|
else:
|
|
# replace the existing lines
|
|
lines[sectionBeginLine+1:sectionEndLine] = [sectionText]
|
|
|
|
f = file(destFile, 'wt')
|
|
f.writelines(lines)
|
|
#f.write('\n')
|
|
f.close()
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateModule(self, module, stream):
|
|
"""
|
|
Generate code for each of the top-level items in the module.
|
|
"""
|
|
assert isinstance(module, extractors.ModuleDef)
|
|
|
|
methodMap = {
|
|
extractors.ClassDef : self.generateClass,
|
|
extractors.DefineDef : self.generateDefine,
|
|
extractors.FunctionDef : self.generateFunction,
|
|
extractors.EnumDef : self.generateEnum,
|
|
extractors.GlobalVarDef : self.generateGlobalVar,
|
|
extractors.TypedefDef : self.generateTypedef,
|
|
extractors.WigCode : self.generateWigCode,
|
|
extractors.PyCodeDef : self.generatePyCode,
|
|
extractors.PyFunctionDef : self.generatePyFunction,
|
|
extractors.PyClassDef : self.generatePyClass,
|
|
extractors.CppMethodDef : self.generateCppMethod,
|
|
extractors.CppMethodDef_sip : self.generateCppMethod_sip,
|
|
}
|
|
|
|
for item in module:
|
|
if item.ignored:
|
|
continue
|
|
function = methodMap[item.__class__]
|
|
function(item, stream)
|
|
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateEnum(self, enum, stream, indent=''):
|
|
assert isinstance(enum, extractors.EnumDef)
|
|
if enum.ignored:
|
|
return
|
|
for v in enum.items:
|
|
if v.ignored:
|
|
continue
|
|
name = v.pyName or v.name
|
|
stream.write('%s%s = 0\n' % (indent, name))
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateGlobalVar(self, globalVar, stream):
|
|
assert isinstance(globalVar, extractors.GlobalVarDef)
|
|
if globalVar.ignored:
|
|
return
|
|
name = globalVar.pyName or globalVar.name
|
|
if guessTypeInt(globalVar):
|
|
valTyp = '0'
|
|
elif guessTypeFloat(globalVar):
|
|
valTyp = '0.0'
|
|
elif guessTypeStr(globalVar):
|
|
valTyp = '""'
|
|
else:
|
|
valTyp = removeWxPrefix(globalVar.type) + '()'
|
|
|
|
stream.write('%s = %s\n' % (name, valTyp))
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateDefine(self, define, stream):
|
|
assert isinstance(define, extractors.DefineDef)
|
|
if define.ignored:
|
|
return
|
|
# we're assuming that all #defines that are not ignored are integer values
|
|
stream.write('%s = 0\n' % (define.pyName or define.name))
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateTypedef(self, typedef, stream):
|
|
assert isinstance(typedef, extractors.TypedefDef)
|
|
if typedef.ignored:
|
|
return
|
|
# write nothing for this one
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateWigCode(self, wig, stream, indent=''):
|
|
assert isinstance(wig, extractors.WigCode)
|
|
# write nothing for this one
|
|
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generatePyCode(self, pc, stream, indent=''):
|
|
assert isinstance(pc, extractors.PyCodeDef)
|
|
stream.write('\n')
|
|
stream.write(nci(pc.code, len(indent)))
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generatePyFunction(self, pf, stream, indent=''):
|
|
assert isinstance(pf, extractors.PyFunctionDef)
|
|
stream.write('\n')
|
|
if pf.deprecated:
|
|
stream.write('%s@wx.deprecated\n' % indent)
|
|
if pf.isStatic:
|
|
stream.write('%s@staticmethodn' % indent)
|
|
stream.write('%sdef %s%s:\n' % (indent, pf.name, pf.argsString))
|
|
indent2 = indent + ' '*4
|
|
if pf.briefDoc:
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write(nci(pf.briefDoc, len(indent2)))
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write('%spass\n' % indent2)
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generatePyClass(self, pc, stream, indent=''):
|
|
assert isinstance(pc, extractors.PyClassDef)
|
|
|
|
# write the class declaration and docstring
|
|
if pc.deprecated:
|
|
stream.write('%s@wx.deprecated\n' % indent)
|
|
stream.write('%sclass %s' % (indent, pc.name))
|
|
if pc.bases:
|
|
stream.write('(%s):\n' % ', '.join(pc.bases))
|
|
indent2 = indent + ' '*4
|
|
if pc.briefDoc:
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write(nci(pc.briefDoc, len(indent2)))
|
|
stream.write('%s"""\n' % indent2)
|
|
|
|
# these are the only kinds of items allowed to be items in a PyClass
|
|
dispatch = {
|
|
extractors.PyFunctionDef : self.generatePyFunction,
|
|
extractors.PyPropertyDef : self.generatePyProperty,
|
|
extractors.PyCodeDef : self.generatePyCode,
|
|
extractors.PyClassDef : self.generatePyClass,
|
|
}
|
|
for item in pc.items:
|
|
item.klass = pc
|
|
f = dispatch[item.__class__]
|
|
f(item, stream, indent2)
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateFunction(self, function, stream):
|
|
assert isinstance(function, extractors.FunctionDef)
|
|
stream.write('\ndef %s' % function.pyName)
|
|
if function.overloads:
|
|
stream.write('(*args, **kw)')
|
|
else:
|
|
stream.write(function.pyArgsString)
|
|
stream.write(':\n')
|
|
stream.write(' """\n')
|
|
stream.write(nci(function.pyDocstring, 4))
|
|
stream.write(' """\n')
|
|
|
|
|
|
def generateParameters(self, parameters, stream, indent):
|
|
def _lastParameter(idx):
|
|
if idx == len(parameters)-1:
|
|
return True
|
|
for i in range(idx+1, len(parameters)):
|
|
if not parameters[i].ignored:
|
|
return False
|
|
return True
|
|
|
|
for idx, param in enumerate(parameters):
|
|
if param.ignored:
|
|
continue
|
|
stream.write(param.name)
|
|
if param.default:
|
|
stream.write('=%s' % param.default)
|
|
if not _lastParameter(idx):
|
|
stream.write(', ')
|
|
|
|
|
|
#-----------------------------------------------------------------------
|
|
def generateClass(self, klass, stream, indent=''):
|
|
assert isinstance(klass, extractors.ClassDef)
|
|
if klass.ignored:
|
|
return
|
|
|
|
# class declaration
|
|
klassName = klass.pyName or klass.name
|
|
stream.write('\n%sclass %s' % (indent, klassName))
|
|
if klass.bases:
|
|
stream.write('(')
|
|
bases = [removeWxPrefix(b) for b in klass.bases]
|
|
stream.write(', '.join(bases))
|
|
stream.write(')')
|
|
stream.write(':\n')
|
|
indent2 = indent + ' '*4
|
|
|
|
# docstring
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write(nci(klass.pyDocstring, len(indent2)))
|
|
stream.write('%s"""\n' % indent2)
|
|
|
|
# generate nested classes
|
|
for item in klass.innerclasses:
|
|
self.generateClass(item, stream, indent2)
|
|
|
|
# Split the items into public and protected groups
|
|
ctors = [i for i in klass if
|
|
isinstance(i, extractors.MethodDef) and
|
|
i.protection == 'public' and (i.isCtor or i.isDtor)]
|
|
public = [i for i in klass if i.protection == 'public' and i not in ctors]
|
|
protected = [i for i in klass if i.protection == 'protected']
|
|
|
|
dispatch = {
|
|
extractors.MemberVarDef : self.generateMemberVar,
|
|
extractors.PropertyDef : self.generateProperty,
|
|
extractors.PyPropertyDef : self.generatePyProperty,
|
|
extractors.MethodDef : self.generateMethod,
|
|
extractors.EnumDef : self.generateEnum,
|
|
extractors.CppMethodDef : self.generateCppMethod,
|
|
extractors.CppMethodDef_sip : self.generateCppMethod_sip,
|
|
extractors.PyMethodDef : self.generatePyMethod,
|
|
extractors.PyCodeDef : self.generatePyCode,
|
|
extractors.WigCode : self.generateWigCode,
|
|
}
|
|
|
|
for item in ctors:
|
|
if item.isCtor:
|
|
self.generateMethod(item, stream, indent2,
|
|
name='__init__', docstring=klass.pyDocstring)
|
|
|
|
for item in public:
|
|
f = dispatch[item.__class__]
|
|
f(item, stream, indent2)
|
|
|
|
for item in protected:
|
|
f = dispatch[item.__class__]
|
|
f(item, stream, indent2)
|
|
|
|
stream.write('%s# end of class %s\n\n' % (indent, klassName))
|
|
|
|
|
|
def generateMemberVar(self, memberVar, stream, indent):
|
|
assert isinstance(memberVar, extractors.MemberVarDef)
|
|
if memberVar.ignored:
|
|
return
|
|
stream.write('%s%s = property(None, None)\n' % (indent, memberVar.name))
|
|
|
|
|
|
def generateProperty(self, prop, stream, indent):
|
|
assert isinstance(prop, extractors.PropertyDef)
|
|
if prop.ignored:
|
|
return
|
|
stream.write('%s%s = property(None, None)\n' % (indent, prop.name))
|
|
|
|
|
|
def generatePyProperty(self, prop, stream, indent):
|
|
assert isinstance(prop, extractors.PyPropertyDef)
|
|
if prop.ignored:
|
|
return
|
|
stream.write('%s%s = property(None, None)\n' % (indent, prop.name))
|
|
|
|
|
|
def generateMethod(self, method, stream, indent, name=None, docstring=None):
|
|
assert isinstance(method, extractors.MethodDef)
|
|
if method.ignored:
|
|
return
|
|
|
|
name = name or method.pyName or method.name
|
|
if name in magicMethods:
|
|
name = magicMethods[name]
|
|
|
|
# write the method declaration
|
|
if method.isStatic:
|
|
stream.write('\n%s@staticmethod' % indent)
|
|
stream.write('\n%sdef %s' % (indent, name))
|
|
if method.overloads:
|
|
if not method.isStatic:
|
|
stream.write('(self, *args, **kw)')
|
|
else:
|
|
stream.write('(*args, **kw)')
|
|
else:
|
|
argsString = method.pyArgsString
|
|
if not argsString:
|
|
argsString = '()'
|
|
if not method.isStatic:
|
|
if argsString[:2] == '()':
|
|
argsString = '(self)' + argsString[2:]
|
|
else:
|
|
argsString = '(self, ' + argsString[1:]
|
|
if '->' in argsString:
|
|
pos = argsString.rfind(')')
|
|
argsString = argsString[:pos+1]
|
|
stream.write(argsString)
|
|
stream.write(':\n')
|
|
indent2 = indent + ' '*4
|
|
|
|
# docstring
|
|
if not docstring:
|
|
if hasattr(method, 'pyDocstring'):
|
|
docstring = method.pyDocstring
|
|
else:
|
|
docstring = ""
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write(nci(docstring, len(indent2)))
|
|
stream.write('%s"""\n' % indent2)
|
|
|
|
|
|
|
|
def generateCppMethod(self, method, stream, indent=''):
|
|
assert isinstance(method, extractors.CppMethodDef)
|
|
self.generateMethod(method, stream, indent)
|
|
|
|
|
|
def generateCppMethod_sip(self, method, stream, indent=''):
|
|
assert isinstance(method, extractors.CppMethodDef_sip)
|
|
self.generateMethod(method, stream, indent)
|
|
|
|
|
|
def generatePyMethod(self, pm, stream, indent):
|
|
assert isinstance(pm, extractors.PyMethodDef)
|
|
if pm.ignored:
|
|
return
|
|
stream.write('\n%sdef %s' % (indent, pm.name))
|
|
stream.write(pm.argsString)
|
|
stream.write(':\n')
|
|
indent2 = indent + ' '*4
|
|
|
|
stream.write('%s"""\n' % indent2)
|
|
stream.write(nci(pm.pyDocstring, len(indent2)))
|
|
stream.write('%s"""\n' % indent2)
|
|
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# helpers
|
|
|
|
def guessTypeInt(v):
|
|
if isinstance(v, extractors.EnumValueDef):
|
|
return True
|
|
if isinstance(v, extractors.DefineDef) and '"' not in v.value:
|
|
return True
|
|
type = v.type.replace('const', '')
|
|
type = type.replace(' ', '')
|
|
if type in ['int', 'long', 'byte', 'size_t']:
|
|
return True
|
|
if 'unsigned' in type:
|
|
return True
|
|
return False
|
|
|
|
|
|
def guessTypeFloat(v):
|
|
type = v.type.replace('const', '')
|
|
type = type.replace(' ', '')
|
|
if type in ['float', 'double', 'wxDouble']:
|
|
return True
|
|
return False
|
|
|
|
def guessTypeStr(v):
|
|
if hasattr(v, 'value') and '"' in v.value:
|
|
return True
|
|
if 'wxString' in v.type:
|
|
return True
|
|
return False
|
|
|
|
#---------------------------------------------------------------------------
|