[pypy-commit] pypy default: (theller)

antocuni noreply at buildbot.pypy.org
Tue Jul 5 11:41:58 CEST 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r45354:634a87b2adbf
Date: 2011-07-05 11:50 +0200
http://bitbucket.org/pypy/pypy/changeset/634a87b2adbf/

Log:	(theller) fix issue743, by Thomas Heller:
	https://bugs.pypy.org/issue743

	I changed test_commethods.py from unittest to py.test. However, I
	don't have a windows box around where to try it, so I cross the
	fingers and I commit it blindly. Please review :-)

diff --git a/lib_pypy/_ctypes/__init__.py b/lib_pypy/_ctypes/__init__.py
--- a/lib_pypy/_ctypes/__init__.py
+++ b/lib_pypy/_ctypes/__init__.py
@@ -18,7 +18,16 @@
 if _os.name in ("nt", "ce"):
     from _rawffi import FormatError
     from _rawffi import check_HRESULT as _check_HRESULT
-    CopyComPointer = None # XXX
+
+    def CopyComPointer(src, dst):
+        from ctypes import c_void_p, cast
+        if src:
+            hr = src[0][0].AddRef(src)
+            if hr & 0x80000000:
+                return hr
+        dst[0] = cast(src, c_void_p).value
+        return 0
+
     LoadLibrary = dlopen
 
 from _rawffi import FUNCFLAG_STDCALL, FUNCFLAG_CDECL, FUNCFLAG_PYTHONAPI
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -139,7 +139,10 @@
         return buffer(self._buffer)
 
     def _get_b_base(self):
-        return self._base
+        try:
+            return self._base
+        except AttributeError:
+            return None
     _b_base_ = property(_get_b_base)
     _b_needsfree_ = False
 
@@ -218,5 +221,7 @@
     'z' : _ffi.types.void_p,
     'O' : _ffi.types.void_p,
     'Z' : _ffi.types.void_p,
+    'X' : _ffi.types.void_p,
+    'v' : _ffi.types.sshort,
     }
 
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -322,20 +322,18 @@
                           RuntimeWarning, stacklevel=2)
 
         if self._com_index:
-            assert False, 'TODO2'
             from ctypes import cast, c_void_p, POINTER
             if not args:
                 raise ValueError(
                     "native COM method call without 'this' parameter"
                     )
-            thisarg = cast(args[0], POINTER(POINTER(c_void_p))).contents
-            argtypes = [c_void_p] + list(argtypes)
-            args = list(args)
-            args[0] = args[0].value
+            thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
+            newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs)
+            newargs.insert(0, args[0].value)
+            argtypes.insert(0, c_void_p)
         else:
             thisarg = None
-            
-        newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
+            newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
 
         funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
         result = self._call_funcptr(funcptr, *newargs)
@@ -343,6 +341,11 @@
 
         if not outargs:
             return result
+
+        simple_cdata = type(c_void_p()).__bases__[0]
+        outargs = [x.value if type(x).__bases__[0] is simple_cdata else x
+                   for x in outargs]
+
         if len(outargs) == 1:
             return outargs[0]
         return tuple(outargs)
@@ -398,10 +401,10 @@
             # extract the address from the object's virtual table
             if not thisarg:
                 raise ValueError("COM method call without VTable")
-            ptr = thisarg[self._com_index - 0x1000]
-            argshapes = [arg._ffiargshape for arg in argtypes]
-            resshape = restype._ffiargshape
-            return _rawffi.FuncPtr(ptr, argshapes, resshape, self._flags_)
+            ptr = thisarg[0][self._com_index - 0x1000]
+            ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
+            ffires = restype.get_ffi_argtype()
+            return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires)
         
         cdll = self.dll._handle
         try:
@@ -468,11 +471,7 @@
         newargtypes = []
         total = len(args)
         paramflags = self._paramflags
-
-        if self._com_index:
-            inargs_idx = 1
-        else:
-            inargs_idx = 0
+        inargs_idx = 0
 
         if not paramflags and total < len(argtypes):
             raise TypeError("not enough arguments")
@@ -587,13 +586,7 @@
 
         retval = None
 
-        if self._com_index:
-            if resbuffer[0] & 0x80000000:
-                raise get_com_error(resbuffer[0],
-                                    self._com_iid, argsandobjs[0])
-            else:
-                retval = int(resbuffer[0])
-        elif restype is not None:
+        if restype is not None:
             checker = getattr(self.restype, '_check_retval_', None)
             if checker:
                 val = restype(result)
@@ -601,7 +594,13 @@
                 # classes defining a new type, and their subclasses
                 if '_type_' in restype.__dict__:
                     val = val.value
-                retval = checker(val)
+                # XXX Raise a COMError when restype is HRESULT and
+                # checker(val) fails.  How to check for restype == HRESULT?
+                if self._com_index:
+                    if result & 0x80000000:
+                        raise get_com_error(result, None, None)
+                else:
+                    retval = checker(val)
             elif not isinstance(restype, _CDataMeta):
                 retval = restype(result)
             else:
diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py
--- a/lib_pypy/_ctypes/primitive.py
+++ b/lib_pypy/_ctypes/primitive.py
@@ -216,10 +216,15 @@
             result.value = property(_getvalue, _setvalue)
 
         elif tp == 'X':
-            from ctypes import windll
-            SysAllocStringLen = windll.oleaut32.SysAllocStringLen
-            SysStringLen = windll.oleaut32.SysStringLen
-            SysFreeString = windll.oleaut32.SysFreeString
+            from ctypes import WinDLL
+            # Use WinDLL("oleaut32") instead of windll.oleaut32
+            # because the latter is a shared (cached) object; and
+            # other code may set their own restypes. We need out own
+            # restype here.
+            oleaut32 = WinDLL("oleaut32")
+            SysAllocStringLen = oleaut32.SysAllocStringLen
+            SysStringLen = oleaut32.SysStringLen
+            SysFreeString = oleaut32.SysFreeString
             def _getvalue(self):
                 addr = self._buffer[0]
                 if addr == 0:
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py
@@ -0,0 +1,82 @@
+# unittest for SOME ctypes com function calls.
+# Can't resist from implementing some kind of mini-comtypes
+# theller ;-)
+
+import py
+import sys
+if sys.platform != "win32":
+    py.test.skip('windows only test')
+
+import ctypes, new, unittest
+from ctypes.wintypes import HRESULT
+from _ctypes import COMError
+
+oleaut32 = ctypes.OleDLL("oleaut32")
+
+class UnboundMethod(object):
+    def __init__(self, func, index, name):
+        self.func = func
+        self.index = index
+        self.name = name
+        self.__doc__ = func.__doc__
+
+    def __repr__(self):
+        return "<Unbound COM method index %d: %s at %x>" % (self.index, self.name, id(self))
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        return new.instancemethod(self.func, instance, owner)
+    
+def commethod(index, restype, *argtypes):
+    """A decorator that generates COM methods.  The decorated function
+    itself is not used except for it's name."""
+    def make_commethod(func):
+        comfunc = ctypes.WINFUNCTYPE(restype, *argtypes)(index, func.__name__)
+        comfunc.__name__ = func.__name__
+        comfunc.__doc__ = func.__doc__
+        return UnboundMethod(comfunc, index, func.__name__)
+    return make_commethod
+
+class ICreateTypeLib2(ctypes.c_void_p):
+
+    @commethod(1, ctypes.c_long)
+    def AddRef(self):
+        pass
+
+    @commethod(2, ctypes.c_long)
+    def Release(self):
+        pass
+
+    @commethod(4, HRESULT, ctypes.c_wchar_p)
+    def SetName(self):
+        """Set the name of the library."""
+
+    @commethod(12, HRESULT)
+    def SaveAllChanges(self):
+        pass
+
+
+CreateTypeLib2 = oleaut32.CreateTypeLib2
+CreateTypeLib2.argtypes = (ctypes.c_int, ctypes.c_wchar_p, ctypes.POINTER(ICreateTypeLib2))
+
+################################################################
+
+def test_basic_comtypes():
+    punk = ICreateTypeLib2()
+    hr = CreateTypeLib2(0, "foobar.tlb", punk)
+    assert hr == 0
+
+    assert 2 == punk.AddRef()
+    assert 3 == punk.AddRef()
+    assert 4 == punk.AddRef()
+
+    punk.SetName("TypeLib_ByPYPY")
+    py.test.raises(COMError, lambda: punk.SetName(None))
+
+    # This would save the typelib to disk.
+    ## punk.SaveAllChanges()
+
+    assert 3 == punk.Release()
+    assert 2 == punk.Release()
+    assert 1 == punk.Release()


More information about the pypy-commit mailing list