ctypes-commit Mailing List for ctypes (Page 7)
Brought to you by:
theller
You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
(8) |
May
(90) |
Jun
(143) |
Jul
(106) |
Aug
(94) |
Sep
(84) |
Oct
(163) |
Nov
(60) |
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(128) |
Feb
(79) |
Mar
(227) |
Apr
(192) |
May
(179) |
Jun
(41) |
Jul
(53) |
Aug
(103) |
Sep
(28) |
Oct
(38) |
Nov
(81) |
Dec
(17) |
2006 |
Jan
(184) |
Feb
(111) |
Mar
(188) |
Apr
(67) |
May
(58) |
Jun
(123) |
Jul
(73) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
From: Thomas H. <th...@us...> - 2006-06-09 18:07:25
|
Update of /cvsroot/ctypes/ctypes/ctypes/test In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv20263 Modified Files: test_cast.py Log Message: Adapt test to the way cast works now. Minial test for pointer item assignments with index != 0. Index: test_cast.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/ctypes/test/test_cast.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** test_cast.py 9 Jun 2006 07:02:10 -0000 1.4 --- test_cast.py 9 Jun 2006 18:07:16 -0000 1.5 *************** *** 40,51 **** # array and p share a common _objects attribute self.failUnless(p._objects is array._objects) ! self.failUnlessEqual(array._objects, {'0': "foo bar"}) p[0] = "spam spam" ! self.failUnlessEqual(p._objects, {'0': "spam spam"}) ! self.failUnlessEqual(array._objects, {'0': "spam spam"}) p[1] = "foo bar" ! self.failUnlessEqual(p._objects, {'1': 'foo bar', '0': "spam spam"}) ! self.failUnlessEqual(array._objects, {'1': 'foo bar', '0': "spam spam"}) if __name__ == "__main__": --- 40,60 ---- # array and p share a common _objects attribute self.failUnless(p._objects is array._objects) ! self.failUnlessEqual(array._objects, {'0': "foo bar", id(array): array}) p[0] = "spam spam" ! self.failUnlessEqual(p._objects, {'0': "spam spam", id(array): array}) ! self.failUnless(array._objects is p._objects) p[1] = "foo bar" ! self.failUnlessEqual(p._objects, {'1': 'foo bar', '0': "spam spam", id(array): array}) ! self.failUnless(array._objects is p._objects) + def test_other(self): + p = cast((c_int * 4)(1, 2, 3, 4), POINTER(c_int)) + self.failUnlessEqual(p[:4], [1,2, 3, 4]) + c_int() + self.failUnlessEqual(p[:4], [1, 2, 3, 4]) + p[2] = 96 + self.failUnlessEqual(p[:4], [1, 2, 96, 4]) + c_int() + self.failUnlessEqual(p[:4], [1, 2, 96, 4]) if __name__ == "__main__": |
From: Thomas H. <th...@us...> - 2006-06-09 18:04:54
|
Update of /cvsroot/ctypes/ctypes/source In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv19110 Modified Files: _ctypes.c Log Message: Remove the restriction that pointer item assignments only work with index of zero. Another fix for the cast function to keep needed objects alive. Index: _ctypes.c =================================================================== RCS file: /cvsroot/ctypes/ctypes/source/_ctypes.c,v retrieving revision 1.336 retrieving revision 1.337 diff -C2 -d -r1.336 -r1.337 *** _ctypes.c 9 Jun 2006 07:09:10 -0000 1.336 --- _ctypes.c 9 Jun 2006 18:04:45 -0000 1.337 *************** *** 4075,4079 **** { CDataObject *self = (CDataObject *)_self; ! int size, offset; StgDictObject *stgdict, *itemdict; PyObject *proto; --- 4075,4080 ---- { CDataObject *self = (CDataObject *)_self; ! int size; ! Py_ssize_t offset; StgDictObject *stgdict, *itemdict; PyObject *proto; *************** *** 4104,4107 **** --- 4105,4109 ---- CDataObject *self = (CDataObject *)_self; int size; + Py_ssize_t offset; StgDictObject *stgdict; *************** *** 4120,4127 **** stgdict = PyObject_stgdict((PyObject *)self); size = stgdict->size / stgdict->length; /* XXXXX Make sure proto is NOT NULL! */ return CData_set((PyObject *)self, stgdict->proto, stgdict->setfunc, value, ! index, size, *(void **)self->b_ptr); } --- 4122,4130 ---- stgdict = PyObject_stgdict((PyObject *)self); size = stgdict->size / stgdict->length; + offset = index * size; /* XXXXX Make sure proto is NOT NULL! */ return CData_set((PyObject *)self, stgdict->proto, stgdict->setfunc, value, ! index, size, (*(char **)self->b_ptr) + offset); } *************** *** 4140,4145 **** assert(stgdict); return CData_FromBaseObj(stgdict->proto, ! (PyObject *)self, 0, ! *(void **)self->b_ptr); } --- 4143,4148 ---- assert(stgdict); return CData_FromBaseObj(stgdict->proto, ! (PyObject *)self, 0, ! *(void **)self->b_ptr); } *************** *** 4498,4501 **** --- 4501,4510 ---- return NULL; + /* + The casted objects '_objects' member: + + It must certainly contain the source objects one. + It must contain the source object itself. + */ if (CDataObject_Check(src)) { CDataObject *obj = (CDataObject *)src; *************** *** 4503,4511 **** this so it can be shared */ CData_GetContainer(obj); ! Py_XINCREF(obj->b_objects); result->b_objects = obj->b_objects; ! ! Py_XINCREF(obj->b_base); ! result->b_base = obj->b_base; } /* Should we assert that result is a pointer type? */ --- 4512,4532 ---- this so it can be shared */ CData_GetContainer(obj); ! /* But we need a dictionary! */ ! if (obj->b_objects == Py_None) { ! Py_DECREF(Py_None); ! obj->b_objects = PyDict_New(); ! } ! Py_INCREF(obj->b_objects); result->b_objects = obj->b_objects; ! if (result->b_objects) { ! PyObject *index = PyLong_FromVoidPtr((void *)src); ! int rc; ! if (index == NULL) ! return NULL; ! rc = PyDict_SetItem(result->b_objects, index, src); ! Py_DECREF(index); ! if (rc == -1) ! return NULL; ! } } /* Should we assert that result is a pointer type? */ |
From: Thomas H. <th...@us...> - 2006-06-09 17:38:44
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv8594 Modified Files: TODO Log Message: Index: TODO =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/TODO,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** TODO 8 Jun 2006 19:11:36 -0000 1.2 --- TODO 9 Jun 2006 17:38:41 -0000 1.3 *************** *** 1,13 **** codegenerator bugs: ! - Don't use the no-longer existing cdecl or stdcall decorators. ! - Don't generate code for COM interfaces. ! - In ctypes/comtypes: Move GUID and _GUID into ctypes.wintypes. ! - Import definitions from ctypes.wintypes ! - Import the old docs into cvs. ! - Write a README file. \ No newline at end of file --- 1,35 ---- codegenerator bugs: ! - In ctypes/comtypes: Move GUID and _GUID into ctypes.wintypes. ! - Import the old docs into cvs. ! - Write a README file. ! - Don't add 'artificial' structure or union names to the __all__ list. ! - Fix code generation for inline functions: ! <C code> ! #ifdef __cplusplus ! __inline int IsEqualGUID(REFGUID rguid1, REFGUID rguid2) ! { ! return !memcmp(&rguid1, &rguid2, sizeof(GUID)); ! } ! <C code/> ! ! <Python code> ! # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/guiddef.h 160 ! IsEqualGUID = CDLL('ole32').IsEqualGUID ! IsEqualGUID.restypes = c_int ! # IsEqualGUID(rguid1, rguid2) ! IsEqualGUID.argtypes = [POINTER(GUID), POINTER(GUID)] ! ! <Python code/> ! ! <xml code> ! <Function id="_4898" name="IsEqualGUID" returns="_3989" context="_1" location="f14:160" file="f14" line="160" endline="162" inline="1"> ! <Argument name="rguid1" type="_9847" location="f14:160" file="f14" line="160"/> ! <Argument name="rguid2" type="_9847" location="f14:160" file="f14" line="160"/> ! </Function> ! <xml code/> \ No newline at end of file |
From: Thomas H. <th...@us...> - 2006-06-09 17:38:02
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv8190 Modified Files: ChangeLog Log Message: Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ChangeLog,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** ChangeLog 8 Jun 2006 19:19:18 -0000 1.4 --- ChangeLog 9 Jun 2006 17:37:58 -0000 1.5 *************** *** 1,2 **** --- 1,12 ---- + 2006-06-09 Thomas Heller <th...@py...> + + * ctypes_codegen\codegenerator.py: Added a USE_COMMENTS variable + which allows to turn comment generation off. Generated + '_anonymous_' declarations for unnamed structure fields. + + Changed the way libraries are loaded, the cdecl and stdcall + decorators are no longer used. Functions are generated with + assignments of .restype and .argtypes.: + 2006-06-08 Thomas Heller <th...@py...> |
From: Thomas H. <th...@us...> - 2006-06-09 17:35:10
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv6996 Modified Files: codegenerator.py Log Message: Added a USE_COMMENTS variable which allows to turn comment generation off. Generated '_anonymous_' declarations for unnamed structure fields. Changed the way libraries are loaded, the cdecl and stdcall decorators are no longer used. Functions are generated with assignments of .restype and .argtypes. Index: codegenerator.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/codegenerator.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** codegenerator.py 8 Jun 2006 20:04:14 -0000 1.3 --- codegenerator.py 9 Jun 2006 17:35:01 -0000 1.4 *************** *** 2,8 **** # Type descriptions are collections of typedesc instances. ! import typedesc, sys import textwrap ASSUME_STRINGS = True --- 2,10 ---- # Type descriptions are collections of typedesc instances. ! import typedesc, sys, os import textwrap + # This should be configurable + USE_COMMENTS = True ASSUME_STRINGS = True *************** *** 288,292 **** 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] --- 290,294 ---- self.generate(struct.get_head()) self.more.add(struct) ! if USE_COMMENTS and head.struct.location: print >> self.stream, "# %s %s" % head.struct.location basenames = [self.type_name(b) for b in head.struct.bases] *************** *** 319,323 **** else: self.generate(tp.typ) ! if self.type_name(tp.typ) in self.known_symbols: stream = self.imports else: --- 321,325 ---- else: self.generate(tp.typ) ! if 0 and self.type_name(tp.typ) in self.known_symbols: stream = self.imports else: *************** *** 450,472 **** 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 --- 452,477 ---- for f in fields: self.type_name(f.typ) + + # unnamed fields get autogenerated names "_0", "_1". "_2", "_3", ... + unnamed_fields = {} + for f in fields: + if not f.name: + unnamed_fields[f] = "_%d" % len(unnamed_fields) + if unnamed_fields: + print >> self.stream, "%s._anonymous_ = %r" % \ + (body.struct.name, unnamed_fields.values()) print >> self.stream, "%s._fields_ = [" % body.struct.name ! ! if USE_COMMENTS and body.struct.location: print >> self.stream, " # %s %s" % body.struct.location ! index = 0 for f in fields: ! fieldname = unnamed_fields.get(f, 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 *************** *** 495,513 **** 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 --- 500,531 ---- return None ! _c_libraries = None ! def need_CLibraries(self): ! # Create a '_libraries' doctionary in the generated code, if ! # it not yet exists. Will map library pathnames to loaded libs. ! if self._c_libraries is None: ! self._c_libraries = {} ! print >> self.imports, "_libraries = {}" ! ! _stdcall_libraries = None ! def need_WinLibraries(self): ! # Create a '_stdcall_libraries' doctionary in the generated code, if ! # it not yet exists. Will map library pathnames to loaded libs. ! if self._stdcall_libraries is None: ! self._stdcall_libraries = {} ! print >> self.imports, "_stdcall_libraries = {}" ! ! def get_sharedlib(self, dllname, cc): ! if cc == "stdcall": ! self.need_WinLibraries() ! if not dllname in self._stdcall_libraries: ! print >> self.imports, "_stdcall_libraries[%r] = WinDLL(%r)" % (dllname, dllname) ! self._stdcall_libraries[dllname] = None ! return "_stdcall_libraries[%r]" % dllname ! self.need_CLibraries() ! if not dllname in self._c_libraries: ! print >> self.imports, "_libraries[%r] = CDLL(%r)" % (dllname, dllname) ! self._c_libraries[dllname] = None ! return "_libraries[%r]" % dllname _STRING_defined = False *************** *** 537,555 **** 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 --- 555,570 ---- else: cc = "cdecl" ! ! libname = self.get_sharedlib(dllname, cc) ! argnames = [a or "p%d" % (i+1) for i, a in enumerate(func.iterArgNames())] ! ! if USE_COMMENTS and func.location: ! print >> self.stream, "# %s %s" % func.location ! print >> self.stream, "%s = %s.%s" % (func.name, libname, func.name) ! print >> self.stream, "%s.restypes = %s" % (func.name, self.type_name(func.returns)) ! print >> self.stream, "# %s(%s)" % (func.name, ", ".join(argnames)) ! print >> self.stream, "%s.argtypes = [%s]" % (func.name, ", ".join(args)) ! self.names.add(func.name) self._functiontypes += 1 |
From: Thomas H. <th...@us...> - 2006-06-09 07:27:26
|
Update of /cvsroot/ctypes/ctypes In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv20940 Modified Files: ChangeLog Log Message: *** empty log message *** Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/ChangeLog,v retrieving revision 1.136 retrieving revision 1.137 diff -C2 -d -r1.136 -r1.137 *** ChangeLog 9 Jun 2006 07:26:40 -0000 1.136 --- ChangeLog 9 Jun 2006 07:27:23 -0000 1.137 *************** *** 1,4 **** --- 1,6 ---- 2006-06-09 Thomas Heller <th...@py...> + * Tagged release 0_9_9_7. + * A casted object does now reference the source objects '_objects' member. |
From: Thomas H. <th...@us...> - 2006-06-09 07:26:45
|
Update of /cvsroot/ctypes/ctypes In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv20402 Modified Files: ChangeLog ANNOUNCE Log Message: Index: ANNOUNCE =================================================================== RCS file: /cvsroot/ctypes/ctypes/ANNOUNCE,v retrieving revision 1.26 retrieving revision 1.27 diff -C2 -d -r1.26 -r1.27 *** ANNOUNCE 20 Apr 2006 17:51:48 -0000 1.26 --- ANNOUNCE 9 Jun 2006 07:26:40 -0000 1.27 *************** *** 1,4 **** ! ctypes 0.9.9.6 released - Apr 20, 2006 ! ===================================== Overview --- 1,4 ---- ! ctypes 0.9.9.7 released - June 9, 2006 ! ====================================== Overview *************** *** 18,21 **** --- 18,44 ---- + Changes in 0.9.9.7 + + Fixes for 654-bit big endian machines. + + It is now possible to read and write any indexes of pointer + instances (up to 0.9.9.6, reading was allowed for any indexes, but + writing only for index zero). + + Support for unnamed (anonymous) structure fields has been added, + the field names must be listed in the '_anonymous_' class + variable. + + Enabled darwin/x86 support for libffi (Bob Ippolito and Ronald + Oussuren). + + Added support for variable sized data structures: array + types with exactly 1 element don't do bounds checking any more, + and a resize function can resize the internal memory buffer of + ctypes instances. + + Fixed a bug with certain array or structure types that contained + more than 256 elements. + Changes in 0.9.9.6 Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/ChangeLog,v retrieving revision 1.135 retrieving revision 1.136 diff -C2 -d -r1.135 -r1.136 *** ChangeLog 8 Jun 2006 15:09:13 -0000 1.135 --- ChangeLog 9 Jun 2006 07:26:40 -0000 1.136 *************** *** 1,2 **** --- 1,10 ---- + 2006-06-09 Thomas Heller <th...@py...> + + * A casted object does now reference the source objects '_objects' + member. + + * It is now possible to assign arbitrary indexes on pointer + instances. + 2006-06-08 Thomas Heller <th...@py...> |
From: Thomas H. <th...@us...> - 2006-06-09 07:18:25
|
Update of /cvsroot/ctypes/ctypes/ctypes/test In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv15448 Added Files: test_objects.py Log Message: Add another test for '_objects'. --- NEW FILE: test_objects.py --- r''' This tests the '_objects' attribute of ctypes instances. '_objects' holds references to objects that must be kept alive as long as the ctypes instance, to make sure that the memory buffer is valid. WARNING: The '_objects' attribute is exposed ONLY for debugging ctypes itself, it MUST NEVER BE MODIFIED! '_objects' is initialized to a dictionary on first use, before that it is None. Here is an array of string pointers: >>> from ctypes import * >>> array = (c_char_p * 5)() >>> print array._objects None >>> The memory block stores pointers to strings, and the strings itself assigned from Python must be kept. >>> array[4] = 'foo bar' >>> array._objects {'4': 'foo bar'} >>> array[4] 'foo bar' >>> It gets more complicated when the ctypes instance itself is contained in a 'base' object. >>> class X(Structure): ... _fields_ = [("x", c_int), ("y", c_int), ("array", c_char_p * 5)] ... >>> x = X() >>> print x._objects None >>> The'array' attribute of the 'x' object shares part of the memory buffer of 'x' ('_b_base_' is either None, or the root object owning the memory block): >>> print x.array._b_base_ # doctest: +ELLIPSIS <X object at 0x...> >>> >>> x.array[0] = 'spam spam spam' >>> x._objects {'0:2': 'spam spam spam'} >>> x.array._b_base_._objects {'0:2': 'spam spam spam'} >>> ''' import unittest, os class TestCase(unittest.TestCase): def test(self): import doctest doctest.testfile(os.path.abspath(__file__)) if __name__ == '__main__': import doctest doctest.testfile(os.path.abspath(__file__)) |
From: Thomas H. <th...@us...> - 2006-06-09 07:09:13
|
Update of /cvsroot/ctypes/ctypes/source In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv10277 Modified Files: _ctypes.c Log Message: Remove the index checking in Pointer_ass_item. This has the effect that you can modify any memory location by assigning the pointer items. A casted object will now contain the source objects '_objects' dictionary. Index: _ctypes.c =================================================================== RCS file: /cvsroot/ctypes/ctypes/source/_ctypes.c,v retrieving revision 1.335 retrieving revision 1.336 diff -C2 -d -r1.335 -r1.336 *** _ctypes.c 8 Jun 2006 14:45:19 -0000 1.335 --- _ctypes.c 9 Jun 2006 07:09:10 -0000 1.336 *************** *** 4119,4127 **** stgdict = PyObject_stgdict((PyObject *)self); - if (index != 0) { - PyErr_SetString(PyExc_IndexError, - "invalid index"); - return -1; - } size = stgdict->size / stgdict->length; --- 4119,4122 ---- *************** *** 4502,4510 **** if (result == NULL) return NULL; ! /* KeepRef consumes a refcount on src */ ! Py_INCREF(src); ! if (-1 == KeepRef(result, 0, src)) { ! Py_DECREF(result); ! return NULL; } /* Should we assert that result is a pointer type? */ --- 4497,4511 ---- if (result == NULL) return NULL; ! ! if (CDataObject_Check(src)) { ! CDataObject *obj = (CDataObject *)src; ! /* CData_GetContainer will initialize src.b_objects, we need ! this so it can be shared */ ! CData_GetContainer(obj); ! Py_XINCREF(obj->b_objects); ! result->b_objects = obj->b_objects; ! ! Py_XINCREF(obj->b_base); ! result->b_base = obj->b_base; } /* Should we assert that result is a pointer type? */ |
From: Thomas H. <th...@us...> - 2006-06-09 07:02:17
|
Update of /cvsroot/ctypes/ctypes/ctypes/test In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv6506 Modified Files: test_cast.py Log Message: Better test for the cast function. Index: test_cast.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/ctypes/test/test_cast.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** test_cast.py 19 Apr 2006 16:42:28 -0000 1.3 --- test_cast.py 9 Jun 2006 07:02:10 -0000 1.4 *************** *** 31,45 **** self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) ! def test_ptr2array(self): ! array = (c_int * 3)(42, 17, 2) ! ! from sys import getrefcount - before = getrefcount(array) - ptr = cast(array, POINTER(c_int)) - self.failUnlessEqual(getrefcount(array), before + 1) - del ptr - self.failUnlessEqual(getrefcount(array), before) if __name__ == "__main__": --- 31,51 ---- self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + def test_p2a_objects(self): + array = (c_char_p * 5)() + self.failUnlessEqual(array._objects, None) + array[0] = "foo bar" + self.failUnlessEqual(array._objects, {'0': "foo bar"}) ! p = cast(array, POINTER(c_char_p)) ! # array and p share a common _objects attribute ! self.failUnless(p._objects is array._objects) ! self.failUnlessEqual(array._objects, {'0': "foo bar"}) ! p[0] = "spam spam" ! self.failUnlessEqual(p._objects, {'0': "spam spam"}) ! self.failUnlessEqual(array._objects, {'0': "spam spam"}) ! p[1] = "foo bar" ! self.failUnlessEqual(p._objects, {'1': 'foo bar', '0': "spam spam"}) ! self.failUnlessEqual(array._objects, {'1': 'foo bar', '0': "spam spam"}) if __name__ == "__main__": |
From: Thomas H. <th...@us...> - 2006-06-09 05:52:31
|
Update of /cvsroot/ctypes/ctypes In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv466 Modified Files: env.bat Log Message: Include codegenerator in PYTHONPATH, for development. Index: env.bat =================================================================== RCS file: /cvsroot/ctypes/ctypes/env.bat,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** env.bat 3 Mar 2006 20:32:10 -0000 1.3 --- env.bat 9 Jun 2006 05:52:27 -0000 1.4 *************** *** 1,2 **** @echo off ! for %%i in (.) do set PYTHONPATH=%%~fi \ No newline at end of file --- 1,2 ---- @echo off ! for %%i in (.) do set PYTHONPATH=%%~fi;%%~fi\codegen;%%~fi\codegen\scripts |
From: Thomas H. <th...@us...> - 2006-06-09 05:50:17
|
Update of /cvsroot/ctypes/ctypes/source In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv31923 Modified Files: ctypes.h Log Message: Correct comment. Index: ctypes.h =================================================================== RCS file: /cvsroot/ctypes/ctypes/source/ctypes.h,v retrieving revision 1.104 retrieving revision 1.105 diff -C2 -d -r1.104 -r1.105 *** ctypes.h 18 Apr 2006 20:18:31 -0000 1.104 --- ctypes.h 9 Jun 2006 05:50:10 -0000 1.105 *************** *** 60,64 **** Py_ssize_t b_index; /* index of this object into base's b_object list */ ! PyObject *b_objects; /* list of references we need to keep */ union value b_value; }; --- 60,64 ---- Py_ssize_t b_index; /* index of this object into base's b_object list */ ! PyObject *b_objects; /* dictionary of references we need to keep, or Py_None */ union value b_value; }; |
From: Thomas H. <th...@us...> - 2006-06-08 20:22:15
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv10169 Modified Files: xml2py_main.py Log Message: As known_symbols, only import instances of type. Index: xml2py_main.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/xml2py_main.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** xml2py_main.py 8 Jun 2006 19:17:26 -0000 1.2 --- xml2py_main.py 8 Jun 2006 20:22:05 -0000 1.3 *************** *** 139,144 **** for submodule in name.split(".")[1:]: mod = getattr(mod, submodule) ! for name in mod.__dict__: ! known_symbols[name] = mod.__name__ if options.kind: --- 139,145 ---- for submodule in name.split(".")[1:]: mod = getattr(mod, submodule) ! for name, item in mod.__dict__.iteritems(): ! if isinstance(item, type): ! known_symbols[name] = mod.__name__ if options.kind: |
From: Thomas H. <th...@us...> - 2006-06-08 20:04:18
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv2016 Modified Files: codegenerator.py Log Message: Removed most of the code that generates COM interfaces. Now, COM interfaces are generated as simple Structure subclasses, without any fields or methods - must be fixed manually in the generated code, but better would be to avoid them at all. Index: codegenerator.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/codegenerator.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** codegenerator.py 8 Jun 2006 19:18:28 -0000 1.2 --- codegenerator.py 8 Jun 2006 20:04:14 -0000 1.3 *************** *** 292,322 **** 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) --- 292,304 ---- basenames = [self.type_name(b) for b in head.struct.bases] if basenames: ! ### 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)) else: ! ### methods = [m for m in head.struct.members if type(m) is typedesc.Method] ! if 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) *************** *** 435,441 **** 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) --- 417,427 ---- pass ! if methods: ! # XXX we have parsed the COM interface methods but should ! # we emit any code for them? ! pass ! else: ! # we don't need _pack_ on Unions (I hope, at least), and not ! # on COM interfaces. try: pack = calc_packing(body.struct, fields) *************** *** 449,453 **** print >> self.stream, "# WARNING: %s" % details ! if fields: if body.struct.bases: assert len(body.struct.bases) == 1 --- 435,445 ---- print >> self.stream, "# WARNING: %s" % details ! if not fields: ! # XXX Normally this does not work for COM ! # interfaces. _fields_ must be defined before they are ! # subclassed. ! ## print >> self.stream, "%s._fields_ = []" % body.struct.name ! pass ! else: if body.struct.bases: assert len(body.struct.bases) == 1 *************** *** 487,536 **** (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"): --- 479,482 ---- *************** *** 579,605 **** 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 --- 525,528 ---- |
From: Thomas H. <th...@us...> - 2006-06-08 19:19:25
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv14809 Modified Files: ChangeLog Log Message: Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ChangeLog,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** ChangeLog 8 Jun 2006 19:11:59 -0000 1.3 --- ChangeLog 8 Jun 2006 19:19:18 -0000 1.4 *************** *** 1,4 **** --- 1,7 ---- 2006-06-08 Thomas Heller <th...@py...> + * ctypes_codegen\codegenerator.py: When generating the '__all__' list, + don't break long strings. + * h2xml does no longer create a file named 'None' when no output file was given on the command line. |
From: Thomas H. <th...@us...> - 2006-06-08 19:18:34
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv14408 Modified Files: codegenerator.py Log Message: Cleanup. When generating the '__all__' list, don't break long strings. Index: codegenerator.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/codegenerator.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** codegenerator.py 8 Jun 2006 18:46:47 -0000 1.1 --- codegenerator.py 8 Jun 2006 19:18:28 -0000 1.2 *************** *** 2,6 **** # Type descriptions are collections of typedesc instances. ! import typedesc, sys, types ASSUME_STRINGS = True --- 2,7 ---- # Type descriptions are collections of typedesc instances. ! import typedesc, sys ! import textwrap ASSUME_STRINGS = True *************** *** 691,699 **** 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 --- 692,700 ---- self.output.write(self.stream.getvalue()) text = "__all__ = [%s]" % ", ".join([repr(str(n)) for n in self.names]) ! wrapper = textwrap.TextWrapper(break_long_words=False, ! subsequent_indent=" ") ! for line in wrapper.wrap(text): print >> self.output, line return loops |
From: Thomas H. <th...@us...> - 2006-06-08 19:17:30
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv13948 Modified Files: xml2py_main.py Log Message: Cleanup. Index: xml2py_main.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/xml2py_main.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** xml2py_main.py 8 Jun 2006 18:46:47 -0000 1.1 --- xml2py_main.py 8 Jun 2006 19:17:26 -0000 1.2 *************** *** 102,114 **** 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" ] --- 102,107 ---- |
From: Thomas H. <th...@us...> - 2006-06-08 19:12:03
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv11512 Modified Files: ChangeLog Log Message: Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ChangeLog,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** ChangeLog 8 Jun 2006 18:58:17 -0000 1.2 --- ChangeLog 8 Jun 2006 19:11:59 -0000 1.3 *************** *** 1,4 **** --- 1,7 ---- 2006-06-08 Thomas Heller <th...@py...> + * h2xml does no longer create a file named 'None' when no output + file was given on the command line. + * Added a TODO list. |
From: Thomas H. <th...@us...> - 2006-06-08 19:11:48
|
Update of /cvsroot/ctypes/ctypes/codegen/ctypes_codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv11481 Modified Files: cparser.py Log Message: Don't create a 'None' output file when no output file is specified on the command line. Index: cparser.py =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ctypes_codegen/cparser.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** cparser.py 8 Jun 2006 18:46:47 -0000 1.1 --- cparser.py 8 Jun 2006 19:11:41 -0000 1.2 *************** *** 112,116 **** xmlfile, and remove the source file again.""" fname = self.create_source_file(lines) ! args = ["gccxml", fname, "-fxml=%s" % xmlfile] if self.options.flags: args.extend(self.options.flags) --- 112,118 ---- xmlfile, and remove the source file again.""" fname = self.create_source_file(lines) ! args = ["gccxml", fname] ! if xmlfile is not None: ! args.append("-fxml=%s" % xmlfile) if self.options.flags: args.extend(self.options.flags) |
From: Thomas H. <th...@us...> - 2006-06-08 19:11:42
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv11458 Modified Files: TODO Log Message: Don't create a 'None' output file when no output file is specified on the command line. Index: TODO =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/TODO,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** TODO 8 Jun 2006 18:58:17 -0000 1.1 --- TODO 8 Jun 2006 19:11:36 -0000 1.2 *************** *** 9,15 **** - Import definitions from ctypes.wintypes - - When no '-o outfile' flag is given to h2xml.py, it generates an - output file named 'None' ;-). - - Import the old docs into cvs. --- 9,12 ---- |
From: Thomas H. <th...@us...> - 2006-06-08 18:58:20
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv5840 Modified Files: ChangeLog Added Files: TODO Log Message: --- NEW FILE: TODO --- codegenerator bugs: - Don't use the no-longer existing cdecl or stdcall decorators. - Don't generate code for COM interfaces. - In ctypes/comtypes: Move GUID and _GUID into ctypes.wintypes. - Import definitions from ctypes.wintypes - When no '-o outfile' flag is given to h2xml.py, it generates an output file named 'None' ;-). - Import the old docs into cvs. - Write a README file. Index: ChangeLog =================================================================== RCS file: /cvsroot/ctypes/ctypes/codegen/ChangeLog,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** ChangeLog 8 Jun 2006 18:46:41 -0000 1.1 --- ChangeLog 8 Jun 2006 18:58:17 -0000 1.2 *************** *** 1,4 **** --- 1,6 ---- 2006-06-08 Thomas Heller <th...@py...> + * Added a TODO list. + * Initial import of the current ctypes codegenerator version. |
From: Thomas H. <th...@us...> - 2006-06-08 18:47:02
|
Update of /cvsroot/ctypes/ctypes/codegen/scripts In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv785 Added Files: xml2py.py h2xml.py Log Message: Initial import of the current ctypes codegenerator. --- NEW FILE: xml2py.py --- import sys from ctypes_codegen.xml2py_main import main if __name__ == "__main__": sys.exit(main()) --- NEW FILE: h2xml.py --- import sys from ctypes_codegen.h2xml_main import main if __name__ == "__main__": sys.exit(main()) |
From: Thomas H. <th...@us...> - 2006-06-08 18:47:00
|
Update of /cvsroot/ctypes/ctypes/codegen In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv750 Added Files: setup.py ChangeLog .cvsignore Log Message: Initial import of the current ctypes codegenerator. --- NEW FILE: .cvsignore --- build dist --- NEW FILE: setup.py --- """ctypes code generator """ from distutils.core import setup setup(name="ctypes_codegen", packages = ['ctypes_codegen'], scripts = ['scripts/h2xml.py', 'scripts/xml2py.py'], description="ctypes code generator", long_description = __doc__, author="Thomas Heller", author_email="th...@py...", license="MIT License", ## url="http://starship.python.net/crew/theller/ctypes.html", ## platforms=["windows", "Linux", "MacOS X", "Solaris", "FreeBSD"], ) --- NEW FILE: ChangeLog --- 2006-06-08 Thomas Heller <th...@py...> * Initial import of the current ctypes codegenerator version. |
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] |
From: Thomas H. <th...@us...> - 2006-06-08 18:41:38
|
Update of /cvsroot/ctypes/ctypes/codegen/scripts In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv31031/scripts Log Message: Directory /cvsroot/ctypes/ctypes/codegen/scripts added to the repository |