Commit 8ea010ee authored by Thomas Heller's avatar Thomas Heller

Apply the patch #1532975 plus ideas from the patch #1533481.

ctypes instances no longer have the internal and undocumented
'_as_parameter_' attribute which was used to adapt them to foreign
function calls; this mechanism is replaced by a function pointer in
the type's stgdict.

In the 'from_param' class methods, try the _as_parameter_ attribute if
other conversions are not possible.

This makes the documented _as_parameter_ mechanism work as intended.

Change the ctypes version number to 1.0.1.
parent 0976b266
......@@ -5,7 +5,7 @@
import os as _os, sys as _sys
__version__ = "1.0.0"
__version__ = "1.0.1"
from _ctypes import Union, Structure, Array
from _ctypes import _Pointer
......
import unittest
from ctypes import *
import _ctypes_test
dll = CDLL(_ctypes_test.__file__)
try:
CALLBACK_FUNCTYPE = WINFUNCTYPE
except NameError:
# fake to enable this test on Linux
CALLBACK_FUNCTYPE = CFUNCTYPE
class POINT(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
class BasicWrapTestCase(unittest.TestCase):
def wrap(self, param):
return param
def test_wchar_parm(self):
try:
c_wchar
except NameError:
return
f = dll._testfunc_i_bhilfd
f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double]
result = f(self.wrap(1), self.wrap(u"x"), self.wrap(3), self.wrap(4), self.wrap(5.0), self.wrap(6.0))
self.failUnlessEqual(result, 139)
self.failUnless(type(result), int)
def test_pointers(self):
f = dll._testfunc_p_p
f.restype = POINTER(c_int)
f.argtypes = [POINTER(c_int)]
# This only works if the value c_int(42) passed to the
# function is still alive while the pointer (the result) is
# used.
v = c_int(42)
self.failUnlessEqual(pointer(v).contents.value, 42)
result = f(self.wrap(pointer(v)))
self.failUnlessEqual(type(result), POINTER(c_int))
self.failUnlessEqual(result.contents.value, 42)
# This on works...
result = f(self.wrap(pointer(v)))
self.failUnlessEqual(result.contents.value, v.value)
p = pointer(c_int(99))
result = f(self.wrap(p))
self.failUnlessEqual(result.contents.value, 99)
def test_shorts(self):
f = dll._testfunc_callback_i_if
args = []
expected = [262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048,
1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]
def callback(v):
args.append(v)
CallBack = CFUNCTYPE(c_int, c_int)
cb = CallBack(callback)
f(self.wrap(2**18), self.wrap(cb))
self.failUnlessEqual(args, expected)
################################################################
def test_callbacks(self):
f = dll._testfunc_callback_i_if
f.restype = c_int
MyCallback = CFUNCTYPE(c_int, c_int)
def callback(value):
#print "called back with", value
return value
cb = MyCallback(callback)
result = f(self.wrap(-10), self.wrap(cb))
self.failUnlessEqual(result, -18)
# test with prototype
f.argtypes = [c_int, MyCallback]
cb = MyCallback(callback)
result = f(self.wrap(-10), self.wrap(cb))
self.failUnlessEqual(result, -18)
result = f(self.wrap(-10), self.wrap(cb))
self.failUnlessEqual(result, -18)
AnotherCallback = CALLBACK_FUNCTYPE(c_int, c_int, c_int, c_int, c_int)
# check that the prototype works: we call f with wrong
# argument types
cb = AnotherCallback(callback)
self.assertRaises(ArgumentError, f, self.wrap(-10), self.wrap(cb))
def test_callbacks_2(self):
# Can also use simple datatypes as argument type specifiers
# for the callback function.
# In this case the call receives an instance of that type
f = dll._testfunc_callback_i_if
f.restype = c_int
MyCallback = CFUNCTYPE(c_int, c_int)
f.argtypes = [c_int, MyCallback]
def callback(value):
#print "called back with", value
self.failUnlessEqual(type(value), int)
return value
cb = MyCallback(callback)
result = f(self.wrap(-10), self.wrap(cb))
self.failUnlessEqual(result, -18)
def test_longlong_callbacks(self):
f = dll._testfunc_callback_q_qf
f.restype = c_longlong
MyCallback = CFUNCTYPE(c_longlong, c_longlong)
f.argtypes = [c_longlong, MyCallback]
def callback(value):
self.failUnless(isinstance(value, (int, long)))
return value & 0x7FFFFFFF
cb = MyCallback(callback)
self.failUnlessEqual(13577625587, int(f(self.wrap(1000000000000), self.wrap(cb))))
def test_byval(self):
# without prototype
ptin = POINT(1, 2)
ptout = POINT()
# EXPORT int _testfunc_byval(point in, point *pout)
result = dll._testfunc_byval(ptin, byref(ptout))
got = result, ptout.x, ptout.y
expected = 3, 1, 2
self.failUnlessEqual(got, expected)
# with prototype
ptin = POINT(101, 102)
ptout = POINT()
dll._testfunc_byval.argtypes = (POINT, POINTER(POINT))
dll._testfunc_byval.restype = c_int
result = dll._testfunc_byval(self.wrap(ptin), byref(ptout))
got = result, ptout.x, ptout.y
expected = 203, 101, 102
self.failUnlessEqual(got, expected)
def test_struct_return_2H(self):
class S2H(Structure):
_fields_ = [("x", c_short),
("y", c_short)]
dll.ret_2h_func.restype = S2H
dll.ret_2h_func.argtypes = [S2H]
inp = S2H(99, 88)
s2h = dll.ret_2h_func(self.wrap(inp))
self.failUnlessEqual((s2h.x, s2h.y), (99*2, 88*3))
def test_struct_return_8H(self):
class S8I(Structure):
_fields_ = [("a", c_int),
("b", c_int),
("c", c_int),
("d", c_int),
("e", c_int),
("f", c_int),
("g", c_int),
("h", c_int)]
dll.ret_8i_func.restype = S8I
dll.ret_8i_func.argtypes = [S8I]
inp = S8I(9, 8, 7, 6, 5, 4, 3, 2)
s8i = dll.ret_8i_func(self.wrap(inp))
self.failUnlessEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h),
(9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AsParamWrapper(object):
def __init__(self, param):
self._as_parameter_ = param
class AsParamWrapperTestCase(BasicWrapTestCase):
wrap = AsParamWrapper
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AsParamPropertyWrapper(object):
def __init__(self, param):
self._param = param
def getParameter(self):
return self._param
_as_parameter_ = property(getParameter)
class AsParamPropertyWrapperTestCase(BasicWrapTestCase):
wrap = AsParamPropertyWrapper
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if __name__ == '__main__':
unittest.main()
......@@ -19,7 +19,7 @@ def valid_ranges(*types):
result.append((min(a, b, c, d), max(a, b, c, d)))
return result
ArgType = type(c_int(0)._as_parameter_)
ArgType = type(byref(c_int(0)))
unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong]
signed_types = [c_byte, c_short, c_int, c_long, c_longlong]
......@@ -80,19 +80,6 @@ class NumberTestCase(unittest.TestCase):
for t in signed_types + unsigned_types + float_types:
self.failUnlessEqual(ArgType, type(t.from_param(0)))
def test_as_parameter(self):
# The _as_parameter_ property must also
# be a PyCArgObject instance
for t in signed_types + unsigned_types + float_types:
parm = t()._as_parameter_
self.failUnlessEqual(ArgType, type(parm))
# _as_parameter_ is readonly!
#
# Python 2.3 and 2.4 raise a TypeError when trying to set
# a readonly attribute, 2.5 raises an AttributeError.
self.assertRaises((AttributeError, TypeError), setattr, t(), "_as_parameter_", None)
def test_byref(self):
# calling byref returns also a PyCArgObject instance
for t in signed_types + unsigned_types + float_types:
......
......@@ -125,13 +125,18 @@ class CharPointersTestCase(unittest.TestCase):
self.failUnlessEqual(None, func(c_wchar_p(None)))
self.failUnlessEqual(u"123", func(c_wchar_p(u"123")))
## def test_instance(self):
## func = testdll._testfunc_p_p
def test_instance(self):
func = testdll._testfunc_p_p
func.restype = c_void_p
class X:
_as_parameter_ = None
## class X:
## _as_parameter_ = 0
func.argtypes = c_void_p,
self.failUnlessEqual(None, func(X()))
## self.failUnlessEqual(0, func(X()))
func.argtypes = None
self.failUnlessEqual(None, func(X()))
try:
c_wchar
......
......@@ -66,6 +66,10 @@ Library
Extension Modules
-----------------
- Patch #1532975 was applied, which fixes Bug #1533481: ctypes now
uses the _as_parameter_ attribute when objects are passed to foreign
function calls. The ctypes version number was changed to 1.0.1.
- Bug #1530559, struct.pack raises TypeError where it used to convert.
Passing float arguments to struct.pack when integers are expected
now triggers a DeprecationWarning.
......
This diff is collapsed.
......@@ -465,7 +465,21 @@ struct argument {
*/
static int ConvParam(PyObject *obj, int index, struct argument *pa)
{
StgDictObject *dict;
pa->keep = NULL; /* so we cannot forget it later */
dict = PyObject_stgdict(obj);
if (dict) {
PyCArgObject *carg;
assert(dict->paramfunc);
/* If it has an stgdict, it is a CDataObject */
carg = dict->paramfunc((CDataObject *)obj);
pa->ffi_type = carg->pffi_type;
memcpy(&pa->value, &carg->value, sizeof(pa->value));
pa->keep = (PyObject *)carg;
return 0;
}
if (PyCArg_CheckExact(obj)) {
PyCArgObject *carg = (PyCArgObject *)obj;
pa->ffi_type = carg->pffi_type;
......@@ -548,25 +562,12 @@ static int ConvParam(PyObject *obj, int index, struct argument *pa)
as parameters (they have to expose the '_as_parameter_'
attribute)
*/
if (arg == 0) {
PyErr_Format(PyExc_TypeError,
"Don't know how to convert parameter %d", index);
return -1;
}
if (PyCArg_CheckExact(arg)) {
PyCArgObject *carg = (PyCArgObject *)arg;
pa->ffi_type = carg->pffi_type;
memcpy(&pa->value, &carg->value, sizeof(pa->value));
pa->keep = arg;
return 0;
}
if (PyInt_Check(arg)) {
pa->ffi_type = &ffi_type_sint;
pa->value.i = PyInt_AS_LONG(arg);
pa->keep = arg;
return 0;
}
if (arg) {
int result;
result = ConvParam(arg, index, pa);
Py_DECREF(arg);
return result;
}
PyErr_Format(PyExc_TypeError,
"Don't know how to convert parameter %d", index);
return -1;
......
......@@ -23,9 +23,11 @@ typedef int Py_ssize_t;
#define PY_LONG_LONG LONG_LONG
#endif
typedef struct tagPyCArgObject PyCArgObject;
typedef struct tagCDataObject CDataObject;
typedef PyObject *(* GETFUNC)(void *, unsigned size);
typedef PyObject *(* SETFUNC)(void *, PyObject *value, unsigned size);
typedef PyCArgObject *(* PARAMFUNC)(CDataObject *obj);
/* A default buffer in CDataObject, which can be used for small C types. If
this buffer is too small, PyMem_Malloc will be called to create a larger one,
......@@ -205,6 +207,7 @@ typedef struct {
PyObject *proto; /* Only for Pointer/ArrayObject */
SETFUNC setfunc; /* Only for simple objects */
GETFUNC getfunc; /* Only for simple objects */
PARAMFUNC paramfunc;
/* Following fields only used by CFuncPtrType_Type instances */
PyObject *argtypes; /* tuple of CDataObjects */
......@@ -283,7 +286,7 @@ PyObject *_CallProc(PPROC pProc,
#define DICTFLAG_FINAL 0x1000
typedef struct {
struct tagPyCArgObject {
PyObject_HEAD
ffi_type *pffi_type;
char tag;
......@@ -302,7 +305,7 @@ typedef struct {
} value;
PyObject *obj;
int size; /* for the 'V' tag */
} PyCArgObject;
};
extern PyTypeObject PyCArg_Type;
extern PyCArgObject *new_CArgObject(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment