[ctypes-commit] ctypes/codegen/ctypes_codegen xml2py_main.py, NONE, 1.1 typedesc.py, NONE, 1.1 h2xm
Brought to you by:
theller
From: Thomas H. <th...@us...> - 2006-06-08 18:46:56
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv767 Added Files: xml2py_main.py typedesc.py h2xml_main.py gccxmlparser.py dynmod.py cparser_config.py cparser.py codegenerator.py __init__.py .cvsignore Log Message: Initial import of the current ctypes codegenerator. --- NEW FILE: .cvsignore --- *.pyc *.pyo --- NEW FILE: xml2py_main.py --- import sys, re from optparse import OptionParser from ctypes_codegen.codegenerator import generate_code from ctypes_codegen import typedesc ################################################################ windows_dll_names = """\ imagehlp user32 kernel32 gdi32 advapi32 oleaut32 ole32 imm32 comdlg32 shell32 version winmm mpr winscard winspool.drv urlmon crypt32 cryptnet ws2_32 opengl32 glu32 mswsock msvcrt msimg32 netapi32 rpcrt4""".split() ##rpcndr ##ntdll def main(argv=None): if argv is None: argv = sys.argv def windows_dlls(option, opt, value, parser): parser.values.dlls.extend(windows_dll_names) parser = OptionParser("usage: %prog xmlfile [options]") parser.add_option("-d", action="store_true", dest="use_decorators", help="use Python 2.4 function decorators", default=False) parser.add_option("-k", action="store", dest="kind", help="kind of type descriptions to include: " "d = #defines, " "e = enumerations, " "f = functions, " "s = structures, " "t = typedefs", metavar="TYPEKIND", default=None) parser.add_option("-l", dest="dlls", help="libraries to search for exported functions", action="append", default=[]) parser.add_option("-o", dest="output", help="output filename (if not specified, standard output will be used)", default="-") parser.add_option("-r", dest="expressions", metavar="EXPRESSION", action="append", help="regular expression for symbols to include " "(if neither symbols nor expressions are specified," "everything will be included)", default=None) parser.add_option("-s", dest="symbols", metavar="SYMBOL", action="append", help="symbol to include " "(if neither symbols nor expressions are specified," "everything will be included)", default=None) parser.add_option("-v", action="store_true", dest="verbose", help="verbose output", default=False) parser.add_option("-w", action="callback", callback=windows_dlls, help="add all standard windows dlls to the searched dlls list") ## try: ## import comtypes ## except ImportError: ## default_modules = ["ctypes", "ctypes.com"] ## else: ## default_modules = ["ctypes", "comtypes"] default_modules = ["ctypes.wintypes", "ctypes", ## "comtypes" ] parser.add_option("-m", dest="modules", metavar="module", help="Python module(s) containing symbols which will " "be imported instead of generated", action="append", default=default_modules) options, files = parser.parse_args(argv[1:]) if len(files) != 1: parser.error("Exactly one input file must be specified") if options.output == "-": stream = sys.stdout else: stream = open(options.output, "w") if options.expressions: options.expressions = map(re.compile, options.expressions) stream.write("# generated by 'xml2py'\n") stream.write("# flags '%s'\n" % " ".join(argv[1:])) known_symbols = {} from ctypes import CDLL dlls = [CDLL(name) for name in options.dlls] for name in options.modules: mod = __import__(name) for submodule in name.split(".")[1:]: mod = getattr(mod, submodule) for name in mod.__dict__: known_symbols[name] = mod.__name__ if options.kind: types = [] for char in options.kind: typ = {"a": [typedesc.Alias], "d": [typedesc.Variable], "e": [typedesc.Enumeration, typedesc.EnumValue], "f": [typedesc.Function], "m": [typedesc.Macro], "s": [typedesc.Structure], "t": [typedesc.Typedef], }[char] types.extend(typ) options.kind = tuple(types) generate_code(files[0], stream, symbols=options.symbols, expressions=options.expressions, verbose=options.verbose, use_decorators=options.use_decorators, known_symbols=known_symbols, searched_dlls=dlls, types=options.kind) if __name__ == "__main__": sys.exit(main()) --- NEW FILE: codegenerator.py --- # Create ctypes wrapper code for abstract type descriptions. # Type descriptions are collections of typedesc instances. import typedesc, sys, types ASSUME_STRINGS = True try: set except NameError: from sets import Set as set try: sorted except NameError: def sorted(seq, cmp): seq = list(seq) seq.sort(cmp) return seq try: import cStringIO as StringIO except ImportError: import StringIO # XXX Should this be in ctypes itself? ctypes_names = { "unsigned char": "c_ubyte", "signed char": "c_byte", "char": "c_char", "wchar_t": "c_wchar", "short unsigned int": "c_ushort", "short int": "c_short", "long unsigned int": "c_ulong", "long int": "c_long", "long signed int": "c_long", "unsigned int": "c_uint", "int": "c_int", "long long unsigned int": "c_ulonglong", "long long int": "c_longlong", "double": "c_double", "float": "c_float", # Hm... "void": "None", } ################ def storage(t): # return the size and alignment of a type if isinstance(t, typedesc.Typedef): return storage(t.typ) elif isinstance(t, typedesc.ArrayType): s, a = storage(t.typ) return s * (int(t.max) - int(t.min) + 1), a return int(t.size), int(t.align) class PackingError(Exception): pass def _calc_packing(struct, fields, pack, isStruct): # Try a certain packing, raise PackingError if field offsets, # total size ot total alignment is wrong. if struct.size is None: # incomplete struct return -1 if struct.name in dont_assert_size: return None if struct.bases: size = struct.bases[0].size total_align = struct.bases[0].align else: size = 0 total_align = 8 # in bits for i, f in enumerate(fields): if f.bits: # this code cannot handle bit field sizes. ## print "##XXX FIXME" return -2 # XXX FIXME s, a = storage(f.typ) if pack is not None: a = min(pack, a) if size % a: size += a - size % a if isStruct: if size != f.offset: raise PackingError, "field %s offset (%s/%s)" % (f.name, size, f.offset) size += s else: size = max(size, s) total_align = max(total_align, a) if total_align != struct.align: raise PackingError, "total alignment (%s/%s)" % (total_align, struct.align) a = total_align if pack is not None: a = min(pack, a) if size % a: size += a - size % a if size != struct.size: raise PackingError, "total size (%s/%s)" % (size, struct.size) def calc_packing(struct, fields): # try several packings, starting with unspecified packing isStruct = isinstance(struct, typedesc.Structure) for pack in [None, 16*8, 8*8, 4*8, 2*8, 1*8]: try: _calc_packing(struct, fields, pack, isStruct) except PackingError, details: continue else: if pack is None: return None return pack/8 raise PackingError, "PACKING FAILED: %s" % details def decode_value(init): # decode init value from gccxml if init[0] == "0": return int(init, 16) # hex integer elif init[0] == "'": return eval(init) # character elif init[0] == '"': return eval(init) # string return int(init) # integer def get_real_type(tp): if type(tp) is typedesc.Typedef: return get_real_type(tp.typ) elif isinstance(tp, typedesc.CvQualifiedType): return get_real_type(tp.typ) return tp # XXX These should be filtered out in gccxmlparser. dont_assert_size = set( [ "__si_class_type_info_pseudo", "__class_type_info_pseudo", ] ) ################################################################ class Generator(object): def __init__(self, output, use_decorators=False, known_symbols=None, searched_dlls=None): self.output = output self.stream = StringIO.StringIO() self.imports = StringIO.StringIO() ## self.stream = self.imports = self.output self.use_decorators = use_decorators self.known_symbols = known_symbols or {} self.searched_dlls = searched_dlls or [] self.done = set() # type descriptions that have been generated self.names = set() # names that have been generated def init_value(self, t, init): tn = self.type_name(t, False) if tn in ["c_ulonglong", "c_ulong", "c_uint", "c_ushort", "c_ubyte"]: return decode_value(init) elif tn in ["c_longlong", "c_long", "c_int", "c_short", "c_byte"]: return decode_value(init) elif tn in ["c_float", "c_double"]: return float(init) elif tn == "POINTER(c_char)": if init[0] == '"': value = eval(init) else: value = int(init, 16) return value elif tn == "POINTER(c_wchar)": if init[0] == '"': value = eval(init) else: value = int(init, 16) if isinstance(value, str): value = value[:-1] # gccxml outputs "D\000S\000\000" for L"DS" value = value.decode("utf-16") # XXX Is this correct? return value elif tn == "c_void_p": if init[0] == "0": value = int(init, 16) else: value = int(init) # hm.. # Hm, ctypes represents them as SIGNED int return value elif tn == "c_char": return decode_value(init) elif tn == "c_wchar": value = decode_value(init) if isinstance(value, int): return unichr(value) return value elif tn.startswith("POINTER("): # Hm, POINTER(HBITMAP__) for example return decode_value(init) else: raise ValueError, "cannot decode %s(%r)" % (tn, init) def type_name(self, t, generate=True): # Return a string containing an expression that can be used to # refer to the type. Assumes the 'from ctypes import *' # namespace is available. if isinstance(t, typedesc.Typedef): return t.name if isinstance(t, typedesc.PointerType): if ASSUME_STRINGS: x = get_real_type(t.typ) if isinstance(x, typedesc.FundamentalType): if x.name == "char": self.need_STRING() return "STRING" elif x.name == "wchar_t": self.need_WSTRING() return "WSTRING" result = "POINTER(%s)" % self.type_name(t.typ, generate) # XXX Better to inspect t.typ! if result.startswith("POINTER(WINFUNCTYPE"): return result[len("POINTER("):-1] if result.startswith("POINTER(CFUNCTYPE"): return result[len("POINTER("):-1] elif result == "POINTER(None)": return "c_void_p" return result elif isinstance(t, typedesc.ArrayType): return "%s * %s" % (self.type_name(t.typ, generate), int(t.max)+1) elif isinstance(t, typedesc.FunctionType): args = [self.type_name(x, generate) for x in [t.returns] + list(t.iterArgTypes())] if "__stdcall__" in t.attributes: return "WINFUNCTYPE(%s)" % ", ".join(args) else: return "CFUNCTYPE(%s)" % ", ".join(args) elif isinstance(t, typedesc.CvQualifiedType): # const and volatile are ignored return "%s" % self.type_name(t.typ, generate) elif isinstance(t, typedesc.FundamentalType): return ctypes_names[t.name] elif isinstance(t, typedesc.Structure): return t.name elif isinstance(t, typedesc.Enumeration): if t.name: return t.name return "c_int" # enums are integers elif isinstance(t, typedesc.Typedef): return t.name return t.name ################################################################ def Alias(self, alias): if alias.typ is not None: # we can resolve it self.generate(alias.typ) if alias.alias in self.names: print >> self.stream, "%s = %s # alias" % (alias.name, alias.alias) self.names.add(alias.name) return # we cannot resolve it print >> self.stream, "# %s = %s # alias" % (alias.name, alias.alias) print "# unresolved alias: %s = %s" % (alias.name, alias.alias) def Macro(self, macro): # We don't know if we can generate valid, error free Python # code All we can do is to try to compile the code. If the # compile fails, we know it cannot work, so we generate # commented out code. If it succeeds, it may fail at runtime. code = "def %s%s: return %s # macro" % (macro.name, macro.args, macro.body) try: compile(code, "<string>", "exec") except SyntaxError: print >> self.stream, "#", code else: print >> self.stream, code self.names.add(macro.name) def StructureHead(self, head): for struct in head.struct.bases: self.generate(struct.get_head()) self.more.add(struct) if head.struct.location: print >> self.stream, "# %s %s" % head.struct.location basenames = [self.type_name(b) for b in head.struct.bases] if basenames: self.need_GUID() method_names = [m.name for m in head.struct.members if type(m) is typedesc.Method] print >> self.stream, "class %s(%s):" % (head.struct.name, ", ".join(basenames)) print >> self.stream, " _iid_ = GUID('{}') # please look up iid and fill in!" if "Enum" in method_names: print >> self.stream, " def __iter__(self):" print >> self.stream, " return self.Enum()" elif method_names == "Next Skip Reset Clone".split(): print >> self.stream, " def __iter__(self):" print >> self.stream, " return self" print >> self.stream print >> self.stream, " def next(self):" print >> self.stream, " arr, fetched = self.Next(1)" print >> self.stream, " if fetched == 0:" print >> self.stream, " raise StopIteration" print >> self.stream, " return arr[0]" else: methods = [m for m in head.struct.members if type(m) is typedesc.Method] if methods: # Hm. We cannot generate code for IUnknown... print >> self.stream, "assert 0, 'cannot generate code for IUnknown'" print >> self.stream, "class %s(_com_interface):" % head.struct.name elif type(head.struct) == typedesc.Structure: print >> self.stream, "class %s(Structure):" % head.struct.name elif type(head.struct) == typedesc.Union: print >> self.stream, "class %s(Union):" % head.struct.name print >> self.stream, " pass" self.names.add(head.struct.name) _structures = 0 def Structure(self, struct): self._structures += 1 self.generate(struct.get_head()) self.generate(struct.get_body()) Union = Structure _typedefs = 0 def Typedef(self, tp): self._typedefs += 1 if type(tp.typ) in (typedesc.Structure, typedesc.Union): self.generate(tp.typ.get_head()) self.more.add(tp.typ) else: self.generate(tp.typ) if self.type_name(tp.typ) in self.known_symbols: stream = self.imports else: stream = self.stream if tp.name != self.type_name(tp.typ): print >> stream, "%s = %s" % \ (tp.name, self.type_name(tp.typ)) self.names.add(tp.name) _arraytypes = 0 def ArrayType(self, tp): self._arraytypes += 1 self.generate(get_real_type(tp.typ)) self.generate(tp.typ) _functiontypes = 0 def FunctionType(self, tp): self._functiontypes += 1 self.generate(tp.returns) self.generate_all(tp.iterArgTypes()) _pointertypes = 0 def PointerType(self, tp): self._pointertypes += 1 if type(tp.typ) is typedesc.PointerType: self.generate(tp.typ) elif type(tp.typ) in (typedesc.Union, typedesc.Structure): self.generate(tp.typ.get_head()) self.more.add(tp.typ) elif type(tp.typ) is typedesc.Typedef: self.generate(tp.typ) else: self.generate(tp.typ) def CvQualifiedType(self, tp): self.generate(tp.typ) _variables = 0 def Variable(self, tp): self._variables += 1 if tp.init is None: # wtypes.h contains IID_IProcessInitControl, for example return try: value = self.init_value(tp.typ, tp.init) except (TypeError, ValueError), detail: print "Could not init", tp.name, tp.init, detail ## raise return print >> self.stream, \ "%s = %r # Variable %s" % (tp.name, value, self.type_name(tp.typ, False)) self.names.add(tp.name) _enumvalues = 0 def EnumValue(self, tp): value = int(tp.value) print >> self.stream, \ "%s = %d" % (tp.name, value) self.names.add(tp.name) self._enumvalues += 1 _enumtypes = 0 def Enumeration(self, tp): self._enumtypes += 1 print >> self.stream if tp.name: print >> self.stream, "# values for enumeration '%s'" % tp.name else: print >> self.stream, "# values for unnamed enumeration" # Some enumerations have the same name for the enum type # and an enum value. Excel's XlDisplayShapes is such an example. # Since we don't have separate namespaces for the type and the values, # we generate the TYPE last, overwriting the value. XXX for item in tp.values: self.generate(item) if tp.name: print >> self.stream, "%s = c_int # enum" % tp.name self.names.add(tp.name) def StructureBody(self, body): fields = [] methods = [] for m in body.struct.members: if type(m) is typedesc.Field: fields.append(m) if type(m.typ) is typedesc.Typedef: self.generate(get_real_type(m.typ)) self.generate(m.typ) elif type(m) is typedesc.Method: methods.append(m) self.generate(m.returns) self.generate_all(m.iterArgTypes()) elif type(m) is typedesc.Constructor: pass # we don't need _pack_ on Unions (I hope, at least), and not # on COM interfaces: if not methods: try: pack = calc_packing(body.struct, fields) if pack is not None: print >> self.stream, "%s._pack_ = %s" % (body.struct.name, pack) except PackingError, details: # if packing fails, write a warning comment to the output. import warnings message = "Structure %s: %s" % (body.struct.name, details) warnings.warn(message, UserWarning) print >> self.stream, "# WARNING: %s" % details if fields: if body.struct.bases: assert len(body.struct.bases) == 1 self.generate(body.struct.bases[0].get_body()) # field definition normally span several lines. # Before we generate them, we need to 'import' everything they need. # So, call type_name for each field once, for f in fields: self.type_name(f.typ) print >> self.stream, "%s._fields_ = [" % body.struct.name if body.struct.location: print >> self.stream, " # %s %s" % body.struct.location # unnamed fields will get autogenerated names "_", "_1". "_2", "_3", ... unnamed_index = 0 for f in fields: if not f.name: if unnamed_index: fieldname = "_%d" % unnamed_index else: fieldname = "_" unnamed_index += 1 print >> self.stream, " # Unnamed field renamed to '%s'" % fieldname else: fieldname = f.name if f.bits is None: print >> self.stream, " ('%s', %s)," % (fieldname, self.type_name(f.typ)) else: print >> self.stream, " ('%s', %s, %s)," % (fieldname, self.type_name(f.typ), f.bits) print >> self.stream, "]" # generate assert statements for size and alignment if body.struct.size and body.struct.name not in dont_assert_size: size = body.struct.size // 8 print >> self.stream, "assert sizeof(%s) == %s, sizeof(%s)" % \ (body.struct.name, size, body.struct.name) align = body.struct.align // 8 print >> self.stream, "assert alignment(%s) == %s, alignment(%s)" % \ (body.struct.name, align, body.struct.name) if methods: # Ha! Autodetect ctypes.com or comtypes ;) if "COMMETHOD" in self.known_symbols: self.need_COMMETHOD() else: self.need_STDMETHOD() # method definitions normally span several lines. # Before we generate them, we need to 'import' everything they need. # So, call type_name for each field once, for m in methods: self.type_name(m.returns) for a in m.iterArgTypes(): self.type_name(a) if "COMMETHOD" in self.known_symbols: print >> self.stream, "%s._methods_ = [" % body.struct.name else: # ctypes.com needs baseclass methods listed as well if body.struct.bases: basename = body.struct.bases[0].name print >> self.stream, "%s._methods_ = %s._methods + [" % \ (body.struct.name, basename) else: print >> self.stream, "%s._methods_ = [" % body.struct.name if body.struct.location: print >> self.stream, "# %s %s" % body.struct.location if "COMMETHOD" in self.known_symbols: for m in methods: if m.location: print >> self.stream, " # %s %s" % m.location print >> self.stream, " COMMETHOD([], %s, '%s'," % ( self.type_name(m.returns), m.name) for a in m.iterArgTypes(): print >> self.stream, \ " ( [], %s, )," % self.type_name(a) print >> self.stream, " )," else: for m in methods: args = [self.type_name(a) for a in m.iterArgTypes()] print >> self.stream, " STDMETHOD(%s, '%s', [%s])," % ( self.type_name(m.returns), m.name, ", ".join(args)) print >> self.stream, "]" def find_dllname(self, func): if hasattr(func, "dllname"): return func.dllname name = func.name for dll in self.searched_dlls: try: getattr(dll, name) except AttributeError: pass else: return dll._name ## if self.verbose: # warnings.warn, maybe? ## print >> sys.stderr, "function %s not found in any dll" % name return None _loadedlibs = None def get_sharedlib(self, dllname): if self._loadedlibs is None: self._loadedlibs = {} try: return self._loadedlibs[dllname] except KeyError: pass import os basename = os.path.basename(dllname) name, ext = os.path.splitext(basename) self._loadedlibs[dllname] = name # This should be handled in another way! ## print >> self.stream, "%s = CDLL(%r)" % (name, dllname) return name _STRING_defined = False def need_STRING(self): if self._STRING_defined: return print >> self.imports, "STRING = c_char_p" self._STRING_defined = True _WSTRING_defined = False def need_WSTRING(self): if self._WSTRING_defined: return print >> self.imports, "WSTRING = c_wchar_p" self._WSTRING_defined = True _STDMETHOD_defined = False def need_STDMETHOD(self): if self._STDMETHOD_defined: return print >> self.imports, "from ctypes.com import STDMETHOD" self._STDMETHOD_defined = True _COMMETHOD_defined = False def need_COMMETHOD(self): if self._COMMETHOD_defined: return print >> self.imports, "from comtypes import COMMETHOD" self._COMMETHOD_defined = True _GUID_defined = False def need_GUID(self): if self._GUID_defined: return self._GUID_defined = True modname = self.known_symbols.get("GUID") if modname: print >> self.imports, "from %s import GUID" % modname _functiontypes = 0 _notfound_functiontypes = 0 def Function(self, func): dllname = self.find_dllname(func) if dllname: self.generate(func.returns) self.generate_all(func.iterArgTypes()) args = [self.type_name(a) for a in func.iterArgTypes()] if "__stdcall__" in func.attributes: cc = "stdcall" else: cc = "cdecl" libname = self.get_sharedlib(dllname) print >> self.stream if self.use_decorators: print >> self.stream, "@ %s(%s, '%s', [%s])" % \ (cc, self.type_name(func.returns), libname, ", ".join(args)) argnames = [a or "p%d" % (i+1) for i, a in enumerate(func.iterArgNames())] # function definition print >> self.stream, "def %s(%s):" % (func.name, ", ".join(argnames)) if func.location: print >> self.stream, " # %s %s" % func.location print >> self.stream, " return %s._api_(%s)" % (func.name, ", ".join(argnames)) if not self.use_decorators: print >> self.stream, "%s = %s(%s, '%s', [%s]) (%s)" % \ (func.name, cc, self.type_name(func.returns), libname, ", ".join(args), func.name) print >> self.stream self.names.add(func.name) self._functiontypes += 1 else: self._notfound_functiontypes += 1 def FundamentalType(self, item): pass # we should check if this is known somewhere ## name = ctypes_names[item.name] ## if name != "None": ## print >> self.stream, "from ctypes import %s" % name ## self.done.add(item) ######## def generate(self, item): if item in self.done: return if isinstance(item, typedesc.StructureHead): name = getattr(item.struct, "name", None) else: name = getattr(item, "name", None) if name in self.known_symbols: mod = self.known_symbols[name] print >> self.imports, "from %s import %s" % (mod, name) self.done.add(item) if isinstance(item, typedesc.Structure): self.done.add(item.get_head()) self.done.add(item.get_body()) return mth = getattr(self, type(item).__name__) # to avoid infinite recursion, we have to mark it as done # before actually generating the code. self.done.add(item) mth(item) def generate_all(self, items): for item in items: self.generate(item) def cmpitems(a, b): a = getattr(a, "location", None) b = getattr(b, "location", None) if a is None: return -1 if b is None: return 1 return cmp(a[0],b[0]) or cmp(int(a[1]),int(b[1])) cmpitems = staticmethod(cmpitems) def generate_code(self, items): print >> self.imports, "from ctypes import *" items = set(items) loops = 0 while items: loops += 1 self.more = set() self.generate_all(sorted(items, self.cmpitems)) items |= self.more items -= self.done self.output.write(self.imports.getvalue()) self.output.write("\n\n") self.output.write(self.stream.getvalue()) import textwrap text = "__all__ = [%s]" % ", ".join([repr(str(n)) for n in self.names]) for line in textwrap.wrap(text, subsequent_indent=" "): print >> self.output, line return loops def print_stats(self, stream): total = self._structures + self._functiontypes + self._enumtypes + self._typedefs +\ self._pointertypes + self._arraytypes print >> stream, "###########################" print >> stream, "# Symbols defined:" print >> stream, "#" print >> stream, "# Variables: %5d" % self._variables print >> stream, "# Struct/Unions: %5d" % self._structures print >> stream, "# Functions: %5d" % self._functiontypes print >> stream, "# Enums: %5d" % self._enumtypes print >> stream, "# Enum values: %5d" % self._enumvalues print >> stream, "# Typedefs: %5d" % self._typedefs print >> stream, "# Pointertypes: %5d" % self._pointertypes print >> stream, "# Arraytypes: %5d" % self._arraytypes print >> stream, "# unknown functions: %5d" % self._notfound_functiontypes print >> stream, "#" print >> stream, "# Total symbols: %5d" % total print >> stream, "###########################" ################################################################ def generate_code(xmlfile, outfile, expressions=None, symbols=None, verbose=False, use_decorators=False, known_symbols=None, searched_dlls=None, types=None): # expressions is a sequence of compiled regular expressions, # symbols is a sequence of names from gccxmlparser import parse items = parse(xmlfile) # filter symbols to generate todo = [] if types: items = [i for i in items if isinstance(i, types)] if symbols: syms = set(symbols) for i in items: if i.name in syms: todo.append(i) syms.remove(i.name) if syms: print "symbols not found", list(syms) if expressions: for i in items: for s in expressions: if i.name is None: continue match = s.match(i.name) # we only want complete matches if match and match.group() == i.name: todo.append(i) break if symbols or expressions: items = todo ################ gen = Generator(outfile, use_decorators=use_decorators, known_symbols=known_symbols, searched_dlls=searched_dlls) loops = gen.generate_code(items) if verbose: gen.print_stats(sys.stderr) print >> sys.stderr, "needed %d loop(s)" % loops --- NEW FILE: cparser_config.py --- # cparser_config.py - configuration items for cparser.py # # XXX Should this be converted to the h2xml.cfg style? import re, sys, os # C keywords, according to MSDN, plus some additional # names like __forceinline, near, far. # Skip all definitions where the rhs is a keyword # Example: #define CALLBACK __stdcall # # Hm, should types be handled differently? # Example: #define VOID void C_KEYWORDS = """__asm else main struct __assume enum __multiple_inheritance switch auto __except __single_inheritance template __based explicit __virtual_inheritance this bool extern mutable thread break false naked throw case __fastcall namespace true catch __finally new try __cdecl float noreturn __try char for operator typedef class friend private typeid const goto protected typename const_cast if public union continue inline register unsigned __declspec __inline default int return uuid delete __int8 short __uuidof dllexport __int16 signed virtual dllimport __int32 sizeof void do __int64 static volatile double __leave static_cast wmain dynamic_cast long __stdcall while far near __forceinline __w64 __noop""".split() C_KEYWORDS.append("long long") # defines we know that won't work # for windows.h EXCLUDED_win32 = """ TTTOOLINFOA_V3_SIZE TTTOOLINFOW_V3_SIZE NMLVCUSTOMDRAW_V3_SIZE NOTIFYICONDATAA_V1_SIZE NOTIFYICONDATAA_V2_SIZE PROPSHEETHEADERA_V1_SIZE PROPSHEETHEADERA_V2_SIZE PROPSHEETHEADERW_V2_SIZE NOTIFYICONDATAW_V2_SIZE s_imp s_host s_lh s_net s_addr h_addr s_impno _VARIANT_BOOL MIDL_uhyper WINSCARDDATA __MIDL_DECLSPEC_DLLIMPORT __MIDL_DECLSPEC_DLLEXPORT NCB_POST STDAPI STDAPIV WINAPI SHDOCAPI WINOLEAUTAPI WINOLEAPI WINOLECTLAPI APIENTRY EXTERN_C FIRMWARE_PTR STDMETHODIMPV STDMETHODIMP DEFAULT_UNREACHABLE MAXLONGLONG IMAGE_ORDINAL_FLAG64 SECURITY_NT_AUTHORITY LLONG_MAX LLONG_MIN ULLONG_MAX """.strip().split() # The ..LONG_MIN and ..LONG_MAX symbols are in MSVC 7.1 limits.h, # gccxml complains: integer constant is too large for "long" type EXCLUDED_linux = """ _IOT_termios """.strip().split() if sys.platform == "win32": EXCLUDED = EXCLUDED_win32 elif sys.platform.startswith("linux"): EXCLUDED = EXCLUDED_linux else: EXCLUDED = [] EXCLUDED = [text for text in EXCLUDED if not text.startswith("#")] EXCLUDED_RE_win32 = r""" ^DECLSPEC\w*$ """.strip().split() EXCLUDED_RE_linux = r""" ^__\w*$ ^__attribute_\w*_$ ^_G_HAVE_ST_BLKSIZE$ """.strip().split() if sys.platform == "win32": EXCLUDED_RE = EXCLUDED_RE_win32 elif sys.platform.startswith("linux"): EXCLUDED_RE = EXCLUDED_RE_linux else: EXCLUDED_RE = [] EXCLUDED_RE = [re.compile(pat) for pat in EXCLUDED_RE if not pat.startswith("#")] --- NEW FILE: typedesc.py --- # typedesc.py - classes representing C type descriptions try: set except NameError: from sets import Set as set class Argument(object): "a Parameter in the argument list of a callable (Function, Method, ...)" def __init__(self, atype, name): self.atype = atype self.name = name class _HasArgs(object): def __init__(self): self.arguments = [] def add_argument(self, arg): assert isinstance(arg, Argument) self.arguments.append(arg) def iterArgTypes(self): for a in self.arguments: yield a.atype def iterArgNames(self): for a in self.arguments: yield a.name def fixup_argtypes(self, typemap): for a in self.arguments: a.atype = typemap[a.atype] ################ class Alias(object): # a C preprocessor alias, like #define A B def __init__(self, name, alias, typ=None): self.name = name self.alias = alias self.typ = typ class Macro(object): # a C preprocessor definition with arguments def __init__(self, name, args, body): # all arguments are strings, args is the literal argument list # *with* the parens around it: # Example: Macro("CD_INDRIVE", "(status)", "((int)status > 0)") self.name = name self.args = args self.body = body class File(object): def __init__(self, name): self.name = name class Function(_HasArgs): location = None def __init__(self, name, returns, attributes, extern): _HasArgs.__init__(self) self.name = name self.returns = returns self.attributes = attributes # dllimport, __stdcall__, __cdecl__ self.extern = extern class Constructor(_HasArgs): location = None def __init__(self, name): _HasArgs.__init__(self) self.name = name class OperatorFunction(_HasArgs): location = None def __init__(self, name, returns): _HasArgs.__init__(self) self.name = name self.returns = returns class FunctionType(_HasArgs): location = None def __init__(self, returns, attributes): _HasArgs.__init__(self) self.returns = returns self.attributes = attributes class Method(_HasArgs): location = None def __init__(self, name, returns): _HasArgs.__init__(self) self.name = name self.returns = returns class FundamentalType(object): location = None def __init__(self, name, size, align): self.name = name if name != "void": self.size = int(size) self.align = int(align) class PointerType(object): location = None def __init__(self, typ, size, align): self.typ = typ self.size = int(size) self.align = int(align) class Typedef(object): location = None def __init__(self, name, typ): self.name = name self.typ = typ class ArrayType(object): location = None def __init__(self, typ, min, max): self.typ = typ self.min = min self.max = max class StructureHead(object): location = None def __init__(self, struct): self.struct = struct class StructureBody(object): location = None def __init__(self, struct): self.struct = struct class _Struct_Union_Base(object): location = None def get_body(self): return self.struct_body def get_head(self): return self.struct_head class Structure(_Struct_Union_Base): def __init__(self, name, align, members, bases, size, artificial=None): self.name = name self.align = int(align) self.members = members self.bases = bases self.artificial = artificial if size is not None: self.size = int(size) else: self.size = None self.struct_body = StructureBody(self) self.struct_head = StructureHead(self) class Union(_Struct_Union_Base): def __init__(self, name, align, members, bases, size, artificial=None): self.name = name self.align = int(align) self.members = members self.bases = bases self.artificial = artificial if size is not None: self.size = int(size) else: self.size = None self.struct_body = StructureBody(self) self.struct_head = StructureHead(self) class Field(object): def __init__(self, name, typ, bits, offset): self.name = name self.typ = typ self.bits = bits self.offset = int(offset) class CvQualifiedType(object): def __init__(self, typ, const, volatile): self.typ = typ self.const = const self.volatile = volatile class Enumeration(object): location = None def __init__(self, name, size, align): self.name = name self.size = int(size) self.align = int(align) self.values = [] def add_value(self, v): self.values.append(v) class EnumValue(object): def __init__(self, name, value, enumeration): self.name = name self.value = value self.enumeration = enumeration class Variable(object): location = None def __init__(self, name, typ, init=None): self.name = name self.typ = typ self.init = init ################################################################ --- NEW FILE: __init__.py --- # ctypes.wrap package - tools for code generation --- NEW FILE: gccxmlparser.py --- """gccxmlparser - parse a gccxml created XML file into a sequence type descriptions""" import xml.sax import typedesc import sys try: set except NameError: from sets import Set as set import re ################################################################ def MAKE_NAME(name): name = name.replace("$", "DOLLAR") name = name.replace(".", "DOT") if name.startswith("__"): return "_X" + name elif name[0] in "01234567879": return "_" + name return name WORDPAT = re.compile("^[a-zA-Z_][a-zA-Z0-9_]*$") def CHECK_NAME(name): if WORDPAT.match(name): return name return None class GCCXML_Handler(xml.sax.handler.ContentHandler): has_values = set(["Enumeration", "Function", "FunctionType", "OperatorFunction", "Method", "Constructor", "Destructor", "OperatorMethod"]) def __init__(self, *args): xml.sax.handler.ContentHandler.__init__(self, *args) self.context = [] self.all = {} self.cpp_data = {} def startElement(self, name, attrs): # find and call the handler for this element mth = getattr(self, name) result = mth(attrs) if result is not None: location = attrs.get("location", None) if location is not None: result.location = location # record the result _id = attrs.get("id", None) # The '_id' attribute is used to link together all the # nodes, in the _fixup_ methods. if _id is not None: self.all[_id] = result else: # EnumValue, for example, has no "_id" attribute. # Invent our own... self.all[id(result)] = result # if this element has children, push onto the context if name in self.has_values: self.context.append(result) cdata = None def endElement(self, name): # if this element has children, pop the context if name in self.has_values: self.context.pop() self.cdata = None ################################ # do-nothing element handlers def Class(self, attrs): pass def Destructor(self, attrs): pass def GCC_XML(self, attrs): pass def Namespace(self, attrs): pass def Base(self, attrs): pass def Ellipsis(self, attrs): pass def OperatorMethod(self, attrs): pass ################################ # real element handlers def CPP_DUMP(self, attrs): name = attrs["name"] # Insert a new list for each named section into self.cpp_data, # and point self.cdata to it. self.cdata will be set to None # again at the end of each section. self.cpp_data[name] = self.cdata = [] def characters(self, content): if self.cdata is not None: self.cdata.append(content) def File(self, attrs): name = attrs["name"] if sys.platform == "win32" and " " in name: # On windows, convert to short filename if it contains blanks from ctypes import windll, create_unicode_buffer, sizeof, WinError buf = create_unicode_buffer(512) if windll.kernel32.GetShortPathNameW(name, buf, sizeof(buf)): name = buf.value return typedesc.File(name) def _fixup_File(self, f): pass # simple types and modifiers def Variable(self, attrs): name = attrs["name"] if name.startswith("cpp_sym_"): # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx fix me! name = name[len("cpp_sym_"):] init = attrs.get("init", None) typ = attrs["type"] return typedesc.Variable(name, typ, init) def _fixup_Variable(self, t): t.typ = self.all[t.typ] def Typedef(self, attrs): name = attrs["name"] typ = attrs["type"] return typedesc.Typedef(name, typ) def _fixup_Typedef(self, t): t.typ = self.all[t.typ] def FundamentalType(self, attrs): name = attrs["name"] if name == "void": size = "" else: size = attrs["size"] align = attrs["align"] return typedesc.FundamentalType(name, size, align) def _fixup_FundamentalType(self, t): pass def PointerType(self, attrs): typ = attrs["type"] size = attrs["size"] align = attrs["align"] return typedesc.PointerType(typ, size, align) def _fixup_PointerType(self, p): p.typ = self.all[p.typ] ReferenceType = PointerType _fixup_ReferenceType = _fixup_PointerType def ArrayType(self, attrs): # type, min?, max? typ = attrs["type"] min = attrs["min"] max = attrs["max"] if max == "ffffffffffffffff": max = "-1" return typedesc.ArrayType(typ, min, max) def _fixup_ArrayType(self, a): a.typ = self.all[a.typ] def CvQualifiedType(self, attrs): # id, type, [const|volatile] typ = attrs["type"] const = attrs.get("const", None) volatile = attrs.get("volatile", None) return typedesc.CvQualifiedType(typ, const, volatile) def _fixup_CvQualifiedType(self, c): c.typ = self.all[c.typ] # callables def Function(self, attrs): # name, returns, extern, attributes name = attrs["name"] returns = attrs["returns"] attributes = attrs.get("attributes", "").split() extern = attrs.get("extern") return typedesc.Function(name, returns, attributes, extern) def _fixup_Function(self, func): func.returns = self.all[func.returns] func.fixup_argtypes(self.all) def FunctionType(self, attrs): # id, returns, attributes returns = attrs["returns"] attributes = attrs.get("attributes", "").split() return typedesc.FunctionType(returns, attributes) def _fixup_FunctionType(self, func): func.returns = self.all[func.returns] func.fixup_argtypes(self.all) def OperatorFunction(self, attrs): # name, returns, extern, attributes name = attrs["name"] returns = attrs["returns"] return typedesc.OperatorFunction(name, returns) def _fixup_OperatorFunction(self, func): func.returns = self.all[func.returns] def Constructor(self, attrs): name = attrs["name"] return typedesc.Constructor(name) def _fixup_Constructor(self, const): pass def Method(self, attrs): # name, virtual, pure_virtual, returns name = attrs["name"] returns = attrs["returns"] return typedesc.Method(name, returns) def _fixup_Method(self, m): m.returns = self.all[m.returns] m.fixup_argtypes(self.all) def Argument(self, attrs): parent = self.context[-1] if parent is not None: parent.add_argument(typedesc.Argument(attrs["type"], attrs.get("name"))) # enumerations def Enumeration(self, attrs): # id, name name = attrs["name"] # If the name isn't a valid Python identifier, create an unnamed enum name = CHECK_NAME(name) size = attrs["size"] align = attrs["align"] return typedesc.Enumeration(name, size, align) def _fixup_Enumeration(self, e): pass def EnumValue(self, attrs): name = attrs["name"] value = attrs["init"] v = typedesc.EnumValue(name, value, self.context[-1]) self.context[-1].add_value(v) return v def _fixup_EnumValue(self, e): pass # structures, unions def Struct(self, attrs): # id, name, members name = attrs.get("name") if name is None: name = MAKE_NAME(attrs["mangled"]) bases = attrs.get("bases", "").split() members = attrs.get("members", "").split() align = attrs["align"] size = attrs.get("size") return typedesc.Structure(name, align, members, bases, size) def _fixup_Structure(self, s): s.members = [self.all[m] for m in s.members] s.bases = [self.all[b] for b in s.bases] _fixup_Union = _fixup_Structure def Union(self, attrs): name = attrs.get("name") if name is None: name = MAKE_NAME(attrs["mangled"]) bases = attrs.get("bases", "").split() members = attrs.get("members", "").split() align = attrs["align"] size = attrs.get("size") return typedesc.Union(name, align, members, bases, size) def Field(self, attrs): # name, type name = attrs["name"] ## if name.startswith("__") and not name.endswith("__"): ## print "INVALID FIELD NAME", name typ = attrs["type"] bits = attrs.get("bits", None) offset = attrs.get("offset") return typedesc.Field(name, typ, bits, offset) def _fixup_Field(self, f): f.typ = self.all[f.typ] ################ def _fixup_Macro(self, m): pass def get_macros(self, text): if text is None: return text = "".join(text) # preprocessor definitions that look like macros with one or more arguments for m in text.splitlines(): name, body = m.split(None, 1) name, args = name.split("(", 1) args = "(%s" % args self.all[name] = typedesc.Macro(name, args, body) def get_aliases(self, text, namespace): if text is None: return # preprocessor definitions that look like aliases: # #define A B text = "".join(text) aliases = {} for a in text.splitlines(): name, value = a.split(None, 1) a = typedesc.Alias(name, value) aliases[name] = a self.all[name] = a for name, a in aliases.items(): value = a.alias # the value should be either in namespace... if value in namespace: # set the type a.typ = namespace[value] # or in aliases... elif value in aliases: a.typ = aliases[value] # or unknown. else: # not known ## print "skip %s = %s" % (name, value) pass def get_result(self): interesting = (typedesc.Typedef, typedesc.Enumeration, typedesc.EnumValue, typedesc.Function, typedesc.Structure, typedesc.Union, typedesc.Variable, typedesc.Macro, typedesc.Alias) self.get_macros(self.cpp_data.get("functions")) remove = [] for n, i in self.all.items(): location = getattr(i, "location", None) if location: fil, line = location.split(":") i.location = self.all[fil].name, line # link together all the nodes (the XML that gccxml generates uses this). mth = getattr(self, "_fixup_" + type(i).__name__) try: mth(i) except KeyError: # XXX better exception catching remove.append(n) for n in remove: del self.all[n] # Now we can build the namespace. namespace = {} for i in self.all.values(): if not isinstance(i, interesting): continue # we don't want these name = getattr(i, "name", None) if name is not None: namespace[name] = i self.get_aliases(self.cpp_data.get("aliases"), namespace) result = [] for i in self.all.values(): if isinstance(i, interesting): result.append(i) return result ################################################################ def parse(xmlfile): # parse an XML file into a sequence of type descriptions handler = GCCXML_Handler() xml.sax.parse(xmlfile, handler) return handler.get_result() --- NEW FILE: h2xml_main.py --- """h2xml - convert C include file(s) into an xml file by running gccxml.""" import sys, os, tempfile, re, ConfigParser from ctypes_codegen import cparser from optparse import OptionParser def main(argv=None): if argv is None: argv = sys.argv def add_option(option, opt, value, parser): parser.values.gccxml_options.extend((opt, value)) # Hm, should there be a way to disable the config file? # And then, this should be done AFTER the parameters are processed. config = ConfigParser.ConfigParser() try: config.read("h2xml.cfg") except ConfigParser.ParsingError, detail: print >> sys.stderr, detail return 1 def get_option(option, default_value): # return an option from the platform specific section of the # config file, or return the default_value if either the # section or the option is not present. try: return config.get(sys.platform, option) except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): return default_value excluded = get_option("excluded", "").split() excluded_re = get_option("excluded_re", "").split() parser = OptionParser("usage: %prog includefile ... [options]") parser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False) parser.add_option("-D", type="string", action="callback", callback=add_option, dest="gccxml_options", help="macros to define", metavar="NAME[=VALUE]", default=[]) parser.add_option("-U", type="string", action="callback", callback=add_option, help="macros to undefine", metavar="NAME") parser.add_option("-I", type="string", action="callback", callback=add_option, dest="gccxml_options", help="additional include directories", metavar="DIRECTORY") parser.add_option("-o", dest="xmlfile", help="XML output filename", default=None) parser.add_option("-c", "--cpp-symbols", dest="cpp_symbols", action="store_true", help="try to find #define symbols - this may give compiler errors, " \ "so it's off by default.", default=False) parser.add_option("-k", dest="keep_temporary_files", action="store_true", help="don't delete the temporary files created "\ "(useful for finding problems)", default=False) parser.add_option("-s", dest="excluded_symbols", action="append", help="specify preprocessor symbol name to exclude", default=excluded) parser.add_option("-r", dest="excluded_symbols_re", action="append", help="regular expression for preprocessor symbol names to exclude", default=[]) options, files = parser.parse_args(argv[1:]) if not files: print "Error: no files to process" print >> sys.stderr, __doc__ return 1 options.flags = options.gccxml_options options.verbose = not options.quiet options.excluded_symbols_re = [re.compile(pat) for pat in options.excluded_symbols_re] try: parser = cparser.IncludeParser(options) parser.parse(files) except cparser.CompilerError, detail: print >> sys.stderr, "CompilerError:", detail return 1 if __name__ == "__main__": sys.exit(main()) --- NEW FILE: dynmod.py --- # A dynamic module generator for ctypes. # # import sys, os, md5, bz2, cPickle, errno import ctypes from ctypes.wrap import xml2py, h2xml, gccxmlparser, codegenerator def get_items(include_files, gccopts): # Process <include_files> with xml2py. <gccopts> is a list of # command line options to pass. Then parse the XML file with # gccxmlparser, and return a tuple containing two items: a # generated filename, and a collection of type descriptions. # # Since all this takes some time, the resulting files are written # to a cache directory, and will be picked up from there instead # of recreated each time. The filename is created by passing all # the options through a message digest, this creates a long, # unique filename. dirname = os.path.dirname(os.path.abspath(xml2py.__file__)) cache_dir = os.path.join(dirname, "_cache") try: os.mkdir(cache_dir) except OSError, detail: if detail.errno != errno.EEXIST: raise if not os.path.exists(os.path.join(cache_dir, "__init__.py")): text = "# package for generated wrappers\n" open(os.path.join(cache_dir, "__init__.py"), "w").write(text) args = include_files + gccopts basename = "_" + md5.new(" ".join(args)).hexdigest() fullname = os.path.join(cache_dir, basename) # todo: record the options and the generated filename somewhere, # to be able to identify them laster. xml_file = fullname + ".xml" pck_file = fullname + ".pck.bz2" # todo: write a version number into the pickle file, and check # that before reading! if os.path.exists(pck_file): print "# reading", pck_file data = open(pck_file, "rb").read() data = bz2.decompress(data) items = cPickle.loads(data) return items if not os.path.exists(... [truncated message content] |