#!/usr/local/bin/python -O """ Automatically create a Python wrapper for the classes, functions and symbols defined in a SWIG generated module. WARNING: This script uses intimate knowledge of the SWIG naming scheme. Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com Copyright (c) 2000-2004, eGenix.com Software GmbH; mailto:info@egenix.com """ import string,types,time from CommandLine import Application,ArgumentOption,SwitchOption __version__ = '0.1' verbose = 0 ### SWIG naming scheme def constructor(classname): return 'new_%s' % classname def is_constructor(name): return name[:4] == 'new_' def destructor(classname): return 'delete_%s' % classname def is_destructor(name): return name[:4] == 'delete_' def get_classname(name): if name[:4] == 'new_': return name[4:] elif name[:7] == 'delete_': return name[7:] else: i = string.find(name,'_') if i >= 0: return name[:i] else: return '' def get_methodname(classname,name): if not classname: return name if name == 'new_%s' % classname: return 'newSWIGObject' elif name == 'delete_%s' % classname: return 'delSWIGObject' else: i = string.find(name,'_') if i >= 0 and name[:i] == classname: return name[i+1:] else: return '' def get_constname(classname,name): if not classname: return name i = string.find(name,'_') if i > 0 and name[:i] == classname: cname = name[i+1:] if cname[:3] in ('get','set'): # Attribute access methods return '' return cname else: return '' def get_attrname(classname,name): i = string.find(name,'_') if i >= 0 and name[:i] == classname: cname = name[i+1:] if cname[:3] not in ('get','set'): return '' return cname[3:] else: return '' def is_readable_attribute(classname,name): return (name[:len(classname)+4] == '%s_get' % classname and name[len(classname)+4] != '_') def is_writeable_attribute(classname,name): return (name[:len(classname)+4] == '%s_set' % classname and name[len(classname)+4] != '_') def is_function(module,name): return type(module.__dict__[name]) in (types.BuiltinFunctionType, types.BuiltinMethodType, types.FunctionType, types.MethodType) ### Introspection APIs def find_classes(module): classes = [] for k,v in module.__dict__.items(): if is_constructor(k): cname = get_classname(k) if cname: classes.append(cname) return classes def find_methods(module,classname=''): methods = {} for k,v in module.__dict__.items(): if not is_function(module,k): continue if classname and get_classname(k) != classname: continue mname = get_methodname(classname,k) if mname: methods[mname] = (k,v) return methods # Alias find_functions = find_methods def find_constants(module,classname=''): constants = {} for k,v in module.__dict__.items(): if not is_function(module,k): if classname and get_classname(k) != classname: continue cname = get_constname(classname,k) if cname: constants[cname] = (k,v) return constants def find_readable_attributes(module,classname): attrs = {} for k,v in module.__dict__.items(): if get_classname(k) == classname: if is_readable_attribute(classname,k): cname = get_attrname(classname,k) if cname: attrs[cname] = (k,v) return attrs def find_writeable_attributes(module,classname): attrs = {} for k,v in module.__dict__.items(): if get_classname(k) == classname: if is_writeable_attribute(classname,k): cname = get_attrname(classname,k) if cname: attrs[cname] = (k,v) return attrs ### Generators modulestub = """\ ######################################################################## # # Generated Python wrapper module for %(modulename)s # # Version: %(date)s # # Import the C module import %(modulename)s # Helpers def _NOP(*args): pass def _NotSupported(*args): raise TypeError,'method not supported' # XXX Needed by make_class() #from new import instancemethod ### Base class for SWIG classes class SWIGClass: # Underlying C object SWIGObject = None # No operation constructor/destructor C_newSWIGObject = _NOP C_delSWIGObject = _NOP def __init__(self,*args,**kws): self.SWIGObject = apply(self.C_newSWIGObject,args,kws) def __del__(self): if self.SWIGObject: self.C_delSWIGObject(self.SWIGObject) ### Constants %(constants)s ### Functions %(functions)s ### Classes %(classes)s """ classdef = """\ class %(name)s%(bases)s: %(classsymbols)s %(cmethods)s """ def make_class(module,classname, bases=('SWIGClass',), want_writeable_attributes=0): methods = find_methods(module,classname) constants = find_constants(module,classname) readable_attributes = find_readable_attributes(module,classname) if want_writeable_attributes: writeable_attributes = find_writeable_attributes(module,classname) modulename = module.__name__ symbols = [] cmethods = [] if constants: symbols.append('# Constants') for name,(cname,constant) in constants.items(): symbols.append('%s = %s.%s' % (name,modulename,cname)) symbols.append('') if methods: cmethods.append('# Bind C Methods') for name,(mname,method) in methods.items(): cmethods.append('%s.C_%s = %s.%s' % (classname,name,modulename,mname)) # XXX This only works if the methods know how to handle the # instance first argument: # cmethods.append('%s.C_%s = instancemethod(%s.%s,None,%s)' % # (classname,name,modulename,mname,classname)) cmethods.append('') symbols.append('# Method wrappers') for name,(mname,method) in methods.items(): if name in ('newSWIGObject','delSWIGObject'): continue if name[:3] in ('get','set') and name[3] != '_': continue symbols.append( 'def %s(self,*args,**kws):' % (name)) symbols.append( ' return apply(self.C_%s,(self.SWIGObject,)+args,kws)' % (name)) symbols.append('') if readable_attributes: symbols.append('# Readable attributes') symbols.append('def __getattr__(self,name):') for name,(mname,attribute) in readable_attributes.items(): symbols.append( ' if name == %s: return self.C_get%s(self.SWIGObject)' % (repr(name),name)) symbols.append(' raise AttributeError,name') symbols.append('') if want_writeable_attributes and writeable_attributes: symbols.append('# Writeable attributes') symbols.append('def __setattr__(self,name,value):') for name,(mname,attribute) in writeable_attributes.items(): symbols.append( ' if name == %s: self.C_set%s(self.SWIGObject,value)' % (repr(name),name)) symbols.append(' self.__dict__[name] = value') if bases: bases = '(%s)' % string.join(bases,',') else: bases = '' return classdef % {'name':classname, 'bases':bases, 'classsymbols':string.join(symbols,'\n '), 'cmethods':string.join(cmethods,'\n'), } def make_module(module,classbases=('SWIGClass',),want_writeable_attributes=0): modulename = module.__name__ constants = find_constants(module) functions = find_functions(module) classes = find_classes(module) classes.sort() classdefs = [] for classname in classes: classdefs.append(make_class(module,classname,classbases, want_writeable_attributes)) constdefs = [] for name,(cname,constant) in constants.items(): if get_classname(cname) in classes: # Skip class constants continue constdefs.append('%s = %s.%s' % (name,modulename,cname)) constdefs.sort() funcdefs = [] for name,(cname,functions) in functions.items(): if get_classname(cname) in classes or cname == 'initModule': # Skip class methods and module init function continue funcdefs.append('%s = %s.%s' % (name,modulename,cname)) funcdefs.sort() return modulestub % {'modulename':module.__name__, 'classes':string.join(classdefs,'\n\n'), 'constants':string.join(constdefs,'\n'), 'functions':string.join(funcdefs,'\n'), 'date':time.ctime(time.time()), } ### class PythonWrapper(Application): header = 'Python Wrapper Generator for SWIG' synopsis = '%s [options] swig_module' version = __version__ options = [ArgumentOption('-o','output file','wrapper.py'), ArgumentOption('-b','base classes','SWIGClass'), SwitchOption('-w','writeable attributes','readonly'), SwitchOption('-v','verbose'), ] about = """ Pass None as argument to -b if you don't want to use any base class at all for the generated classes. """ bases = ('SWIGClass',) want_writeable_attributes = 0 def handle_v(self,arg): global verbose verbose = 1 def handle_b(self,arg): if arg == 'None': self.bases = () else: self.bases = string.split(arg,',') def handle_w(self,arg): self.want_writeable_attributes = 1 def make_module(self,module_name): import sys if sys.path[0] != '': # Add current dir to the search path sys.path[:0] = [''] try: module = __import__(module_name) except ImportError,why: self.help('Error importing module %s: %s' % (module_name,why)) return code = make_module(module,self.bases,self.want_writeable_attributes) try: f = open(self.values['-o'],'w') except IOError,why: self.help('Error opening output file: %s' % why) return f.write(code) f.close() return def main(self): # Check if len(self.files) < 1: self.help('Missing arguments.') return # Make module self.make_module(self.files[0]) if __name__ == '__main__': PythonWrapper()