Author: Antonio Cuni <[email protected]>
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()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit