diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 4afa4ebd422493..40ff98ccb44407 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -3,7 +3,7 @@ import os as _os, sys as _sys import types as _types -__version__ = "1.1.0" +__version__ = "1.1.1" from _ctypes import Union, Structure, Array from _ctypes import _Pointer @@ -560,4 +560,18 @@ def DllCanUnloadNow(): elif sizeof(kind) == 8: c_uint64 = kind del(kind) +# _ctypes needs the following types for variadic function type promotion +import _ctypes +_ctypes.c_int = c_int +_ctypes.c_uint = c_uint +_ctypes.c_int8 = c_int8 +_ctypes.c_uint8 = c_uint8 +_ctypes.c_int16 = c_int16 +_ctypes.c_uint16 = c_uint16 +_ctypes.c_int32 = c_int32 +_ctypes.c_uint32 = c_uint32 +_ctypes.c_int64 = c_int64 +_ctypes.c_uint64 = c_uint64 +_ctypes.c_double = c_double + _reset_cache() diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index 75628924206bce..731b4c970dca4d 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -138,6 +138,47 @@ def test_doubleresult(self): self.assertEqual(result, -21) self.assertEqual(type(result), float) + def test_doubleresult_var(self): + f = dll._testfunc_d_bhilfd_var + # This function only supports the following argument types: + # c_byte, c_short, c_int, c_long, c_float, c_double + # Even if it's a variadic function, don't try to pass anything else + f.argtypes = (c_int, ...) + f.restype = c_double + + # builtin types + result = f(0, 1, 2, 3, 4, 5.0, 6.0) + self.assertEqual(result, 21) + self.assertEqual(type(result), float) + + # user defined types with _as_parameter_ + class AsParameter: + def __init__(self, number): + self._as_parameter_ = number + result = f(0, *map(AsParameter, (1, 2, 3, 4, 5.0, 6.0))) + self.assertEqual(result, 21) + self.assertEqual(type(result), float) + + # ctypes without automatic type promotion + result = f( + 0, c_int(1), c_int(2), c_int(3), c_int(4), c_double(5.0), + c_double(6.0) + ) + self.assertEqual(result, 21) + self.assertEqual(type(result), float) + + # ctypes with automatic type promotion + result = f( + 0, c_byte(1), c_int8(2 ** 7 - 1), c_uint32(2 ** 32 - 1), + c_int32(4), c_float(5.0), c_double(6.0) + ) + self.assertEqual(result, 1 + 2 ** 7 - 1 - 1 + 4 + 5. + 6.) + self.assertEqual(type(result), float) + + result = f(0, -1, -2, -3, -4, -5.0, -6.0) + self.assertEqual(result, -21) + self.assertEqual(type(result), float) + def test_longdoubleresult(self): f = dll._testfunc_D_bhilfD f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_longdouble] diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py index 9c137469c3b5a5..42822f681c76b5 100644 --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -68,7 +68,7 @@ def test_PyObj_FromPtr(self): def test_PyOS_snprintf(self): PyOS_snprintf = pythonapi.PyOS_snprintf - PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p + PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p, ... buf = c_buffer(256) PyOS_snprintf(buf, sizeof(buf), b"Hello from %s", b"ctypes") diff --git a/Misc/NEWS.d/next/Library/2020-02-19-11-56-22.bpo-39632.7jCbTV.rst b/Misc/NEWS.d/next/Library/2020-02-19-11-56-22.bpo-39632.7jCbTV.rst new file mode 100644 index 00000000000000..31daae8de1b615 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-02-19-11-56-22.bpo-39632.7jCbTV.rst @@ -0,0 +1,6 @@ +Fix and improve ctypes variadic function support. Functions that take a +variable number of arguments should now be declared using an Ellipsis (...) +as their last argument (just as a prototype for a C function). ctypes variadic +functions declared this way automatically perform the default argument +promotion (small integer types are promoted to c_int and c_float is promoted to +double). diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b31e912933450a..be8b2b20dfdf27 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1060,7 +1060,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *typedict; _Py_IDENTIFIER(_type_); - typedict = PyTuple_GetItem(args, 2); + typedict = PySequence_GetItem(args, 2); if (!typedict) return NULL; /* @@ -1965,13 +1965,13 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject { PyTypeObject *result; StgDictObject *stgdict; - PyObject *name = PyTuple_GET_ITEM(args, 0); + PyObject *name = PySequence_Fast_GET_ITEM(args, 0); PyObject *newname; PyObject *swapped_args; static PyObject *suffix; Py_ssize_t i; - swapped_args = PyTuple_New(PyTuple_GET_SIZE(args)); + swapped_args = PyTuple_New(PySequence_Fast_GET_SIZE(args)); if (!swapped_args) return NULL; @@ -1993,8 +1993,8 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject } PyTuple_SET_ITEM(swapped_args, 0, newname); - for (i=1; i 0) { + value = PyObject_CallOneArg(converter, value); + if (!PyCArg_CheckExact(value)) { + dict = PyObject_stgdict(value); + assert(dict); + assert(dict->paramfunc); + /* If it has an stgdict, it is a CDataObject */ + parg = dict->paramfunc((CDataObject *)value); + } else { + parg = (PyCArgObject*)value; + } + switch (parg->tag) { + case 'b': case 'c': case 'h': + fd = _ctypes_get_fielddesc("i"); + parg->tag = 'i'; + parg->pffi_type = fd->pffi_type; + Py_XDECREF(parg->obj); + if (parg->tag == 'h') + parg->obj = fd->setfunc( + &parg->value, PyLong_FromLong(parg->value.h), 0); + else + parg->obj = fd->setfunc( + &parg->value, PyLong_FromLong(parg->value.b), 0); + break; + case 'B': case 'H': + fd = _ctypes_get_fielddesc("i"); + parg->tag = 'i'; + parg->pffi_type = fd->pffi_type; + Py_XDECREF(parg->obj); + if (parg->tag == 'H') + parg->obj = fd->setfunc( + &parg->value, PyLong_FromLong(parg->value.h), 0); + else + parg->obj = fd->setfunc( + &parg->value, PyLong_FromLong(parg->value.b), 0); + break; + case 'f': + fd = _ctypes_get_fielddesc("d"); + parg->tag = 'd'; + parg->pffi_type = fd->pffi_type; + Py_XDECREF(parg->obj); + parg->obj = fd->setfunc( + &parg->value, PyFloat_FromDouble(parg->value.f), 0); + break; + default: + break; + } + value = (PyObject*)parg; + } else if (_PyObject_LookupAttrId( + value, &PyId__as_parameter_, &as_parameter) > 0) { + if (Py_EnterRecursiveCall("while processing _as_parameter_")) { + Py_DECREF(as_parameter); + return NULL; + } + value = variadic_param_promotion(self, as_parameter); + Py_LeaveRecursiveCall(); + Py_DECREF(as_parameter); + } + return value; +} + +static PyMethodDef variadic_param_promotion_ml = { + "variadic_param_promotion", + variadic_param_promotion, + METH_O, +}; + + /******************************************************************/ /* PyCFuncPtrType_Type @@ -2444,7 +2539,14 @@ converters_from_argtypes(PyObject *ob) } */ - if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) { + if (tp == Py_Ellipsis) { + if (i != nArgs - 1) { + PyErr_Format(PyExc_TypeError, + "Ellipsis (...) must be the last item of _argtypes_"); + } + Py_INCREF(VariadicParamPromotionFunc); + PyTuple_SET_ITEM(converters, i, VariadicParamPromotionFunc); + } else if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) { Py_DECREF(converters); Py_DECREF(ob); if (!PyErr_Occurred()) { @@ -2453,8 +2555,9 @@ converters_from_argtypes(PyObject *ob) i+1); } return NULL; + } else { + PyTuple_SET_ITEM(converters, i, cnv); } - PyTuple_SET_ITEM(converters, i, cnv); } Py_DECREF(ob); return converters; @@ -3472,7 +3575,7 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) } len = PyTuple_GET_SIZE(paramflags); - if (len != PyTuple_GET_SIZE(dict->argtypes)) { + if (len != PySequence_Fast_GET_SIZE(dict->argtypes)) { PyErr_SetString(PyExc_ValueError, "paramflags must have the same length as argtypes"); return 0; @@ -3491,7 +3594,7 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) "paramflags must be a sequence of (int [,string [,value]]) tuples"); return 0; } - typ = PyTuple_GET_ITEM(argtypes, i); + typ = PySequence_Fast_GET_ITEM(argtypes, i); switch (flag & (PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID)) { case 0: case PARAMFLAG_FIN: @@ -3708,21 +3811,23 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) StgDictObject *dict; CThunkObject *thunk; - if (PyTuple_GET_SIZE(args) == 0) + if (PySequence_Fast_GET_SIZE(args) == 0) return GenericPyCData_new(type, args, kwds); - if (1 <= PyTuple_GET_SIZE(args) && PyTuple_Check(PyTuple_GET_ITEM(args, 0))) + if (1 <= PySequence_Fast_GET_SIZE(args) + && PyTuple_Check(PySequence_Fast_GET_ITEM(args, 0))) return PyCFuncPtr_FromDll(type, args, kwds); #ifdef MS_WIN32 - if (2 <= PyTuple_GET_SIZE(args) && PyLong_Check(PyTuple_GET_ITEM(args, 0))) + if (2 <= PySequence_Fast_GET_SIZE(args) + && PyLong_Check(PySequence_Fast_GET_ITEM(args, 0))) return PyCFuncPtr_FromVtblIndex(type, args, kwds); #endif - if (1 == PyTuple_GET_SIZE(args) - && (PyLong_Check(PyTuple_GET_ITEM(args, 0)))) { + if (1 == PySequence_Fast_GET_SIZE(args) + && (PyLong_Check(PySequence_Fast_GET_ITEM(args, 0)))) { CDataObject *ob; - void *ptr = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 0)); + void *ptr = PyLong_AsVoidPtr(PySequence_Fast_GET_ITEM(args, 0)); if (ptr == NULL && PyErr_Occurred()) return NULL; ob = (CDataObject *)GenericPyCData_new(type, args, kwds); @@ -3824,8 +3929,8 @@ _get_arg(int *pindex, PyObject *name, PyObject *defval, PyObject *inargs, PyObje { PyObject *v; - if (*pindex < PyTuple_GET_SIZE(inargs)) { - v = PyTuple_GET_ITEM(inargs, *pindex); + if (*pindex < PySequence_Fast_GET_SIZE(inargs)) { + v = PySequence_Fast_GET_ITEM(inargs, *pindex); ++*pindex; Py_INCREF(v); return v; @@ -3876,10 +3981,11 @@ _get_arg(int *pindex, PyObject *name, PyObject *defval, PyObject *inargs, PyObje static PyObject * _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, PyObject *inargs, PyObject *kwds, - int *poutmask, int *pinoutmask, unsigned int *pnumretvals) + int *poutmask, int *pinoutmask, unsigned int *pnumretvals, + Py_ssize_t *fixed_argcount) { PyObject *paramflags = self->paramflags; - PyObject *callargs; + PyObject *callargs = NULL; StgDictObject *dict; Py_ssize_t i, len; int inargs_index = 0; @@ -3892,17 +3998,36 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, *pinoutmask = 0; *pnumretvals = 0; + len = argtypes ? PySequence_Fast_GET_SIZE(argtypes) : 0; + *fixed_argcount = len; + if (len > 0 && PySequence_Fast_GET_ITEM(argtypes, len - 1) == Py_Ellipsis) { + Py_INCREF(Py_Ellipsis); + *fixed_argcount = len - 1; + len = PySequence_Fast_GET_SIZE(inargs); + if (len < *fixed_argcount) { + PyErr_Format( + PyExc_TypeError, + "this function takes at least %d argument%s (%d given)", + *fixed_argcount, + *fixed_argcount == 1 ? "" : "s", + len); + goto error; + } + } + /* Trivial cases, where we either return inargs itself, or a slice of it. */ - if (argtypes == NULL || paramflags == NULL || PyTuple_GET_SIZE(argtypes) == 0) { + if (argtypes == NULL + || (paramflags == NULL && *fixed_argcount == len) + || len == 0) { #ifdef MS_WIN32 if (self->index) - return PyTuple_GetSlice(inargs, 1, PyTuple_GET_SIZE(inargs)); + return PyTuple_GetSlice(inargs, 1, PySequence_Fast_GET_SIZE(inargs)); #endif Py_INCREF(inargs); return inargs; } - len = PyTuple_GET_SIZE(argtypes); + callargs = PyTuple_New(len); /* the argument tuple we build */ if (callargs == NULL) return NULL; @@ -3914,7 +4039,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, } #endif for (i = 0; i < len; ++i) { - PyObject *item = PyTuple_GET_ITEM(paramflags, i); + PyObject *item = paramflags ? PyTuple_GET_ITEM(paramflags, i) : NULL; PyObject *ob; unsigned int flag; PyObject *name = NULL; @@ -3923,8 +4048,10 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, /* This way seems to be ~2 us faster than the PyArg_ParseTuple calls below. */ /* We HAVE already checked that the tuple can be parsed with "i|ZO", so... */ - Py_ssize_t tsize = PyTuple_GET_SIZE(item); - flag = PyLong_AsUnsignedLongMask(PyTuple_GET_ITEM(item, 0)); + Py_ssize_t tsize = item ? PyTuple_GET_SIZE(item) : 0; + flag = item ? + PyLong_AsUnsignedLongMask(PyTuple_GET_ITEM(item, 0)) + : PARAMFLAG_FIN; name = tsize > 1 ? PyTuple_GET_ITEM(item, 1) : NULL; defval = tsize > 2 ? PyTuple_GET_ITEM(item, 2) : NULL; @@ -3948,6 +4075,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, ob =_get_arg(&inargs_index, name, defval, inargs, kwds); if (ob == NULL) goto error; + Py_INCREF(ob); PyTuple_SET_ITEM(callargs, i, ob); break; case PARAMFLAG_FOUT: @@ -3968,7 +4096,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, (*pnumretvals)++; break; } - ob = PyTuple_GET_ITEM(argtypes, i); + ob = PySequence_Fast_GET_ITEM(argtypes, i); dict = PyType_stgdict(ob); if (dict == NULL) { /* Cannot happen: _validate_paramflags() @@ -4015,7 +4143,8 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, must be the same as len(inargs) + len(kwds), otherwise we have either too much or not enough arguments. */ - actual_args = PyTuple_GET_SIZE(inargs) + (kwds ? PyDict_GET_SIZE(kwds) : 0); + actual_args = (PySequence_Fast_GET_SIZE(inargs) + + (kwds ? PyDict_GET_SIZE(kwds) : 0)); if (actual_args != inargs_index) { /* When we have default values or named parameters, this error message is misleading. See unittests/test_paramflags.py @@ -4031,7 +4160,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, */ return callargs; error: - Py_DECREF(callargs); + Py_XDECREF(callargs); return NULL; } @@ -4073,7 +4202,7 @@ _build_result(PyObject *result, PyObject *callargs, for (bit = 1, i = 0; i < 32; ++i, bit <<= 1) { PyObject *v; if (bit & inoutmask) { - v = PyTuple_GET_ITEM(callargs, i); + v = PySequence_Fast_GET_ITEM(callargs, i); Py_INCREF(v); if (numretvals == 1) { Py_DECREF(callargs); @@ -4084,7 +4213,7 @@ _build_result(PyObject *result, PyObject *callargs, } else if (bit & outmask) { _Py_IDENTIFIER(__ctypes_from_outparam__); - v = PyTuple_GET_ITEM(callargs, i); + v = PySequence_Fast_GET_ITEM(callargs, i); v = _PyObject_CallMethodIdNoArgs(v, &PyId___ctypes_from_outparam__); if (v == NULL || numretvals == 1) { Py_DECREF(callargs); @@ -4120,6 +4249,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) int inoutmask; int outmask; unsigned int numretvals; + Py_ssize_t fixed_argcount; assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ restype = self->restype ? self->restype : dict->restype; @@ -4135,7 +4265,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) if (self->index) { /* It's a COM method */ CDataObject *this; - this = (CDataObject *)PyTuple_GetItem(inargs, 0); /* borrowed ref! */ + this = (CDataObject *)PySequence_GetItem(inargs, 0); /* borrowed ref! */ if (!this) { PyErr_SetString(PyExc_ValueError, "native com method call without 'this' parameter"); @@ -4164,35 +4294,33 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) #endif callargs = _build_callargs(self, argtypes, inargs, kwds, - &outmask, &inoutmask, &numretvals); + &outmask, &inoutmask, &numretvals, &fixed_argcount); if (callargs == NULL) return NULL; if (converters) { - int required = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(converters), - Py_ssize_t, int); - int actual = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(callargs), + int actual = Py_SAFE_DOWNCAST(PySequence_Fast_GET_SIZE(callargs), Py_ssize_t, int); if ((dict->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) { /* For cdecl functions, we allow more actual arguments than the length of the argtypes tuple. */ - if (required > actual) { + if (fixed_argcount > actual) { Py_DECREF(callargs); PyErr_Format(PyExc_TypeError, "this function takes at least %d argument%s (%d given)", - required, - required == 1 ? "" : "s", + fixed_argcount, + fixed_argcount == 1 ? "" : "s", actual); return NULL; } - } else if (required != actual) { + } else if (fixed_argcount != actual) { Py_DECREF(callargs); PyErr_Format(PyExc_TypeError, "this function takes %d argument%s (%d given)", - required, - required == 1 ? "" : "s", + fixed_argcount, + fixed_argcount == 1 ? "" : "s", actual); return NULL; } @@ -4388,7 +4516,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, } for (i = 0; - i < dict->length && (i+index) < PyTuple_GET_SIZE(args); + i < dict->length && (i+index) < PySequence_Fast_GET_SIZE(args); ++i) { PyObject *pair = PySequence_GetItem(fields, i); PyObject *name, *val; @@ -4400,7 +4528,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, Py_DECREF(pair); return -1; } - val = PyTuple_GET_ITEM(args, i + index); + val = PySequence_Fast_GET_ITEM(args, i + index); if (kwds) { res = PyDict_Contains(kwds, name); if (res != 0) { @@ -4435,12 +4563,12 @@ Struct_init(PyObject *self, PyObject *args, PyObject *kwds) "args not a tuple?"); return -1; } - if (PyTuple_GET_SIZE(args)) { + if (PySequence_Fast_GET_SIZE(args)) { Py_ssize_t res = _init_pos_args(self, Py_TYPE(self), args, kwds, 0); if (res == -1) return -1; - if (res < PyTuple_GET_SIZE(args)) { + if (res < PySequence_Fast_GET_SIZE(args)) { PyErr_SetString(PyExc_TypeError, "too many initializers"); return -1; @@ -4558,10 +4686,10 @@ Array_init(CDataObject *self, PyObject *args, PyObject *kw) "args not a tuple?"); return -1; } - n = PyTuple_GET_SIZE(args); + n = PySequence_Fast_GET_SIZE(args); for (i = 0; i < n; ++i) { PyObject *v; - v = PyTuple_GET_ITEM(args, i); + v = PySequence_Fast_GET_ITEM(args, i); if (-1 == PySequence_SetItem((PyObject *)self, i, v)) return -1; } @@ -5489,7 +5617,7 @@ comerror_init(PyObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTuple(args, "OOO:COMError", &hresult, &text, &details)) return -1; - a = PySequence_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + a = PySequence_GetSlice(args, 1, PySequence_Fast_GET_SIZE(args)); if (!a) return -1; status = PyObject_SetAttrString(self, "args", a); @@ -5782,7 +5910,7 @@ _ctypes_add_objects(PyObject *mod) MOD_ADD("FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO)); MOD_ADD("FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR)); MOD_ADD("FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI)); - MOD_ADD("__version__", PyUnicode_FromString("1.1.0")); + MOD_ADD("__version__", PyUnicode_FromString("1.1.1")); MOD_ADD("_memmove_addr", PyLong_FromVoidPtr(memmove)); MOD_ADD("_memset_addr", PyLong_FromVoidPtr(memset)); @@ -5821,6 +5949,15 @@ _ctypes_mod_exec(PyObject *mod) return -1; } + VariadicParamPromotionFunc = PyCFunction_New(&variadic_param_promotion_ml, NULL); + if (!VariadicParamPromotionFunc) + return -1; + + _ctypes_dict = PyModule_GetDict(mod); + if (_ctypes_dict == NULL) { + return -1; + } + PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL); if (!PyExc_ArgError) { return -1; diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 1ccad8e0e3d64d..5ad2cc7f09740e 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -341,6 +341,29 @@ EXPORT(double) _testfunc_d_bhilfd(signed char b, short h, int i, long l, float f return (double)(b + h + i + l + f + d); } +EXPORT(double) _testfunc_d_bhilfd_var(int dummy, ...) +{ + signed char b; + short h; + int i; + long l; + float f; + double d; + va_list argptr; + va_start(argptr, dummy); + b = (signed char)va_arg(argptr, int); + h = (short)va_arg(argptr, int); + i = va_arg(argptr, int); + l = va_arg(argptr, long); + f = (float)va_arg(argptr, double); + d = va_arg(argptr, double); + va_end(argptr); +/* printf("_testfunc_d_bhilfd_var got %d %d %d %ld %f %f\n", + b, h, i, l, f, d); +*/ + return (double)(b + h + i + l + f + d); +} + EXPORT(long double) _testfunc_D_bhilfD(signed char b, short h, int i, long l, float f, long double d) { /* printf("_testfunc_d_bhilfd got %d %d %d %ld %f %f\n", diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 56ccc2f1e0b5da..86a720e1dc7eba 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1195,9 +1195,13 @@ PyObject *_ctypes_callproc(PPROC pProc, than the length of the argtypes tuple. This is checked in _ctypes::PyCFuncPtr_Call */ - if (argtypes && argtype_count > i) { + if (argtypes && (argtype_count > i || argtype_count != argcount)) { PyObject *v; - converter = PyTuple_GET_ITEM(argtypes, i); + if (i < argtype_count) { + converter = PyTuple_GET_ITEM(argtypes, i); + } else { + converter = PyTuple_GET_ITEM(argtypes, argtype_count -1); + } v = PyObject_CallOneArg(converter, arg); if (v == NULL) { _ctypes_extend_error(PyExc_ArgError, "argument %zd: ", i+1); @@ -1631,8 +1635,8 @@ static const char sizeof_doc[] = "sizeof(C instance) -> integer\n" "Return the size in bytes of a C instance"; -static PyObject * -sizeof_func(PyObject *self, PyObject *obj) +PyObject * +_ctypes_sizeof_func(PyObject *self, PyObject *obj) { StgDictObject *dict; @@ -1996,7 +2000,7 @@ PyMethodDef _ctypes_module_methods[] = { {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, #endif {"alignment", align_func, METH_O, alignment_doc}, - {"sizeof", sizeof_func, METH_O, sizeof_doc}, + {"sizeof", _ctypes_sizeof_func, METH_O, sizeof_doc}, {"byref", byref, METH_VARARGS, byref_doc}, {"addressof", addressof, METH_O, addressof_doc}, {"call_function", call_function, METH_VARARGS }, diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 6110027980827c..790e3e112e2a2d 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -278,6 +278,9 @@ PyObject *_ctypes_callproc(PPROC pProc, PyObject *restype, PyObject *checker); +PyObject * +_ctypes_sizeof_func(PyObject *self, PyObject *obj); + #define FUNCFLAG_STDCALL 0x0 #define FUNCFLAG_CDECL 0x1