Hello community, here is the log from the commit of package python3-cffi for openSUSE:Factory checked in at 2016-11-18 22:00:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-cffi (Old) and /work/SRC/openSUSE:Factory/.python3-cffi.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-cffi" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-cffi/python3-cffi-doc.changes 2016-09-20 13:24:53.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-cffi.new/python3-cffi-doc.changes 2016-11-18 22:00:16.000000000 +0100 @@ -1,0 +2,35 @@ +Tue Nov 15 23:55:38 UTC 2016 - a...@gmx.de + +- update to version 1.9.1: + (no changelog available) + +- changes from version 1.9: + * Structs with variable-sized arrays as their last field: now we + track the length of the array after ffi.new() is called, just like + we always tracked the length of ffi.new("int[]", 42). This lets us + detect out-of-range accesses to array items. This also lets us + display a better repr(), and have the total size returned by + ffi.sizeof() and ffi.buffer(). Previously both functions would + return a result based on the size of the declared structure type, + with an assumed empty array. (Thanks andrew for starting this + refactoring.) + * Add support in cdef()/set_source() for unspecified-length arrays + in typedefs: typedef int foo_t[...];. It was already supported for + global variables or structure fields. + * I turned in v1.8 a warning from cffi/model.py into an error: 'enum + xxx' has no values explicitly defined: refusing to guess which + integer type it is meant to be (unsigned/signed, int/long). Now + I’m turning it back to a warning again; it seems that guessing + that the enum has size int is a 99%-safe bet. (But not 100%, so it + stays as a warning.) + * Fix leaks in the code handling FILE * arguments. In CPython 3 + there is a remaining issue that is hard to fix: if you pass a + Python file object to a FILE * argument, then os.dup() is used and + the new file descriptor is only closed when the GC reclaims the + Python file object—and not at the earlier time when you call + close(), which only closes the original file descriptor. If this + is an issue, you should avoid this automatic convertion of Python + file objects: instead, explicitly manipulate file descriptors and + call fdopen() from C (...via cffi). + +------------------------------------------------------------------- python3-cffi.changes: same change Old: ---- cffi-1.8.3.tar.gz New: ---- cffi-1.9.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-cffi-doc.spec ++++++ --- /var/tmp/diff_new_pack.iFQMqf/_old 2016-11-18 22:00:17.000000000 +0100 +++ /var/tmp/diff_new_pack.iFQMqf/_new 2016-11-18 22:00:17.000000000 +0100 @@ -17,7 +17,7 @@ Name: python3-cffi-doc -Version: 1.8.3 +Version: 1.9.1 Release: 0 Summary: Documentation for python3-cffi License: MIT python3-cffi.spec: same change ++++++ cffi-1.8.3.tar.gz -> cffi-1.9.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/PKG-INFO new/cffi-1.9.1/PKG-INFO --- old/cffi-1.8.3/PKG-INFO 2016-09-17 12:21:52.000000000 +0200 +++ new/cffi-1.9.1/PKG-INFO 2016-11-12 14:46:13.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.8.3 +Version: 1.9.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/_cffi_backend.c new/cffi-1.9.1/c/_cffi_backend.c --- old/cffi-1.8.3/c/_cffi_backend.c 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/_cffi_backend.c 2016-11-12 14:45:24.000000000 +0100 @@ -2,7 +2,7 @@ #include <Python.h> #include "structmember.h" -#define CFFI_VERSION "1.8.3" +#define CFFI_VERSION "1.9.1" #ifdef MS_WIN32 #include <windows.h> @@ -132,7 +132,7 @@ #define CT_PRIMITIVE_FITS_LONG 2048 #define CT_IS_OPAQUE 4096 #define CT_IS_ENUM 8192 -#define CT_IS_PTR_TO_OWNED 16384 +#define CT_IS_PTR_TO_OWNED 16384 /* only owned if CDataOwning_Type */ #define CT_CUSTOM_FIELD_POS 32768 #define CT_IS_LONGDOUBLE 65536 #define CT_IS_BOOL 131072 @@ -190,9 +190,9 @@ unsigned char cf_flags; /* BF_... */ struct cfieldobject_s *cf_next; } CFieldObject; -#define BS_REGULAR (-1) /* a regular field, not with bitshift */ -#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */ -#define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */ +#define BS_REGULAR (-1) /* a regular field, not with bitshift */ +#define BS_EMPTY_ARRAY (-2) /* a field declared 'type[0]' or 'type[]' */ +#define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */ static PyTypeObject CTypeDescr_Type; static PyTypeObject CField_Type; @@ -266,6 +266,9 @@ Py_ssize_t exchange_offset_arg[1]; } cif_description_t; +#define ADD_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) + ((size_t)(y)))) +#define MUL_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) * ((size_t)(y)))) + /* whenever running Python code, the errno is saved in this thread-local variable */ @@ -905,6 +908,23 @@ return (PyObject *)cd; } +static PyObject * +new_sized_cdata(char *data, CTypeDescrObject *ct, Py_ssize_t length) +{ + CDataObject_own_length *scd; + + scd = (CDataObject_own_length *)PyObject_Malloc( + offsetof(CDataObject_own_length, alignment)); + if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) + return NULL; + Py_INCREF(ct); + scd->head.c_type = ct; + scd->head.c_data = data; + scd->head.c_weakreflist = NULL; + scd->length = length; + return (PyObject *)scd; +} + static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/ static PyObject * @@ -1161,7 +1181,8 @@ Py_ssize_t size, itemsize; assert(data == NULL); itemsize = cf->cf_type->ct_itemdescr->ct_size; - size = cf->cf_offset + itemsize * varsizelength; + size = ADD_WRAPAROUND(cf->cf_offset, + MUL_WRAPAROUND(itemsize, varsizelength)); if (size < 0 || ((size - cf->cf_offset) / itemsize) != varsizelength) { PyErr_SetString(PyExc_OverflowError, @@ -1827,16 +1848,34 @@ return res; } -static PyObject *cdataowning_repr(CDataObject *cd) +static Py_ssize_t _cdata_var_byte_size(CDataObject *cd) { - Py_ssize_t size; - if (cd->c_type->ct_flags & CT_POINTER) - size = cd->c_type->ct_itemdescr->ct_size; - else if (cd->c_type->ct_flags & CT_ARRAY) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; - else - size = cd->c_type->ct_size; + /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with + ffi.new(), and if the struct foo contains a varsize array, + then return the real allocated size. Otherwise, return -1. */ + if (!CDataOwn_Check(cd)) + return -1; + if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { + cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj; + } + if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) { + return ((CDataObject_own_length *)cd)->length; + } + return -1; +} + +static PyObject *cdataowning_repr(CDataObject *cd) +{ + Py_ssize_t size = _cdata_var_byte_size(cd); + if (size < 0) { + if (cd->c_type->ct_flags & CT_POINTER) + size = cd->c_type->ct_itemdescr->ct_size; + else if (cd->c_type->ct_flags & CT_ARRAY) + size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + else + size = cd->c_type->ct_size; + } return PyText_FromFormat("<cdata '%s' owning %zd bytes>", cd->c_type->ct_name, size); } @@ -2114,8 +2153,8 @@ static PyObject * cdata_slice(CDataObject *cd, PySliceObject *slice) { + char *cdata; Py_ssize_t bounds[2]; - CDataObject_own_length *scd; CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); if (ct == NULL) return NULL; @@ -2127,16 +2166,8 @@ } ct = (CTypeDescrObject *)ct->ct_stuff; - scd = (CDataObject_own_length *)PyObject_Malloc( - offsetof(CDataObject_own_length, alignment)); - if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) - return NULL; - Py_INCREF(ct); - scd->head.c_type = ct; - scd->head.c_data = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; - scd->head.c_weakreflist = NULL; - scd->length = bounds[1]; - return (PyObject *)scd; + cdata = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; + return new_sized_cdata(cdata, ct, bounds[1]); } static int @@ -2401,13 +2432,25 @@ if (cf != NULL) { /* read the field 'cf' */ char *data = cd->c_data + cf->cf_offset; - if (cf->cf_bitshift == BS_REGULAR) + Py_ssize_t array_len, size; + + if (cf->cf_bitshift == BS_REGULAR) { return convert_to_object(data, cf->cf_type); - else if (cf->cf_bitshift == BS_EMPTY_ARRAY) - return new_simple_cdata(data, - (CTypeDescrObject *)cf->cf_type->ct_stuff); - else + } + else if (cf->cf_bitshift != BS_EMPTY_ARRAY) { return convert_to_object_bitfield(data, cf); + } + + /* variable-length array: */ + /* if reading variable length array from variable length + struct, calculate array type from allocated length */ + size = _cdata_var_byte_size(cd) - cf->cf_offset; + if (size >= 0) { + array_len = size / cf->cf_type->ct_itemdescr->ct_size; + return new_sized_cdata(data, cf->cf_type, array_len); + } + return new_simple_cdata(data, + (CTypeDescrObject *)cf->cf_type->ct_stuff); } break; case -1: @@ -2527,7 +2570,7 @@ if (ctitem->ct_size <= 0) goto convert_default; - datasize = length * ctitem->ct_size; + datasize = MUL_WRAPAROUND(length, ctitem->ct_size); if ((datasize / ctitem->ct_size) != length) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); @@ -3062,6 +3105,10 @@ "return type is an opaque structure or union"); return NULL; } + if (ct->ct_flags & CT_WITH_VAR_ARRAY) { + PyErr_SetString(PyExc_TypeError, + "return type is a struct/union with a varsize array member"); + } cd = allocate_owning_object(dataoffset + datasize, ct); if (cd == NULL) return NULL; @@ -3160,15 +3207,21 @@ if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) datasize *= 2; /* forcefully add another character: a null */ - if ((ctitem->ct_flags & (CT_STRUCT | CT_UNION)) && init != Py_None) { + if (ctitem->ct_flags & (CT_STRUCT | CT_UNION)) { if (force_lazy_struct(ctitem) < 0) /* for CT_WITH_VAR_ARRAY */ return NULL; + if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) { - Py_ssize_t optvarsize = datasize; - if (convert_struct_from_object(NULL,ctitem, init, - &optvarsize) < 0) - return NULL; - datasize = optvarsize; + assert(ct->ct_flags & CT_IS_PTR_TO_OWNED); + dataoffset = offsetof(CDataObject_own_length, alignment); + + if (init != Py_None) { + Py_ssize_t optvarsize = datasize; + if (convert_struct_from_object(NULL, ctitem, init, + &optvarsize) < 0) + return NULL; + datasize = optvarsize; + } } } } @@ -3181,7 +3234,7 @@ return NULL; ctitem = ct->ct_itemdescr; dataoffset = offsetof(CDataObject_own_length, alignment); - datasize = explicitlength * ctitem->ct_size; + datasize = MUL_WRAPAROUND(explicitlength, ctitem->ct_size); if (explicitlength > 0 && (datasize / explicitlength) != ctitem->ct_size) { PyErr_SetString(PyExc_OverflowError, @@ -3216,6 +3269,10 @@ } /* store the only reference to cds into cd */ ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; + /* store information about the allocated size of the struct */ + if (dataoffset == offsetof(CDataObject_own_length, alignment)) { + ((CDataObject_own_length *)cds)->length = datasize; + } assert(explicitlength < 0); cd->c_data = cds->c_data; @@ -4014,7 +4071,7 @@ } else { sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length); - arraysize = length * ctitem->ct_size; + arraysize = MUL_WRAPAROUND(length, ctitem->ct_size); if (length > 0 && (arraysize / length) != ctitem->ct_size) { PyErr_SetString(PyExc_OverflowError, "array size would overflow a Py_ssize_t"); @@ -4287,7 +4344,7 @@ /* not a bitfield: common case */ int bs_flag; - if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0) + if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length <= 0) bs_flag = BS_EMPTY_ARRAY; else bs_flag = BS_REGULAR; @@ -5425,17 +5482,27 @@ return PyInt_FromLong(align); } +static Py_ssize_t direct_sizeof_cdata(CDataObject *cd) +{ + Py_ssize_t size; + if (cd->c_type->ct_flags & CT_ARRAY) + size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + else { + size = -1; + if (cd->c_type->ct_flags & (CT_STRUCT | CT_UNION)) + size = _cdata_var_byte_size(cd); + if (size < 0) + size = cd->c_type->ct_size; + } + return size; +} + static PyObject *b_sizeof(PyObject *self, PyObject *arg) { Py_ssize_t size; if (CData_Check(arg)) { - CDataObject *cd = (CDataObject *)arg; - - if (cd->c_type->ct_flags & CT_ARRAY) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; - else - size = cd->c_type->ct_size; + size = direct_sizeof_cdata((CDataObject *)arg); } else if (CTypeDescr_Check(arg)) { size = ((CTypeDescrObject *)arg)->ct_size; @@ -5516,7 +5583,7 @@ return NULL; } res = ct->ct_itemdescr; - *offset = index * ct->ct_itemdescr->ct_size; + *offset = MUL_WRAPAROUND(index, ct->ct_itemdescr->ct_size); if ((*offset / ct->ct_itemdescr->ct_size) != index) { PyErr_SetString(PyExc_OverflowError, "array offset would overflow a Py_ssize_t"); @@ -5829,6 +5896,9 @@ &CData_Type, &cd, &size)) return NULL; + if (size < 0) + size = _cdata_var_byte_size(cd); + if (cd->c_type->ct_flags & CT_POINTER) { if (size < 0) size = cd->c_type->ct_itemdescr->ct_size; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/ffi_obj.c new/cffi-1.9.1/c/ffi_obj.c --- old/cffi-1.8.3/c/ffi_obj.c 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/ffi_obj.c 2016-11-12 14:45:16.000000000 +0100 @@ -257,22 +257,20 @@ static PyObject *ffi_sizeof(FFIObject *self, PyObject *arg) { Py_ssize_t size; - CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); - if (ct == NULL) - return NULL; - - size = ct->ct_size; if (CData_Check(arg)) { - CDataObject *cd = (CDataObject *)arg; - if (cd->c_type->ct_flags & CT_ARRAY) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + size = direct_sizeof_cdata((CDataObject *)arg); } - - if (size < 0) { - PyErr_Format(FFIError, "don't know the size of ctype '%s'", - ct->ct_name); - return NULL; + else { + CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); + if (ct == NULL) + return NULL; + size = ct->ct_size; + if (size < 0) { + PyErr_Format(FFIError, "don't know the size of ctype '%s'", + ct->ct_name); + return NULL; + } } return PyInt_FromSsize_t(size); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/file_emulator.h new/cffi-1.9.1/c/file_emulator.h --- old/cffi-1.8.3/c/file_emulator.h 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/file_emulator.h 2016-11-12 14:45:16.000000000 +0100 @@ -31,7 +31,7 @@ static FILE *PyFile_AsFile(PyObject *ob_file) { PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL; - FILE *f = NULL; + FILE *f; int fd; char *mode; @@ -80,7 +80,11 @@ if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0) goto fail; } - return PyCapsule_GetPointer(ob_capsule, "FILE"); + else { + f = PyCapsule_GetPointer(ob_capsule, "FILE"); + } + Py_DECREF(ob_capsule); /* assumes still at least one reference */ + return f; fail: Py_XDECREF(ob_mode); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/libffi_msvc/ffi.h new/cffi-1.9.1/c/libffi_msvc/ffi.h --- old/cffi-1.8.3/c/libffi_msvc/ffi.h 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/libffi_msvc/ffi.h 2016-11-12 14:45:16.000000000 +0100 @@ -231,6 +231,9 @@ void *user_data, void *codeloc); +/* AR: for cffi we need the following API, and not the _loc version */ +#define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) + typedef struct { char tramp[FFI_TRAMPOLINE_SIZE]; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/misc_win32.h new/cffi-1.9.1/c/misc_win32.h --- old/cffi-1.8.3/c/misc_win32.h 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/misc_win32.h 2016-11-12 14:45:16.000000000 +0100 @@ -234,8 +234,3 @@ sprintf(buf, "error 0x%x", (unsigned int)dw); return buf; } - -/************************************************************/ -/* obscure */ - -#define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/c/test_c.py new/cffi-1.9.1/c/test_c.py --- old/cffi-1.8.3/c/test_c.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/c/test_c.py 2016-11-12 14:45:24.000000000 +0100 @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.8.3", ("This test_c.py file is for testing a version" +assert __version__ == "1.9.1", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -3172,17 +3172,19 @@ assert d[1][0] == 'y' assert d[1][1].type is BArray assert d[1][1].offset == size_of_int() - assert d[1][1].bitshift == -1 + assert d[1][1].bitshift == -2 assert d[1][1].bitsize == -1 # p = newp(new_pointer_type(BStruct)) p.x = 42 assert p.x == 42 - assert typeof(p.y) is BIntP + assert typeof(p.y) is BArray + assert len(p.y) == 0 assert p.y == cast(BIntP, p) + 1 # p = newp(new_pointer_type(BStruct), [100]) assert p.x == 100 + assert len(p.y) == 0 # # Tests for # ffi.new("struct_with_var_array *", [field.., [the_array_items..]]) @@ -3197,6 +3199,10 @@ p.y[0] = 200 assert p.y[2] == 0 p.y[2] = 400 + assert len(p.y) == 3 + assert len(p[0].y) == 3 + assert len(buffer(p)) == sizeof(BInt) * 4 + assert sizeof(p[0]) == sizeof(BInt) * 4 plist.append(p) for i in range(20): p = plist[i] @@ -3204,13 +3210,31 @@ assert p.y[0] == 200 assert p.y[1] == i assert p.y[2] == 400 - assert list(p.y[0:3]) == [200, i, 400] + assert list(p.y) == [200, i, 400] # # the following assignment works, as it normally would, for any array field - p.y = [500, 600] - assert list(p.y[0:3]) == [500, 600, 400] + p.y = [501, 601] + assert list(p.y) == [501, 601, 400] + p[0].y = [500, 600] + assert list(p[0].y) == [500, 600, 400] + assert repr(p) == "<cdata 'foo *' owning %d bytes>" % ( + sizeof(BStruct) + 3 * sizeof(BInt),) + assert repr(p[0]) == "<cdata 'foo' owning %d bytes>" % ( + sizeof(BStruct) + 3 * sizeof(BInt),) + assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt) + # + # from a non-owning pointer, we can't get the length + q = cast(new_pointer_type(BStruct), p) + assert q.y[0] == 500 + assert q[0].y[0] == 500 + py.test.raises(TypeError, len, q.y) + py.test.raises(TypeError, len, q[0].y) + assert typeof(q.y) is BIntP + assert typeof(q[0].y) is BIntP + assert sizeof(q[0]) == sizeof(BStruct) # # error cases + py.test.raises(IndexError, "p.y[4]") py.test.raises(TypeError, "p.y = cast(BIntP, 0)") py.test.raises(TypeError, "p.y = 15") py.test.raises(TypeError, "p.y = None") @@ -3275,6 +3299,33 @@ assert p.x[5] == 60 assert p.x[6] == 70 +def test_struct_array_not_aligned(): + # struct a { int x; char y; char z[]; }; + # ends up of size 8, but 'z' is at offset 5 + BChar = new_primitive_type("char") + BInt = new_primitive_type("int") + BCharP = new_pointer_type(BChar) + BArray = new_array_type(BCharP, None) + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('x', BInt), + ('y', BChar), + ('z', BArray)]) + assert sizeof(BStruct) == 2 * size_of_int() + def offsetof(BType, fieldname): + return typeoffsetof(BType, fieldname)[1] + base = offsetof(BStruct, 'z') + assert base == size_of_int() + 1 + # + p = newp(new_pointer_type(BStruct), {'z': 3}) + assert sizeof(p[0]) == base + 3 + q = newp(new_pointer_type(BStruct), {'z': size_of_int()}) + assert sizeof(q) == size_of_ptr() + assert sizeof(q[0]) == base + size_of_int() + assert len(p.z) == 3 + assert len(p[0].z) == 3 + assert len(q.z) == size_of_int() + assert len(q[0].z) == size_of_int() + def test_ass_slice(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), None) @@ -3700,3 +3751,5 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + # check that the warnings are associated with lines in this file + assert w[1].lineno == w[0].lineno + 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/__init__.py new/cffi-1.9.1/cffi/__init__.py --- old/cffi-1.8.3/cffi/__init__.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/__init__.py 2016-11-12 14:45:24.000000000 +0100 @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.8.3" -__version_info__ = (1, 8, 3) +__version__ = "1.9.1" +__version_info__ = (1, 9, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/_cffi_include.h new/cffi-1.9.1/cffi/_cffi_include.h --- old/cffi-1.8.3/cffi/_cffi_include.h 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/_cffi_include.h 2016-11-12 14:45:16.000000000 +0100 @@ -141,9 +141,9 @@ #define _cffi_to_c_char \ ((int(*)(PyObject *))_cffi_exports[9]) #define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) #define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) + ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) #define _cffi_get_struct_layout \ not used any more #define _cffi_restore_errno \ @@ -153,11 +153,11 @@ #define _cffi_from_c_char \ ((PyObject *(*)(char))_cffi_exports[15]) #define _cffi_from_c_deref \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) #define _cffi_to_c \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) #define _cffi_from_c_struct \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) #define _cffi_to_c_wchar_t \ ((wchar_t(*)(PyObject *))_cffi_exports[19]) #define _cffi_from_c_wchar_t \ @@ -167,21 +167,22 @@ #define _cffi_to_c__Bool \ ((_Bool(*)(PyObject *))_cffi_exports[22]) #define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) + ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ + PyObject *, char **))_cffi_exports[23]) #define _cffi_convert_array_from_object \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) #define _CFFI_CPIDX 25 #define _cffi_call_python \ ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) #define _CFFI_NUM_EXPORTS 26 -typedef struct _ctypedescr CTypeDescrObject; +struct _cffi_ctypedescr; static void *_cffi_exports[_CFFI_NUM_EXPORTS]; #define _cffi_type(index) ( \ assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ - (CTypeDescrObject *)_cffi_types[index]) + (struct _cffi_ctypedescr *)_cffi_types[index]) static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, const struct _cffi_type_context_s *ctx) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/_embedding.h new/cffi-1.9.1/cffi/_embedding.h --- old/cffi-1.8.3/cffi/_embedding.h 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/_embedding.h 2016-11-12 14:45:24.000000000 +0100 @@ -233,7 +233,7 @@ f = PySys_GetObject((char *)"stderr"); if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.8.3" + "\ncompiled with cffi version: 1.9.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); @@ -366,7 +366,7 @@ const char *code; } _cffi_pypy_init = { _CFFI_MODULE_NAME, - _CFFI_PYTHON_STARTUP_FUNC, + (void(*)(const void *[]))_CFFI_PYTHON_STARTUP_FUNC, _CFFI_PYTHON_STARTUP_CODE, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/cparser.py new/cffi-1.9.1/cffi/cparser.py --- old/cffi-1.8.3/cffi/cparser.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/cparser.py 2016-11-12 14:45:16.000000000 +0100 @@ -332,8 +332,10 @@ realtype = model.unknown_ptr_type(decl.name) else: realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name) + decl.type, name=decl.name, partial_length_ok=True) self._declare('typedef ' + decl.name, realtype, quals=quals) + elif decl.__class__.__name__ == 'Pragma': + pass # skip pragma, only in pycparser 2.15 else: raise api.CDefError("unrecognized construct", decl) except api.FFIError as e: @@ -781,11 +783,14 @@ exprnode.name in self._int_constants): return self._int_constants[exprnode.name] # - if partial_length_ok: - if (isinstance(exprnode, pycparser.c_ast.ID) and + if (isinstance(exprnode, pycparser.c_ast.ID) and exprnode.name == '__dotdotdotarray__'): + if partial_length_ok: self._partial_length = True return '...' + raise api.FFIError(":%d: unsupported '[...]' here, cannot derive " + "the actual array length in this context" + % exprnode.coord.line) # raise api.FFIError(":%d: unsupported expression: expected a " "simple numeric constant" % exprnode.coord.line) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/model.py new/cffi-1.9.1/cffi/model.py --- old/cffi-1.8.3/cffi/model.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/model.py 2016-11-12 14:45:16.000000000 +0100 @@ -519,10 +519,18 @@ smallest_value = min(self.enumvalues) largest_value = max(self.enumvalues) else: - raise api.CDefError("%r has no values explicitly defined: " - "refusing to guess which integer type it is " - "meant to be (unsigned/signed, int/long)" - % self._get_c_name()) + import warnings + try: + # XXX! The goal is to ensure that the warnings.warn() + # will not suppress the warning. We want to get it + # several times if we reach this point several times. + __warningregistry__.clear() + except NameError: + pass + warnings.warn("%r has no values explicitly defined; " + "guessing that it is equivalent to 'unsigned int'" + % self._get_c_name()) + smallest_value = largest_value = 0 if smallest_value < 0: # needs a signed type sign = 1 candidate1 = PrimitiveType("int") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/recompiler.py new/cffi-1.9.1/cffi/recompiler.py --- old/cffi-1.8.3/cffi/recompiler.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/recompiler.py 2016-11-12 14:45:16.000000000 +0100 @@ -587,8 +587,11 @@ # ---------- # typedefs + def _typedef_type(self, tp, name): + return self._global_type(tp, "(*(%s *)0)" % (name,)) + def _generate_cpy_typedef_collecttype(self, tp, name): - self._do_collect_type(tp) + self._do_collect_type(self._typedef_type(tp, name)) def _generate_cpy_typedef_decl(self, tp, name): pass @@ -598,6 +601,7 @@ self._lsts["typename"].append(TypenameExpr(name, type_index)) def _generate_cpy_typedef_ctx(self, tp, name): + tp = self._typedef_type(tp, name) self._typedef_ctx(tp, name) if getattr(tp, "origin", None) == "unknown_type": self._struct_ctx(tp, tp.name, approxname=None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi/setuptools_ext.py new/cffi-1.9.1/cffi/setuptools_ext.py --- old/cffi-1.8.3/cffi/setuptools_ext.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/cffi/setuptools_ext.py 2016-11-12 14:45:16.000000000 +0100 @@ -1,4 +1,5 @@ import os +import sys try: basestring @@ -74,8 +75,13 @@ Add py_limited_api to kwds if setuptools >= 26 is in use. Do not alter the setting if it already exists. Setuptools takes care of ignoring the flag on Python 2 and PyPy. + + CPython itself should ignore the flag in a debugging version + (by not listing .abi3.so in the extensions it supports), but + it doesn't so far, creating troubles. That's why we check + for "not sys.flags.debug". (http://bugs.python.org/issue28401) """ - if 'py_limited_api' not in kwds: + if 'py_limited_api' not in kwds and not sys.flags.debug: import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/cffi.egg-info/PKG-INFO new/cffi-1.9.1/cffi.egg-info/PKG-INFO --- old/cffi-1.8.3/cffi.egg-info/PKG-INFO 2016-09-17 12:21:52.000000000 +0200 +++ new/cffi-1.9.1/cffi.egg-info/PKG-INFO 2016-11-12 14:46:13.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.8.3 +Version: 1.9.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/demo/embedding_test.c new/cffi-1.9.1/demo/embedding_test.c --- old/cffi-1.8.3/demo/embedding_test.c 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/demo/embedding_test.c 2016-11-12 14:45:16.000000000 +0100 @@ -1,7 +1,31 @@ -/* Link this program with libembedding_test.so. +/* There are two options: + + =====1===== + + Link this program with _embedding_test.so. E.g. with gcc: gcc -o embedding_test embedding_test.c _embedding_cffi*.so + + You must then run the executable with the right command + (LD_LIBRARY_PATH on Linux), otherwise it won't find the + _embedding_cffi*.so: + + LD_LIBRARY_PATH=. ./embedding_test + + There are platform-specific options to gcc to avoid needing + that, too. Linux: + + gcc -o embedding_test embedding_test.c _embedding_cffi*.so \ + -Wl,-rpath=\$ORIGIN/ + + =====2===== + + Compile and link the _embedding_test.c source code together with + this example (e.g. with PyPy): + + gcc -o embedding_test embedding_test.c _embedding_cffi.c \ + -I/opt/pypy/include -pthread -lpypy-c */ #include <stdio.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/cdef.rst new/cffi-1.9.1/doc/source/cdef.rst --- old/cffi-1.8.3/doc/source/cdef.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/cdef.rst 2016-11-12 14:45:16.000000000 +0100 @@ -58,7 +58,7 @@ import cffi ffibuilder = cffi.FFI() - ffibuilder.set_source("package._foo", "real C code") # <= + ffibuilder.set_source("package._foo", r"""real C code""") # <= ffibuilder.cdef("C-like declarations with '...'") if __name__ == "__main__": @@ -345,7 +345,9 @@ module to generate. In this mode, no C compiler is called. In **API mode,** the ``c_header_source`` argument is a string that -will be pasted into the .c file generated. This piece of C code +will be pasted into the .c file generated. Typically, it is specified as +``r""" ...multiple lines of C code... """`` (the ``r`` prefix allows these +lines to contain a literal ``\n``, for example). This piece of C code typically contains some ``#include``, but may also contain more, like definitions for custom "wrapper" C functions. The goal is that the .c file can be generated like this:: @@ -387,7 +389,7 @@ .. code-block:: python - ffibuilder.set_source("mymodule", ''' + ffibuilder.set_source("mymodule", r''' extern "C" { int somefunc(int somearg) { return real_cpp_func(somearg); } } @@ -851,7 +853,7 @@ import cffi ffi = cffi.FFI() - C_HEADER_SRC = ''' + C_HEADER_SRC = r''' #include "somelib.h" ''' C_KEYWORDS = dict(libraries=['somelib']) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/conf.py new/cffi-1.9.1/doc/source/conf.py --- old/cffi-1.8.3/doc/source/conf.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/conf.py 2016-11-12 14:45:24.000000000 +0100 @@ -45,9 +45,9 @@ # built documents. # # The short X.Y version. -version = '1.8' +version = '1.9' # The full version, including alpha/beta/rc tags. -release = '1.8.3' +release = '1.9.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/embedding.rst new/cffi-1.9.1/doc/source/embedding.rst --- old/cffi-1.8.3/doc/source/embedding.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/embedding.rst 2016-11-12 14:45:16.000000000 +0100 @@ -56,7 +56,7 @@ with open('plugin.h') as f: ffibuilder.embedding_api(f.read()) - ffibuilder.set_source("my_plugin", ''' + ffibuilder.set_source("my_plugin", r''' #include "plugin.h" ''') @@ -211,7 +211,9 @@ As in the example above, you can also use the same ``foo.h`` from ``ffibuilder.set_source()``:: - ffibuilder.set_source('module_name', '#include "foo.h"') + ffibuilder.set_source('module_name', r''' + #include "foo.h" + ''') .. __: using.html#working @@ -404,7 +406,7 @@ extern "Python" int mycb(int); """) - ffibuilder.set_source("my_plugin", """ + ffibuilder.set_source("my_plugin", r""" static int mycb(int); /* the callback: forward declaration, to make it accessible from the C code that follows */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/installation.rst new/cffi-1.9.1/doc/source/installation.rst --- old/cffi-1.8.3/doc/source/installation.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/installation.rst 2016-11-12 14:45:24.000000000 +0100 @@ -51,7 +51,7 @@ Download and Installation: -* http://pypi.python.org/packages/source/c/cffi/cffi-1.8.3.tar.gz +* http://pypi.python.org/packages/source/c/cffi/cffi-1.9.1.tar.gz - MD5: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/overview.rst new/cffi-1.9.1/doc/source/overview.rst --- old/cffi-1.8.3/doc/source/overview.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/overview.rst 2016-11-12 14:45:16.000000000 +0100 @@ -72,7 +72,7 @@ ffibuilder = FFI() ffibuilder.set_source("_example", - """ // passed to the real C compiler + r""" // passed to the real C compiler #include <sys/types.h> #include <pwd.h> """, @@ -195,7 +195,7 @@ ffibuilder.cdef("int foo(int *, int *, int);") ffibuilder.set_source("_example", - """ + r""" static int foo(int *buffer_in, int *buffer_out, int x) { /* some algorithm that is seriously faster in C than in Python */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/ref.rst new/cffi-1.9.1/doc/source/ref.rst --- old/cffi-1.8.3/doc/source/ref.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/ref.rst 2016-11-12 14:45:16.000000000 +0100 @@ -141,7 +141,8 @@ into a struct over a socket, rewriting the contents of mystruct[0] Remember that like in C, you can use ``array + index`` to get the pointer -to the index'th item of an array. +to the index'th item of an array. (In C you might more naturally write +``&array[index]``, but that is equivalent.) The returned object is not a built-in buffer nor memoryview object, because these objects' API changes too much across Python versions. @@ -255,6 +256,16 @@ argument in bytes. The argument can be either a C type, or a cdata object, like in the equivalent ``sizeof`` operator in C. +For ``array = ffi.new("T[]", n)``, then ``ffi.sizeof(array)`` returns +``n * ffi.sizeof("T")``. *New in version 1.9:* Similar rules apply for +structures with aa variable-sized array at the end. More precisely, if +``p`` was returned by ``ffi.new("struct foo *", ...)``, then +``ffi.sizeof(p[0])`` now returns the total allocated size. In previous +versions, it used to just return ``ffi.sizeof(ffi.typeof(p[0]))``, which +is the size of the structure ignoring the variable-sized part. (Note +that due to alignment, it is possible for ``ffi.sizeof(p[0])`` to return +a value smaller than ``ffi.sizeof(ffi.typeof(p[0]))``.) + **ffi.alignof("C type")**: return the natural alignment size in bytes of the argument. Corresponds to the ``__alignof__`` operator in GCC. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/using.rst new/cffi-1.9.1/doc/source/using.rst --- old/cffi-1.8.3/doc/source/using.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/using.rst 2016-11-12 14:45:16.000000000 +0100 @@ -479,7 +479,7 @@ void library_function(int(*callback)(int, int)); """) - ffibuilder.set_source("_my_example", """ + ffibuilder.set_source("_my_example", r""" #include <some_library.h> """) @@ -546,7 +546,7 @@ extern "Python" void my_event_callback(event_t *, void *); """) - ffibuilder.set_source("_demo_cffi", """ + ffibuilder.set_source("_demo_cffi", r""" #include <the_event_library.h> """) @@ -613,7 +613,7 @@ :: - ffibuilder.set_source("_demo_cffi", """ + ffibuilder.set_source("_demo_cffi", r""" #include <the_event_library.h> static void my_event_callback(widget_t *, event_t *); @@ -629,7 +629,7 @@ extern "Python" int f(int); int my_algo(int); """) - ffibuilder.set_source("_example_cffi", """ + ffibuilder.set_source("_example_cffi", r""" static int f(int); /* the forward declaration */ static int my_algo(int n) { @@ -673,7 +673,7 @@ ffibuilder.cdef(""" extern "Python+C" int f(int); /* not static */ """) - ffibuilder.set_source("_example_cffi", """ + ffibuilder.set_source("_example_cffi", r""" /* the forward declaration, setting a gcc attribute (this line could also be in some .h file, to be included both here and in the other C files of the project) */ @@ -834,7 +834,7 @@ int (*python_callback)(int how_many, int *values); void *const c_callback; /* pass this const ptr to C routines */ """) - ffibuilder.set_source("_example", """ + ffibuilder.set_source("_example", r""" #include <stdarg.h> #include <alloca.h> static int (*python_callback)(int how_many, int *values); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/doc/source/whatsnew.rst new/cffi-1.9.1/doc/source/whatsnew.rst --- old/cffi-1.8.3/doc/source/whatsnew.rst 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/doc/source/whatsnew.rst 2016-11-12 14:45:16.000000000 +0100 @@ -3,6 +3,41 @@ ====================== +v1.9 +==== + +* Structs with variable-sized arrays as their last field: now we track + the length of the array after ``ffi.new()`` is called, just like we + always tracked the length of ``ffi.new("int[]", 42)``. This lets us + detect out-of-range accesses to array items. This also lets us + display a better ``repr()``, and have the total size returned by + ``ffi.sizeof()`` and ``ffi.buffer()``. Previously both functions + would return a result based on the size of the declared structure + type, with an assumed empty array. (Thanks andrew for starting this + refactoring.) + +* Add support in ``cdef()/set_source()`` for unspecified-length arrays + in typedefs: ``typedef int foo_t[...];``. It was already supported + for global variables or structure fields. + +* I turned in v1.8 a warning from ``cffi/model.py`` into an error: + ``'enum xxx' has no values explicitly defined: refusing to guess which + integer type it is meant to be (unsigned/signed, int/long)``. Now I'm + turning it back to a warning again; it seems that guessing that the + enum has size ``int`` is a 99%-safe bet. (But not 100%, so it stays + as a warning.) + +* Fix leaks in the code handling ``FILE *`` arguments. In CPython 3 + there is a remaining issue that is hard to fix: if you pass a Python + file object to a ``FILE *`` argument, then ``os.dup()`` is used and + the new file descriptor is only closed when the GC reclaims the Python + file object---and not at the earlier time when you call ``close()``, + which only closes the original file descriptor. If this is an issue, + you should avoid this automatic convertion of Python file objects: + instead, explicitly manipulate file descriptors and call ``fdopen()`` + from C (...via cffi). + + v1.8.3 ====== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/setup.py new/cffi-1.9.1/setup.py --- old/cffi-1.8.3/setup.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/setup.py 2016-11-12 14:45:24.000000000 +0100 @@ -51,12 +51,17 @@ see http://stackoverflow.com/questions/22313407/ .)\n""") sys.exit(1) -def ask_supports_thread(): +def get_config(): from distutils.core import Distribution from distutils.sysconfig import get_config_vars get_config_vars() # workaround for a bug of distutils, e.g. on OS/X config = Distribution().get_command_obj('config') - ok = config.try_compile('__thread int some_threadlocal_variable_42;') + return config + +def ask_supports_thread(): + config = get_config() + ok = (sys.platform != 'win32' and + config.try_compile('__thread int some_threadlocal_variable_42;')) if ok: define_macros.append(('USE__THREAD', None)) else: @@ -66,6 +71,10 @@ sys.stderr.write("Note: will not use '__thread' in the C code\n") sys.stderr.write("The above error message can be safely ignored\n") +def uses_msvc(): + config = get_config() + return config.try_compile('#ifndef _MSC_VER\n#error "not MSVC"\n#endif') + def use_pkg_config(): if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'): use_homebrew_for_libffi() @@ -86,7 +95,7 @@ os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig) -if sys.platform == 'win32': +if sys.platform == 'win32' and uses_msvc(): COMPILE_LIBFFI = 'c/libffi_msvc' # from the CPython distribution else: COMPILE_LIBFFI = None @@ -144,7 +153,7 @@ `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ """, - version='1.8.3', + version='1.9.1', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h']} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi0/backend_tests.py new/cffi-1.9.1/testing/cffi0/backend_tests.py --- old/cffi-1.8.3/testing/cffi0/backend_tests.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi0/backend_tests.py 2016-11-12 14:45:16.000000000 +0100 @@ -1355,15 +1355,15 @@ assert ffi.getctype("e1*") == 'e1 *' def test_opaque_enum(self): + import warnings ffi = FFI(backend=self.Backend()) ffi.cdef("enum foo;") - from cffi import __version_info__ - if __version_info__ < (1, 8): - py.test.skip("re-enable me in version 1.8") - e = py.test.raises(CDefError, ffi.cast, "enum foo", -1) - assert str(e.value) == ( - "'enum foo' has no values explicitly defined: refusing to guess " - "which integer type it is meant to be (unsigned/signed, int/long)") + with warnings.catch_warnings(record=True) as log: + n = ffi.cast("enum foo", -1) + assert int(n) == 0xffffffff + assert str(log[0].message) == ( + "'enum foo' has no values explicitly defined; " + "guessing that it is equivalent to 'unsigned int'") def test_new_ctype(self): ffi = FFI(backend=self.Backend()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi0/test_ffi_backend.py new/cffi-1.9.1/testing/cffi0/test_ffi_backend.py --- old/cffi-1.8.3/testing/cffi0/test_ffi_backend.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi0/test_ffi_backend.py 2016-11-12 14:45:16.000000000 +0100 @@ -282,10 +282,20 @@ ffi.cdef("struct foo_s { int x; int a[]; };") p = ffi.new("struct foo_s *", [100, [200, 300, 400]]) assert p.x == 100 - assert ffi.typeof(p.a) is ffi.typeof("int *") # no length available + assert ffi.typeof(p.a) is ffi.typeof("int[]") + assert len(p.a) == 3 # length recorded assert p.a[0] == 200 assert p.a[1] == 300 assert p.a[2] == 400 + assert list(p.a) == [200, 300, 400] + q = ffi.cast("struct foo_s *", p) + assert q.x == 100 + assert ffi.typeof(q.a) is ffi.typeof("int *") # no length recorded + py.test.raises(TypeError, len, q.a) + assert q.a[0] == 200 + assert q.a[1] == 300 + assert q.a[2] == 400 + py.test.raises(TypeError, list, q.a) @pytest.mark.skipif("sys.platform != 'win32'") def test_getwinerror(self): @@ -479,3 +489,7 @@ assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" p = ffi.new("int[]", [-123456789]) assert ffi.unpack(p, 1) == [-123456789] + + def test_negative_array_size(self): + ffi = FFI() + py.test.raises(ValueError, ffi.cast, "int[-5]", 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi0/test_verify.py new/cffi-1.9.1/testing/cffi0/test_verify.py --- old/cffi-1.8.3/testing/cffi0/test_verify.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi0/test_verify.py 2016-11-12 14:45:16.000000000 +0100 @@ -561,7 +561,8 @@ "int bar(struct foo_s *f) { return f->a[14]; }\n") assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) is ffi.typeof('int *') # because no length + assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length + assert len(s.a) == 18 # max length, computed from the size and start offset s.a[14] = 4242 assert lib.bar(s) == 4242 # with no declared length, out-of-bound accesses are not detected @@ -591,10 +592,15 @@ ffi.verify("struct foo_s { int x; int a[]; };") assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') # the same in C + assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + # ^^^ explanation: if you write in C: "char x[5];", then + # "sizeof(x)" will evaluate to 5. The behavior above is + # a generalization of that to "struct foo_s[len(a)=5] x;" + # if you could do that in C. assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') @@ -609,10 +615,10 @@ ffi.verify("struct foo_s { int x, y; int a[]; };") assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi1/test_ffi_obj.py new/cffi-1.9.1/testing/cffi1/test_ffi_obj.py --- old/cffi-1.8.3/testing/cffi1/test_ffi_obj.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi1/test_ffi_obj.py 2016-11-12 14:45:16.000000000 +0100 @@ -502,3 +502,7 @@ assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" p = ffi.new("int[]", [-123456789]) assert ffi.unpack(p, 1) == [-123456789] + +def test_negative_array_size(): + ffi = _cffi1_backend.FFI() + py.test.raises(ffi.error, ffi.cast, "int[-5]", 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi1/test_new_ffi_1.py new/cffi-1.9.1/testing/cffi1/test_new_ffi_1.py --- old/cffi-1.8.3/testing/cffi1/test_new_ffi_1.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi1/test_new_ffi_1.py 2016-11-12 14:45:16.000000000 +0100 @@ -1633,10 +1633,19 @@ # struct array_no_length { int x; int a[]; }; p = ffi.new("struct array_no_length *", [100, [200, 300, 400]]) assert p.x == 100 - assert ffi.typeof(p.a) is ffi.typeof("int *") # no length available + assert ffi.typeof(p.a) is ffi.typeof("int[]") # length available assert p.a[0] == 200 assert p.a[1] == 300 assert p.a[2] == 400 + assert len(p.a) == 3 + assert list(p.a) == [200, 300, 400] + q = ffi.cast("struct array_no_length *", p) + assert ffi.typeof(q.a) is ffi.typeof("int *") # no length available + assert q.a[0] == 200 + assert q.a[1] == 300 + assert q.a[2] == 400 + py.test.raises(TypeError, len, q.a) + py.test.raises(TypeError, list, q.a) def test_from_buffer(self): import array diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi1/test_recompiler.py new/cffi-1.9.1/testing/cffi1/test_recompiler.py --- old/cffi-1.8.3/testing/cffi1/test_recompiler.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi1/test_recompiler.py 2016-11-12 14:45:16.000000000 +0100 @@ -399,11 +399,14 @@ pass # ok, fail during compilation already (e.g. C++) else: assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code - p = ffi.new("struct foo_s *") - # lazily build the fields and boom: - e = py.test.raises(ffi.error, "p.a") - assert str(e.value).startswith("struct foo_s: wrong size for field 'a' " - "(cdef says 20, but C compiler says 24)") + try: + # lazily build the fields and boom: + p = ffi.new("struct foo_s *") + p.a + assert False, "should have raised" + except ffi.error as e: + assert str(e).startswith("struct foo_s: wrong size for field 'a' " + "(cdef says 20, but C compiler says 24)") def test_open_array_in_struct(): ffi = FFI() @@ -411,8 +414,10 @@ verify(ffi, 'test_open_array_in_struct', "struct foo_s { int b; int a[]; };") assert ffi.sizeof("struct foo_s") == 4 - p = ffi.new("struct foo_s *", [5, [10, 20, 30]]) + p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]]) assert p.a[2] == 30 + assert ffi.sizeof(p) == ffi.sizeof("void *") + assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") def test_math_sin_type(): ffi = FFI() @@ -998,6 +1003,7 @@ "struct foo_s { int x; int a[5][8]; int y; };") assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') s = ffi.new("struct foo_s *") + assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') assert s.a[4][7] == 0 py.test.raises(IndexError, 's.a[4][8]') @@ -1012,7 +1018,7 @@ "struct foo_s { int x; int a[5][7]; int y; };") assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) == ffi.typeof("int(*)[7]") + assert ffi.typeof(s.a) == ffi.typeof("int[][7]") assert s.a[4][6] == 0 py.test.raises(IndexError, 's.a[4][7]') assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") @@ -1980,3 +1986,29 @@ static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } """) assert lib.f1(52).a == 52 + +def test_typedef_array_dotdotdot(): + ffi = FFI() + ffi.cdef(""" + typedef int foo_t[...], bar_t[...]; + int gv[...]; + typedef int mat_t[...][...]; + typedef int vmat_t[][...]; + """) + lib = verify(ffi, "test_typedef_array_dotdotdot", """ + typedef int foo_t[50], bar_t[50]; + int gv[23]; + typedef int mat_t[6][7]; + typedef int vmat_t[][8]; + """) + assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") + assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") + assert len(ffi.new("foo_t")) == 50 + assert len(ffi.new("bar_t")) == 50 + assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") + assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") + assert len(ffi.new("mat_t")) == 6 + assert len(ffi.new("mat_t")[3]) == 7 + py.test.raises(ffi.error, ffi.sizeof, "vmat_t") + p = ffi.new("vmat_t", 4) + assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.8.3/testing/cffi1/test_verify1.py new/cffi-1.9.1/testing/cffi1/test_verify1.py --- old/cffi-1.8.3/testing/cffi1/test_verify1.py 2016-09-17 12:21:34.000000000 +0200 +++ new/cffi-1.9.1/testing/cffi1/test_verify1.py 2016-11-12 14:45:16.000000000 +0100 @@ -546,7 +546,8 @@ "int bar(struct foo_s *f) { return f->a[14]; }\n") assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) is ffi.typeof('int *') # because no length + assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length + assert len(s.a) == 18 # max length, computed from the size and start offset s.a[14] = 4242 assert lib.bar(s) == 4242 # with no declared length, out-of-bound accesses are not detected @@ -576,10 +577,15 @@ ffi.verify("struct foo_s { int x; int a[]; };") assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') # the same in C + assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + # ^^^ explanation: if you write in C: "char x[5];", then + # "sizeof(x)" will evaluate to 5. The behavior above is + # a generalization of that to "struct foo_s[len(a)=5] x;" + # if you could do that in C. assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') @@ -594,10 +600,10 @@ ffi.verify("struct foo_s { int x, y; int a[]; };") assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') assert s.a[3] == 0 s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') assert s.a[3] == -10 s = ffi.new("struct foo_s *") assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')