Hello community, here is the log from the commit of package python3-cffi for openSUSE:Factory checked in at 2016-09-20 13:24:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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-06-25 01:56:13.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-cffi.new/python3-cffi-doc.changes 2016-09-20 13:24:53.000000000 +0200 @@ -1,0 +2,41 @@ +Sun Sep 18 15:53:20 UTC 2016 - arun@gmx.de + +- update to version 1.8.3: + * When passing a void * argument to a function with a different + pointer type, or vice-versa, the cast occurs automatically, like + in C. The same occurs for initialization with ffi.new() and a few + other places. However, I thought that char * had the same + property—but I was mistaken. In C you get the usual warning if you + try to give a char * to a char ** argument, for example. Sorry + about the confusion. This has been fixed in CFFI by giving for now + a warning, too. It will turn into an error in a future version. + +------------------------------------------------------------------- +Fri Sep 16 15:10:25 UTC 2016 - toddrme2178@gmail.com + +- Update to 1.8.2 + * Issue #283: fixed ``ffi.new()`` on structures/unions with nested + anonymous structures/unions, when there is at least one union in + the mix. When initialized with a list or a dict, it should now + behave more closely like the ``{ }`` syntax does in GCC. +- Update to 1.8.1 + * CPython 3.x: experimental: the generated C extension modules now use + the "limited API", which means that, as a compiled .so/.dll, it should + work directly on any version of CPython >= 3.2. The name produced by + distutils is still version-specific. To get the version-independent + name, you can rename it manually to ``NAME.abi3.so``, or use the very + recent setuptools 26. + * Added ``ffi.compile(debug=...)``, similar to ``python setup.py build + --debug`` but defaulting to True if we are running a debugging + version of Python itself. +- Update to 1.8 + * Removed the restriction that ``ffi.from_buffer()`` cannot be used on + byte strings. Now you can get a ``char *`` out of a byte string, + which is valid as long as the string object is kept alive. (But + don't use it to *modify* the string object! If you need this, use + ``bytearray`` or other official techniques.) + * PyPy 5.4 can now pass a byte string directly to a ``char *`` + argument (in older versions, a copy would be made). This used to be + a CPython-only optimization. + +------------------------------------------------------------------- python3-cffi.changes: same change Old: ---- cffi-1.7.0.tar.gz New: ---- cffi-1.8.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-cffi-doc.spec ++++++ --- /var/tmp/diff_new_pack.CijluO/_old 2016-09-20 13:24:54.000000000 +0200 +++ /var/tmp/diff_new_pack.CijluO/_new 2016-09-20 13:24:54.000000000 +0200 @@ -17,7 +17,7 @@ Name: python3-cffi-doc -Version: 1.7.0 +Version: 1.8.3 Release: 0 Summary: Documentation for python3-cffi License: MIT python3-cffi.spec: same change ++++++ cffi-1.7.0.tar.gz -> cffi-1.8.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/PKG-INFO new/cffi-1.8.3/PKG-INFO --- old/cffi-1.7.0/PKG-INFO 2016-06-20 16:33:45.000000000 +0200 +++ new/cffi-1.8.3/PKG-INFO 2016-09-17 12:21:52.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.7.0 +Version: 1.8.3 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski @@ -27,5 +27,6 @@ Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/c/_cffi_backend.c new/cffi-1.8.3/c/_cffi_backend.c --- old/cffi-1.7.0/c/_cffi_backend.c 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/c/_cffi_backend.c 2016-09-17 12:21:34.000000000 +0200 @@ -2,7 +2,7 @@ #include <Python.h> #include "structmember.h" -#define CFFI_VERSION "1.7.0" +#define CFFI_VERSION "1.8.3" #ifdef MS_WIN32 #include <windows.h> @@ -128,7 +128,7 @@ #define CT_VOID 512 /* void */ /* other flags that may also be set in addition to the base flag: */ -#define CT_CAST_ANYTHING 1024 /* 'char *' and 'void *' only */ +#define CT_IS_VOIDCHAR_PTR 1024 #define CT_PRIMITIVE_FITS_LONG 2048 #define CT_IS_OPAQUE 4096 #define CT_IS_ENUM 8192 @@ -187,10 +187,12 @@ Py_ssize_t cf_offset; short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */ short cf_bitsize; + 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 */ static PyTypeObject CTypeDescr_Type; static PyTypeObject CField_Type; @@ -657,6 +659,7 @@ {"offset", T_PYSSIZET, OFF(cf_offset), READONLY}, {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY}, {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY}, + {"flags", T_UBYTE, OFF(cf_flags), READONLY}, {NULL} /* Sentinel */ }; #undef OFF @@ -1274,24 +1277,14 @@ return -1; } - if (ct->ct_flags & CT_UNION) { - Py_ssize_t n = PyObject_Size(init); - if (n < 0) - return -1; - if (n > 1) { - PyErr_Format(PyExc_ValueError, - "initializer for '%s': %zd items given, but " - "only one supported (use a dict if needed)", - ct->ct_name, n); - return -1; - } - } if (PyList_Check(init) || PyTuple_Check(init)) { PyObject **items = PySequence_Fast_ITEMS(init); Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init); CFieldObject *cf = (CFieldObject *)ct->ct_extra; for (i=0; i<n; i++) { + while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR)) + cf = cf->cf_next; if (cf == NULL) { PyErr_Format(PyExc_ValueError, "too many initializers for '%s' (got %zd)", @@ -1365,9 +1358,26 @@ } } if (ctinit != ct) { - if ((ct->ct_flags & CT_CAST_ANYTHING) || - (ctinit->ct_flags & CT_CAST_ANYTHING)) - ; /* accept void* or char* as either source or target */ + int combined_flags = ct->ct_flags | ctinit->ct_flags; + if (combined_flags & CT_IS_VOID_PTR) + ; /* accept "void *" as either source or target */ + else if (combined_flags & CT_IS_VOIDCHAR_PTR) { + /* for backward compatibility, accept "char *" as either + source of target. This is not what C does, though, + so emit a warning that will eventually turn into an + error. */ + char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? + "implicit cast to 'char *' from a different pointer type: " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" : + "implicit cast from 'char *' to a different pointer type: " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)"); + if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + return -1; + } else { expected = "pointer to same type"; goto cannot_convert; @@ -2355,8 +2365,16 @@ return NULL; } itemsize = ct->ct_itemdescr->ct_size; - if (itemsize <= 0) itemsize = 1; - diff = (cdv->c_data - cdw->c_data) / itemsize; + diff = cdv->c_data - cdw->c_data; + if (itemsize > 1) { + if (diff % itemsize) { + PyErr_SetString(PyExc_ValueError, + "pointer subtraction: the distance between the two " + "pointers is not a multiple of the item size"); + return NULL; + } + diff = diff / itemsize; + } #if PY_MAJOR_VERSION < 3 return PyInt_FromSsize_t(diff); #else @@ -2475,7 +2493,7 @@ if (PyBytes_Check(init)) { /* from a string: just returning the string here is fine. We assume that the C code won't modify the 'char *' data. */ - if ((ctptr->ct_flags & CT_CAST_ANYTHING) || + if ((ctptr->ct_flags & CT_IS_VOIDCHAR_PTR) || ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) && (ctitem->ct_size == sizeof(char)))) { #if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) @@ -3034,13 +3052,14 @@ static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) { + /* also accepts unions, for the API mode */ CDataObject *cd; Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); Py_ssize_t datasize = ct->ct_size; - if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) != CT_STRUCT) { + if (datasize < 0) { PyErr_SetString(PyExc_TypeError, - "return type is not a struct or is opaque"); + "return type is an opaque structure or union"); return NULL; } cd = allocate_owning_object(dataoffset + datasize, ct); @@ -3778,7 +3797,7 @@ EPTYPE(um, uintmax_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(pd, ptrdiff_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(sz, size_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(ssz, ssize_t, CT_PRIMITIVE_SIGNED) + EPTYPE2(ssz, "ssize_t", Py_ssize_t, CT_PRIMITIVE_SIGNED) #ifdef HAVE_WCHAR_H # define ENUM_PRIMITIVE_TYPES_WCHAR \ @@ -3787,21 +3806,24 @@ # define ENUM_PRIMITIVE_TYPES_WCHAR /* nothing */ #endif -#define EPTYPE(code, typename, flags) \ +#define EPTYPE(code, typename, flags) EPTYPE2(code, #typename, typename, flags) + +#define EPTYPE2(code, export_name, typename, flags) \ struct aligncheck_##code { char x; typename y; }; ENUM_PRIMITIVE_TYPES -#undef EPTYPE +#undef EPTYPE2 CTypeDescrObject *td; static const struct descr_s { const char *name; int size, align, flags; } types[] = { -#define EPTYPE(code, typename, flags) \ - { #typename, \ +#define EPTYPE2(code, export_name, typename, flags) \ + { export_name, \ sizeof(typename), \ offsetof(struct aligncheck_##code, y), \ flags \ }, ENUM_PRIMITIVE_TYPES +#undef EPTYPE2 #undef EPTYPE #undef ENUM_PRIMITIVE_TYPES_WCHAR #undef ENUM_PRIMITIVE_TYPES @@ -3924,7 +3946,7 @@ if ((ctitem->ct_flags & CT_VOID) || ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && ctitem->ct_size == sizeof(char))) - td->ct_flags |= CT_CAST_ANYTHING; /* 'void *' or 'char *' only */ + td->ct_flags |= CT_IS_VOIDCHAR_PTR; /* 'void *' or 'char *' only */ unique_key[0] = ctitem; return get_unique_type(td, unique_key, 1); } @@ -4073,7 +4095,7 @@ static CFieldObject * _add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype, - Py_ssize_t offset, int bitshift, int fbitsize) + Py_ssize_t offset, int bitshift, int fbitsize, int flags) { int err; Py_ssize_t prev_size; @@ -4086,6 +4108,7 @@ cf->cf_offset = offset; cf->cf_bitshift = bitshift; cf->cf_bitsize = fbitsize; + cf->cf_flags = flags; Py_INCREF(fname); PyText_InternInPlace(&fname); @@ -4172,7 +4195,7 @@ int totalalignment = -1; CFieldObject **previous; int prev_bitfield_size, prev_bitfield_free; - int sflags = 0; + int sflags = 0, fflags; if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union", &CTypeDescr_Type, &ct, @@ -4258,6 +4281,8 @@ if (alignment < falign && do_align) alignment = falign; + fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0; + if (fbitsize < 0) { /* not a bitfield: common case */ int bs_flag; @@ -4293,7 +4318,8 @@ cfsrc->cf_type, boffset / 8 + cfsrc->cf_offset, cfsrc->cf_bitshift, - cfsrc->cf_bitsize); + cfsrc->cf_bitsize, + cfsrc->cf_flags | fflags); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; @@ -4303,7 +4329,7 @@ } else { *previous = _add_field(interned_fields, fname, ftype, - boffset / 8, bs_flag, -1); + boffset / 8, bs_flag, -1, fflags); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; @@ -4433,7 +4459,8 @@ bitshift = 8 * ftype->ct_size - fbitsize - bitshift; *previous = _add_field(interned_fields, fname, ftype, - field_offset_bytes, bitshift, fbitsize); + field_offset_bytes, bitshift, fbitsize, + fflags); if (*previous == NULL) goto error; previous = &(*previous)->cf_next; @@ -4612,6 +4639,8 @@ ct = ct->ct_itemdescr; } ffifield = fb_fill_type(fb, ct, 0); + if (PyErr_Occurred()) + return NULL; if (elements != NULL) { for (j=0; j<flat; j++) elements[nflat++] = ffifield; @@ -5472,7 +5501,7 @@ *offset = cf->cf_offset; } else { - ssize_t index = PyInt_AsSsize_t(fieldname); + Py_ssize_t index = PyInt_AsSsize_t(fieldname); if (index < 0 && PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "field name or array index expected"); @@ -5891,7 +5920,7 @@ return NULL; } ct = ((CDataObject *)arg)->c_type; - if (!(ct->ct_flags & CT_CAST_ANYTHING)) { + if (!(ct->ct_flags & CT_IS_VOIDCHAR_PTR)) { PyErr_Format(PyExc_TypeError, "expected a 'cdata' object with a 'void *' out of " "new_handle(), got '%s'", ct->ct_name); @@ -5971,6 +6000,11 @@ static int invalid_input_buffer_type(PyObject *x) { + /* From PyPy 5.4, from_buffer() accepts strings (but still not buffers + or memoryviews on strings). */ + if (PyBytes_Check(x)) + return 0; + #if PY_MAJOR_VERSION < 3 if (PyBuffer_Check(x)) { /* XXX fish fish fish in an inofficial way */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/c/test_c.py new/cffi-1.8.3/c/test_c.py --- old/cffi-1.7.0/c/test_c.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/c/test_c.py 2016-09-17 12:21:34.000000000 +0200 @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.7.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.8.3", ("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,): @@ -587,6 +587,19 @@ e = py.test.raises(TypeError, "q - a") assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'" +def test_ptr_sub_unaligned(): + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + a = cast(BIntPtr, 1240) + for bi in range(1430, 1438): + b = cast(BIntPtr, bi) + if ((bi - 1240) % size_of_int()) == 0: + assert b - a == (bi - 1240) // size_of_int() + assert a - b == (1240 - bi) // size_of_int() + else: + py.test.raises(ValueError, "b - a") + py.test.raises(ValueError, "a - b") + def test_cast_primitive_from_cdata(): p = new_primitive_type("int") n = cast(p, cast(p, -42)) @@ -2523,6 +2536,25 @@ assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 +def test_nested_anonymous_struct_2(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BInnerUnion = new_union_type("union bar") + complete_struct_or_union(BInnerUnion, [('a1', BInt, -1), + ('a2', BInt, -1)]) + complete_struct_or_union(BStruct, [('b1', BInt, -1), + ('', BInnerUnion, -1), + ('b2', BInt, -1)]) + assert sizeof(BInnerUnion) == sizeof(BInt) + assert sizeof(BStruct) == sizeof(BInt) * 3 + fields = [(name, fld.offset, fld.flags) for (name, fld) in BStruct.fields] + assert fields == [ + ('b1', 0 * sizeof(BInt), 0), + ('a1', 1 * sizeof(BInt), 0), + ('a2', 1 * sizeof(BInt), 1), + ('b2', 2 * sizeof(BInt), 0), + ] + def test_sizeof_union(): # a union has the largest alignment of its members, and a total size # that is the largest of its items *possibly further aligned* if @@ -3341,13 +3373,18 @@ BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) - py.test.raises(TypeError, from_buffer, BCharA, b"foo") + p1 = from_buffer(BCharA, b"foo") + assert p1 == from_buffer(BCharA, b"foo") + import gc; gc.collect() + assert p1 == from_buffer(BCharA, b"foo") py.test.raises(TypeError, from_buffer, BCharA, u+"foo") try: from __builtin__ import buffer except ImportError: pass else: + # from_buffer(buffer(b"foo")) does not work, because it's not + # implemented on pypy; only from_buffer(b"foo") works. py.test.raises(TypeError, from_buffer, BCharA, buffer(b"foo")) py.test.raises(TypeError, from_buffer, BCharA, buffer(u+"foo")) try: @@ -3639,3 +3676,27 @@ check_dir(pp, []) check_dir(pp[0], ['a1', 'a2']) check_dir(pp[0][0], ['a1', 'a2']) + +def test_char_pointer_conversion(): + import warnings + assert __version__.startswith(("1.8", "1.9")), ( + "consider turning the warning into an error") + BCharP = new_pointer_type(new_primitive_type("char")) + BIntP = new_pointer_type(new_primitive_type("int")) + BVoidP = new_pointer_type(new_void_type()) + z1 = cast(BCharP, 0) + z2 = cast(BIntP, 0) + z3 = cast(BVoidP, 0) + with warnings.catch_warnings(record=True) as w: + newp(new_pointer_type(BIntP), z1) # warn + assert len(w) == 1 + newp(new_pointer_type(BVoidP), z1) # fine + assert len(w) == 1 + newp(new_pointer_type(BCharP), z2) # warn + assert len(w) == 2 + newp(new_pointer_type(BVoidP), z2) # fine + assert len(w) == 2 + newp(new_pointer_type(BCharP), z3) # fine + assert len(w) == 2 + newp(new_pointer_type(BIntP), z3) # fine + assert len(w) == 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/__init__.py new/cffi-1.8.3/cffi/__init__.py --- old/cffi-1.7.0/cffi/__init__.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/__init__.py 2016-09-17 12:21:34.000000000 +0200 @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.7.0" -__version_info__ = (1, 7, 0) +__version__ = "1.8.3" +__version_info__ = (1, 8, 3) # 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.7.0/cffi/_cffi_include.h new/cffi-1.8.3/cffi/_cffi_include.h --- old/cffi-1.7.0/cffi/_cffi_include.h 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/_cffi_include.h 2016-09-17 12:21:34.000000000 +0200 @@ -1,4 +1,20 @@ #define _CFFI_ + +/* We try to define Py_LIMITED_API before including Python.h. + + Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and + Py_REF_DEBUG are not defined. This is a best-effort approximation: + we can learn about Py_DEBUG from pyconfig.h, but it is unclear if + the same works for the other two macros. Py_DEBUG implies them, + but not the other way around. +*/ +#ifndef _CFFI_USE_EMBEDDING +# include <pyconfig.h> +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API +# endif +#endif + #include <Python.h> #ifdef __cplusplus extern "C" { @@ -42,7 +58,9 @@ # include <stdint.h> # endif # if _MSC_VER < 1800 /* MSVC < 2013 */ - typedef unsigned char _Bool; +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif # endif #else # include <stdint.h> @@ -59,7 +77,7 @@ #ifdef __cplusplus # ifndef _Bool -# define _Bool bool /* semi-hackish: C++ has no _Bool; bool is builtin */ + typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ # endif #endif @@ -196,20 +214,6 @@ return NULL; } -_CFFI_UNUSED_FN -static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected, - const char *fnname) -{ - if (PyTuple_GET_SIZE(args_tuple) != expected) { - PyErr_Format(PyExc_TypeError, - "%.150s() takes exactly %zd arguments (%zd given)", - fnname, expected, PyTuple_GET_SIZE(args_tuple)); - return NULL; - } - return &PyTuple_GET_ITEM(args_tuple, 0); /* pointer to the first item, - the others follow */ -} - /********** end CPython-specific section **********/ #else _CFFI_UNUSED_FN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/_embedding.h new/cffi-1.8.3/cffi/_embedding.h --- old/cffi-1.7.0/cffi/_embedding.h 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/_embedding.h 2016-09-17 12:21:34.000000000 +0200 @@ -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.7.0" + "\ncompiled with cffi version: 1.8.3" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/api.py new/cffi-1.8.3/cffi/api.py --- old/cffi-1.7.0/cffi/api.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/api.py 2016-09-17 12:21:34.000000000 +0200 @@ -652,7 +652,7 @@ recompile(self, module_name, source, c_file=filename, call_c_compiler=False, **kwds) - def compile(self, tmpdir='.', verbose=0, target=None): + def compile(self, tmpdir='.', verbose=0, target=None, debug=None): """The 'target' argument gives the final file name of the compiled DLL. Use '*' to force distutils' choice, suitable for regular CPython C API modules. Use a file name ending in '.*' @@ -669,7 +669,7 @@ module_name, source, source_extension, kwds = self._assigned_source return recompile(self, module_name, source, tmpdir=tmpdir, target=target, source_extension=source_extension, - compiler_verbose=verbose, **kwds) + compiler_verbose=verbose, debug=debug, **kwds) def init_once(self, func, tag): # Read _init_once_cache[tag], which is either (False, lock) if diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/backend_ctypes.py new/cffi-1.8.3/cffi/backend_ctypes.py --- old/cffi-1.7.0/cffi/backend_ctypes.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/backend_ctypes.py 2016-09-17 12:21:34.000000000 +0200 @@ -997,29 +997,43 @@ assert onerror is None # XXX not implemented return BType(source, error) + _weakref_cache_ref = None + def gcp(self, cdata, destructor): - BType = self.typeof(cdata) + if self._weakref_cache_ref is None: + import weakref + class MyRef(weakref.ref): + def __eq__(self, other): + myref = self() + return self is other or ( + myref is not None and myref is other()) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self()) + return self._hash + self._weakref_cache_ref = {}, MyRef + weak_cache, MyRef = self._weakref_cache_ref if destructor is None: - if not (hasattr(BType, '_gcp_type') and - BType._gcp_type is BType): + try: + del weak_cache[MyRef(cdata)] + except KeyError: raise TypeError("Can remove destructor only on a object " "previously returned by ffi.gc()") - cdata._destructor = None return None - try: - gcp_type = BType._gcp_type - except AttributeError: - class CTypesDataGcp(BType): - __slots__ = ['_orig', '_destructor'] - def __del__(self): - if self._destructor is not None: - self._destructor(self._orig) - gcp_type = BType._gcp_type = CTypesDataGcp - new_cdata = self.cast(gcp_type, cdata) - new_cdata._orig = cdata - new_cdata._destructor = destructor + def remove(k): + cdata, destructor = weak_cache.pop(k, (None, None)) + if destructor is not None: + destructor(cdata) + + new_cdata = self.cast(self.typeof(cdata), cdata) + assert new_cdata is not cdata + weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) return new_cdata typeof = type diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/ffiplatform.py new/cffi-1.8.3/cffi/ffiplatform.py --- old/cffi-1.7.0/cffi/ffiplatform.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/ffiplatform.py 2016-09-17 12:21:34.000000000 +0200 @@ -21,12 +21,12 @@ allsources.append(os.path.normpath(src)) return Extension(name=modname, sources=allsources, **kwds) -def compile(tmpdir, ext, compiler_verbose=0): +def compile(tmpdir, ext, compiler_verbose=0, debug=None): """Compile a C extension module using distutils.""" saved_environ = os.environ.copy() try: - outputfilename = _build(tmpdir, ext, compiler_verbose) + outputfilename = _build(tmpdir, ext, compiler_verbose, debug) outputfilename = os.path.abspath(outputfilename) finally: # workaround for a distutils bugs where some env vars can @@ -36,7 +36,7 @@ os.environ[key] = value return outputfilename -def _build(tmpdir, ext, compiler_verbose=0): +def _build(tmpdir, ext, compiler_verbose=0, debug=None): # XXX compact but horrible :-( from distutils.core import Distribution import distutils.errors, distutils.log @@ -44,6 +44,9 @@ dist = Distribution({'ext_modules': [ext]}) dist.parse_config_files() options = dist.get_option_dict('build_ext') + if debug is None: + debug = sys.flags.debug + options['debug'] = ('ffiplatform', debug) options['force'] = ('ffiplatform', True) options['build_lib'] = ('ffiplatform', tmpdir) options['build_temp'] = ('ffiplatform', tmpdir) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/model.py new/cffi-1.8.3/cffi/model.py --- old/cffi-1.7.0/cffi/model.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/model.py 2016-09-17 12:21:34.000000000 +0200 @@ -519,12 +519,10 @@ smallest_value = min(self.enumvalues) largest_value = max(self.enumvalues) else: - import warnings - warnings.warn("%r has no values explicitly defined; next version " - "will refuse to guess which integer type it is " - "meant to be (unsigned/signed, int/long)" - % self._get_c_name()) - smallest_value = largest_value = 0 + 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()) 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.7.0/cffi/recompiler.py new/cffi-1.8.3/cffi/recompiler.py --- old/cffi-1.7.0/cffi/recompiler.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/recompiler.py 2016-09-17 12:21:34.000000000 +0200 @@ -275,6 +275,8 @@ def write_c_source_to_f(self, f, preamble): self._f = f prnt = self._prnt + if self.ffi._embedding is not None: + prnt('#define _CFFI_USE_EMBEDDING') # # first the '#include' (actually done by inlining the file's content) lines = self._rel_readlines('_cffi_include.h') @@ -513,7 +515,7 @@ tovar, errcode) return # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): + elif isinstance(tp, model.StructOrUnionOrEnum): # a struct (not a struct pointer) as a function argument self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' % (tovar, self._gettypenum(tp), fromvar)) @@ -570,7 +572,7 @@ elif isinstance(tp, model.ArrayType): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructType): + elif isinstance(tp, model.StructOrUnion): if tp.fldnames is None: raise TypeError("'%s' is used as %s, but is opaque" % ( tp._get_c_name(), context)) @@ -683,13 +685,11 @@ rng = range(len(tp.args)) for i in rng: prnt(' PyObject *arg%d;' % i) - prnt(' PyObject **aa;') prnt() - prnt(' aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name)) - prnt(' if (aa == NULL)') + prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( + name, len(rng), len(rng), + ', '.join(['&arg%d' % i for i in rng]))) prnt(' return NULL;') - for i in rng: - prnt(' arg%d = aa[%d];' % (i, i)) prnt() # for i, type in enumerate(tp.args): @@ -862,6 +862,8 @@ enumfields = list(tp.enumfields()) for fldname, fldtype, fbitsize, fqual in enumfields: fldtype = self._field_type(tp, fldname, fldtype) + self._check_not_opaque(fldtype, + "field '%s.%s'" % (tp.name, fldname)) # cname is None for _add_missing_struct_unions() only op = OP_NOOP if fbitsize >= 0: @@ -911,6 +913,13 @@ first_field_index, c_fields)) self._seen_struct_unions.add(tp) + def _check_not_opaque(self, tp, location): + while isinstance(tp, model.ArrayType): + tp = tp.item + if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: + raise TypeError( + "%s is of an opaque type (not declared in cdef())" % location) + def _add_missing_struct_unions(self): # not very nice, but some struct declarations might be missing # because they don't have any known C name. Check that they are @@ -1422,7 +1431,7 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, **kwds): + compiler_verbose=1, target=None, debug=None, **kwds): if not isinstance(module_name, str): module_name = module_name.encode('ascii') if ffi._windows_unicode: @@ -1458,7 +1467,8 @@ if target != '*': _patch_for_target(patchlist, target) os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, compiler_verbose) + outputfilename = ffiplatform.compile('.', ext, + compiler_verbose, debug) finally: os.chdir(cwd) _unpatch_meths(patchlist) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/setuptools_ext.py new/cffi-1.8.3/cffi/setuptools_ext.py --- old/cffi-1.7.0/cffi/setuptools_ext.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/setuptools_ext.py 2016-09-17 12:21:34.000000000 +0200 @@ -69,16 +69,36 @@ else: _add_c_module(dist, ffi, module_name, source, source_extension, kwds) +def _set_py_limited_api(Extension, kwds): + """ + 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. + """ + if 'py_limited_api' not in kwds: + import setuptools + try: + setuptools_major_version = int(setuptools.__version__.partition('.')[0]) + if setuptools_major_version >= 26: + kwds['py_limited_api'] = True + except ValueError: # certain development versions of setuptools + # If we don't know the version number of setuptools, we + # try to set 'py_limited_api' anyway. At worst, we get a + # warning. + kwds['py_limited_api'] = True + return kwds def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): from distutils.core import Extension - from distutils.command.build_ext import build_ext + # We are a setuptools extension. Need this build_ext for py_limited_api. + from setuptools.command.build_ext import build_ext from distutils.dir_util import mkpath from distutils import log from cffi import recompiler allsources = ['$PLACEHOLDER'] allsources.extend(kwds.pop('sources', [])) + kwds = _set_py_limited_api(Extension, kwds) ext = Extension(name=module_name, sources=allsources, **kwds) def make_mod(tmpdir, pre_run=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi/vengine_cpy.py new/cffi-1.8.3/cffi/vengine_cpy.py --- old/cffi-1.7.0/cffi/vengine_cpy.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/cffi/vengine_cpy.py 2016-09-17 12:21:34.000000000 +0200 @@ -308,7 +308,7 @@ elif isinstance(tp, model.ArrayType): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructType): + elif isinstance(tp, model.StructOrUnion): if tp.fldnames is None: raise TypeError("'%s' is used as %s, but is opaque" % ( tp._get_c_name(), context)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/cffi.egg-info/PKG-INFO new/cffi-1.8.3/cffi.egg-info/PKG-INFO --- old/cffi-1.7.0/cffi.egg-info/PKG-INFO 2016-06-20 16:33:45.000000000 +0200 +++ new/cffi-1.8.3/cffi.egg-info/PKG-INFO 2016-09-17 12:21:52.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.7.0 +Version: 1.8.3 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski @@ -27,5 +27,6 @@ Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/doc/source/cdef.rst new/cffi-1.8.3/doc/source/cdef.rst --- old/cffi-1.7.0/doc/source/cdef.rst 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/cdef.rst 2016-09-17 12:21:34.000000000 +0200 @@ -269,9 +269,7 @@ Usually, the right thing to do is to call this method with True. Be aware (particularly on Python 2) that, afterwards, you need to pass unicode -strings as arguments instead of byte strings. (Before cffi version 0.9, -``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, -inconsistently, not defined by default.) +strings as arguments instead of byte strings. .. _loading-libraries: @@ -336,7 +334,7 @@ **ffibuilder.set_source(module_name, c_header_source, [**keywords...])**: prepare the ffi for producing out-of-line an external module called -``module_name``. *New in version 1.0.* +``module_name``. ``ffibuilder.set_source()`` by itself does not write any file, but merely records its arguments for later. It can therefore be called before or @@ -425,7 +423,7 @@ declaration which doesn't use "``...``" is assumed to be exact, but this is checked: you get an error if it is not correct. -* *New in version 1.1:* integer types: the syntax "``typedef +* integer types: the syntax "``typedef int... foo_t;``" declares the type ``foo_t`` as an integer type whose exact size and signedness is not specified. The compiler will figure it out. (Note that this requires ``set_source()``; it does @@ -462,8 +460,8 @@ length is completed by the C compiler. This is slightly different from "``int n[];``", because the latter means that the length is not known even to the C compiler, and thus - no attempt is made to complete it. *New in version 1.1:* support - for multidimensional arrays: "``int n[...][...];``". + no attempt is made to complete it. This supports + multidimensional arrays: "``int n[...][...];``". *New in version 1.2:* "``int m[][...];``", i.e. ``...`` can be used in the innermost dimensions without being also used in the outermost @@ -506,7 +504,10 @@ ``long *``) or in other locations (e.g. a global array ``int a[5];`` must not be misdeclared ``long a[5];``). CFFI considers `all types listed above`_ as primitive (so ``long long a[5];`` and ``int64_t a[5]`` are -different declarations). +different declarations). The reason for that is detailed in `a comment +about an issue.`__ + +.. __: https://bitbucket.org/cffi/cffi/issues/265/cffi-doesnt-allow-creating-pointe... ffibuilder.compile() etc.: compiling out-of-line modules @@ -521,7 +522,18 @@ the mtime to be updated anyway, delete the file before calling the functions. -**ffibuilder.compile(tmpdir='.', verbose=False):** +*New in version 1.8:* the C code produced by ``emit_c_code()`` or +``compile()`` contains ``#define Py_LIMITED_API``. This means that on +CPython >= 3.2, compiling this source produces a binary .so/.dll that +should work for any version of CPython >= 3.2 (as opposed to only for +the same version of CPython x.y). However, the standard ``distutils`` +package will still produce a file called e.g. +``NAME.cpython-35m-x86_64-linux-gnu.so``. You can manually rename it to +``NAME.abi3.so``, or use setuptools version 26 or later. Also, note +that compiling with a debug version of Python will not actually define +``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy. + +**ffibuilder.compile(tmpdir='.', verbose=False, debug=None):** explicitly generate the .py or .c file, and (if .c) compile it. The output file is (or are) put in the directory given by ``tmpdir``. In the examples given here, we use @@ -536,6 +548,13 @@ compiler. (This parameter might be changed to True by default in a future release.) +*New in version 1.8.1:* ``debug`` argument. If set to a bool, it +controls whether the C code is compiled in debug mode or not. The +default None means to use the host Python's ``sys.flags.debug``. +Starting with version 1.8.1, if you are running a debug-mode Python, the +C code is thus compiled in debug mode by default (note that it is anyway +necessary to do so on Windows). + **ffibuilder.emit_python_code(filename):** generate the given .py file (same as ``ffibuilder.compile()`` for ABI mode, with an explicitly-named file to write). If you choose, you can include this .py file pre-packaged in @@ -823,8 +842,8 @@ .. __: distutils-setuptools_ The following example should work both with old (pre-1.0) and new -versions of CFFI---supporting both is important to run on PyPy, -because CFFI 1.0 does not work in PyPy < 2.6: +versions of CFFI---supporting both is important to run on old +versions of PyPy (CFFI 1.0 does not work in PyPy < 2.6): .. code-block:: python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/doc/source/conf.py new/cffi-1.8.3/doc/source/conf.py --- old/cffi-1.7.0/doc/source/conf.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/conf.py 2016-09-17 12:21:34.000000000 +0200 @@ -45,9 +45,9 @@ # built documents. # # The short X.Y version. -version = '1.7' +version = '1.8' # The full version, including alpha/beta/rc tags. -release = '1.7.0' +release = '1.8.3' # 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.7.0/doc/source/installation.rst new/cffi-1.8.3/doc/source/installation.rst --- old/cffi-1.7.0/doc/source/installation.rst 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/installation.rst 2016-09-17 12:21:34.000000000 +0200 @@ -51,12 +51,14 @@ Download and Installation: -* http://pypi.python.org/packages/source/c/cffi/cffi-1.7.0.tar.gz +* http://pypi.python.org/packages/source/c/cffi/cffi-1.8.3.tar.gz - MD5: ... - SHA: ... + - SHA256: ... + * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/doc/source/ref.rst new/cffi-1.8.3/doc/source/ref.rst --- old/cffi-1.7.0/doc/source/ref.rst 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/ref.rst 2016-09-17 12:21:34.000000000 +0200 @@ -171,7 +171,7 @@ buffer interface. This is the opposite of ``ffi.buffer()``. It gives a reference to the existing data, not a copy; for this reason, and for PyPy compatibility, it does not work with the built-in -types str or unicode (or buffers/memoryviews on them). +type unicode; nor buffers/memoryviews to byte or unicode strings. It is meant to be used on objects containing large quantities of raw data, like bytearrays or ``array.array`` or numpy @@ -193,6 +193,9 @@ method is called), then the ``<cdata>`` object will point to freed memory and must not be used any more. +*New in version 1.8:* the python_buffer can be a byte string (but still +not a buffer/memoryview on a string). + ffi.memmove() +++++++++++++ @@ -289,7 +292,7 @@ 3. ``ffi.addressof(<library>, "name")`` returns the address of the named function or global variable from the given library object. -*New in version 1.1:* for functions, it returns a regular cdata +For functions, it returns a regular cdata object containing a pointer to the function. Note that the case 1. cannot be used to take the address of a @@ -572,12 +575,12 @@ +---------------+------------------------+------------------+----------------+ | pointers | another <cdata> with | a <cdata> |``[]`` `(****)`,| | | a compatible type (i.e.| |``+``, ``-``, | -| | same type or ``char*`` | |bool() | +| | same type | |bool() | | | or ``void*``, or as an | | | | | array instead) `(*)` | | | +---------------+------------------------+ | | -| ``void *``, | another <cdata> with | | | -| ``char *`` | any pointer or array | | | +| ``void *`` | another <cdata> with | | | +| | any pointer or array | | | | | type | | | +---------------+------------------------+ +----------------+ | pointers to | same as pointers | | ``[]``, ``+``, | @@ -624,12 +627,12 @@ *`` argument might be passed as ``[[x, y]]`` or ``[{'x': 5, 'y': 10}]``. - As an optimization, the CPython version of CFFI assumes that a + As an optimization, CFFI assumes that a function with a ``char *`` argument to which you pass a Python string will not actually modify the array of characters passed in, and so passes directly a pointer inside the Python string object. - (PyPy might in the future do the same, but it is harder because - strings are not naturally zero-terminated in PyPy.) + (On PyPy, this optimization is only available since PyPy 5.4 + with CFFI 1.8.) `(**)` C function calls are done with the GIL released. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/doc/source/using.rst new/cffi-1.8.3/doc/source/using.rst --- old/cffi-1.7.0/doc/source/using.rst 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/using.rst 2016-09-17 12:21:34.000000000 +0200 @@ -81,6 +81,33 @@ # away, then the weak dictionary entry will be removed. return s1 +Usually you don't need a weak dict: for example, to call a function with +a ``char * *`` argument that contains a pointer to a ``char *`` pointer, +it is enough to do this: + +.. code-block:: python + + p = ffi.new("char[]", "hello, world") # p is a 'char *' + q = ffi.new("char **", p) # q is a 'char **' + lib.myfunction(q) + # p is alive at least until here, so that's fine + +However, this is always wrong (usage of freed memory): + +.. code-block:: python + + p = ffi.new("char **", ffi.new("char[]", "hello, world")) + # WRONG! as soon as p is built, the inner ffi.new() gets freed! + +This is wrong too, for the same reason: + +.. code-block:: python + + p = ffi.new("struct my_stuff") + p.foo = ffi.new("char[]", "hello, world") + # WRONG! as soon as p.foo is set, the ffi.new() gets freed! + + The cdata objects support mostly the same operations as in C: you can read or write from pointers, arrays and structures. Dereferencing a pointer is done usually in C with the syntax ``*p``, which is not valid @@ -339,8 +366,8 @@ __ ref.html#conversions -CFFI supports passing and returning structs to functions and callbacks. -Example: +CFFI supports passing and returning structs and unions to functions and +callbacks. Example: .. code-block:: python @@ -350,36 +377,33 @@ myfoo = lib.function_returning_a_struct() # `myfoo`: <cdata 'struct foo_s' owning 8 bytes> -There are a few (obscure) limitations to the argument types and return -type. You cannot pass directly as argument a union (but a *pointer* -to a union is fine), nor a struct which uses bitfields (but a -*pointer* to such a struct is fine). If you pass a struct (not a -*pointer* to a struct), the struct type cannot have been declared with -"``...;``" in the ``cdef()``; you need to declare it completely in -``cdef()``. You can work around these limitations by writing a C -function with a simpler signature in the C header code passed to -``ffibuilder.set_source()``, and have this C function call the real one. - -Aside from these limitations, functions and callbacks can receive and -return structs. - -For performance, API-level functions are not returned as ``<cdata>`` -objects, but as a different type (on CPython, ``<built-in -function>``). This means you cannot e.g. pass them to some other C +For performance, non-variadic API-level functions that you get by +writing ``lib.some_function`` are not ``<cdata>`` +objects, but an object of a different type (on CPython, ``<built-in +function>``). This means you cannot pass them directly to some other C function expecting a function pointer argument. Only ``ffi.typeof()`` works on them. To get a cdata containing a regular function pointer, -use ``ffi.addressof(lib, "name")`` (new in version 1.1). - -Before version 1.1 (or with the deprecated ``ffi.verify()``), if you -really need a cdata pointer to the function, use the following -workaround: - -.. code-block:: python - - ffi.cdef(""" int (*foo)(int a, int b); """) +use ``ffi.addressof(lib, "name")``. -i.e. declare them as pointer-to-function in the cdef (even if they are -regular functions in the C code). +There are a few (obscure) limitations to the supported argument and +return types. These limitations come from libffi and apply only to +calling ``<cdata>`` function pointers; in other words, they don't +apply to non-variadic ``cdef()``-declared functions if you are using +the API mode. The limitations are that you cannot pass directly as +argument or return type: + +* a union (but a *pointer* to a union is fine); + +* a struct which uses bitfields (but a *pointer* to such a struct is + fine); + +* a struct that was declared with "``...``" in the ``cdef()``. + +In API mode, you can work around these limitations: for example, if you +need to call such a function pointer from Python, you can instead write +a custom C function that accepts the function pointer and the real +arguments and that does the call from C. Then declare that custom C +function in the ``cdef()`` and use it from Python. Variadic function calls diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/doc/source/whatsnew.rst new/cffi-1.8.3/doc/source/whatsnew.rst --- old/cffi-1.7.0/doc/source/whatsnew.rst 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/doc/source/whatsnew.rst 2016-09-17 12:21:34.000000000 +0200 @@ -3,6 +3,57 @@ ====================== +v1.8.3 +====== + +* When passing a ``void *`` argument to a function with a different + pointer type, or vice-versa, the cast occurs automatically, like in C. + The same occurs for initialization with ``ffi.new()`` and a few other + places. However, I thought that ``char *`` had the same + property---but I was mistaken. In C you get the usual warning if you + try to give a ``char *`` to a ``char **`` argument, for example. + Sorry about the confusion. This has been fixed in CFFI by giving for + now a warning, too. It will turn into an error in a future version. + + +v1.8.2 +====== + +* Issue #283: fixed ``ffi.new()`` on structures/unions with nested + anonymous structures/unions, when there is at least one union in + the mix. When initialized with a list or a dict, it should now + behave more closely like the ``{ }`` syntax does in GCC. + + +v1.8.1 +====== + +* CPython 3.x: experimental: the generated C extension modules now use + the "limited API", which means that, as a compiled .so/.dll, it should + work directly on any version of CPython >= 3.2. The name produced by + distutils is still version-specific. To get the version-independent + name, you can rename it manually to ``NAME.abi3.so``, or use the very + recent setuptools 26. + +* Added ``ffi.compile(debug=...)``, similar to ``python setup.py build + --debug`` but defaulting to True if we are running a debugging + version of Python itself. + + +v1.8 +==== + +* Removed the restriction that ``ffi.from_buffer()`` cannot be used on + byte strings. Now you can get a ``char *`` out of a byte string, + which is valid as long as the string object is kept alive. (But + don't use it to *modify* the string object! If you need this, use + ``bytearray`` or other official techniques.) + +* PyPy 5.4 can now pass a byte string directly to a ``char *`` + argument (in older versions, a copy would be made). This used to be + a CPython-only optimization. + + v1.7 ==== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/setup.py new/cffi-1.8.3/setup.py --- old/cffi-1.7.0/setup.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/setup.py 2016-09-17 12:21:34.000000000 +0200 @@ -144,7 +144,7 @@ `Mailing list https://groups.google.com/forum/#!forum/python-cffi`_ """, - version='1.7.0', + version='1.8.3', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h']} @@ -188,6 +188,7 @@ 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/testing/cffi0/backend_tests.py new/cffi-1.8.3/testing/cffi0/backend_tests.py --- old/cffi-1.7.0/testing/cffi0/backend_tests.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/testing/cffi0/backend_tests.py 2016-09-17 12:21:34.000000000 +0200 @@ -1414,6 +1414,7 @@ assert p.b == 12 assert p.c == 14 assert p.d == 14 + py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0]) def test_nested_field_offset_align(self): ffi = FFI(backend=self.Backend()) @@ -1453,14 +1454,42 @@ assert p.b == 0 assert p.c == 14 assert p.d == 14 - p = ffi.new("union foo_u *", {'b': 12}) - assert p.a == 0 + p = ffi.new("union foo_u *", {'a': -63, 'b': 12}) + assert p.a == -63 assert p.b == 12 - assert p.c == 0 - assert p.d == 0 - # we cannot specify several items in the dict, even though - # in theory in this particular case it would make sense - # to give both 'a' and 'b' + assert p.c == -63 + assert p.d == -63 + p = ffi.new("union foo_u *", [123, 456]) + assert p.a == 123 + assert p.b == 456 + assert p.c == 123 + assert p.d == 123 + py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0]) + + def test_nested_anonymous_struct_2(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + struct foo_s { + int a; + union { int b; union { int c, d; }; }; + int e; + }; + """) + assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT + p = ffi.new("struct foo_s *", [11, 22, 33]) + assert p.a == 11 + assert p.b == p.c == p.d == 22 + assert p.e == 33 + py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44]) + FOO = ffi.typeof("struct foo_s") + fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields] + assert fields == [ + ('a', 0 * SIZE_OF_INT, 0), + ('b', 1 * SIZE_OF_INT, 0), + ('c', 1 * SIZE_OF_INT, 1), + ('d', 1 * SIZE_OF_INT, 1), + ('e', 2 * SIZE_OF_INT, 0), + ] def test_cast_to_array_type(self): ffi = FFI(backend=self.Backend()) @@ -1478,6 +1507,7 @@ assert p1[0] == 123 seen.append(1) q = ffi.gc(p, destructor) + assert ffi.typeof(q) is ffi.typeof(p) import gc; gc.collect() assert seen == [] del q diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/testing/cffi0/test_ctypes.py new/cffi-1.8.3/testing/cffi0/test_ctypes.py --- old/cffi-1.7.0/testing/cffi0/test_ctypes.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/testing/cffi0/test_ctypes.py 2016-09-17 12:21:34.000000000 +0200 @@ -34,6 +34,9 @@ def test_nested_anonymous_union(self): py.test.skip("ctypes backend: not supported: nested anonymous union") + def test_nested_anonymous_struct_2(self): + py.test.skip("ctypes backend: not supported: nested anonymous union") + def test_CData_CType_2(self): if sys.version_info >= (3,): py.test.skip("ctypes backend: not supported in Python 3: CType") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/testing/cffi0/test_ownlib.py new/cffi-1.8.3/testing/cffi0/test_ownlib.py --- old/cffi-1.7.0/testing/cffi0/test_ownlib.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/testing/cffi0/test_ownlib.py 2016-09-17 12:21:34.000000000 +0200 @@ -129,7 +129,7 @@ cls.module = str(udir.join('testownlib.dll')) else: subprocess.check_call( - 'gcc testownlib.c -shared -fPIC -o testownlib.so', + 'cc testownlib.c -shared -fPIC -o testownlib.so', cwd=str(udir), shell=True) cls.module = str(udir.join('testownlib.so')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/testing/cffi0/test_zintegration.py new/cffi-1.8.3/testing/cffi0/test_zintegration.py --- old/cffi-1.7.0/testing/cffi0/test_zintegration.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/testing/cffi0/test_zintegration.py 2016-09-17 12:21:34.000000000 +0200 @@ -148,3 +148,28 @@ p = snip_setuptools_verify2.C.getpwuid(0) assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" ''') + + def test_set_py_limited_api(self): + from cffi.setuptools_ext import _set_py_limited_api + try: + import setuptools + except ImportError as e: + py.test.skip(str(e)) + orig_version = setuptools.__version__ + try: + setuptools.__version__ = '26.0.0' + from setuptools import Extension + + kwds = _set_py_limited_api(Extension, {}) + assert kwds['py_limited_api'] == True + + setuptools.__version__ = '25.0' + kwds = _set_py_limited_api(Extension, {}) + assert not kwds + + setuptools.__version__ = 'development' + kwds = _set_py_limited_api(Extension, {}) + assert kwds['py_limited_api'] == True + + finally: + setuptools.__version__ = orig_version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cffi-1.7.0/testing/cffi1/test_recompiler.py new/cffi-1.8.3/testing/cffi1/test_recompiler.py --- old/cffi-1.7.0/testing/cffi1/test_recompiler.py 2016-06-20 16:30:45.000000000 +0200 +++ new/cffi-1.8.3/testing/cffi1/test_recompiler.py 2016-09-17 12:21:34.000000000 +0200 @@ -851,9 +851,12 @@ assert str(e2.value) == "foo0() takes no arguments (2 given)" assert str(e3.value) == "foo1() takes exactly one argument (0 given)" assert str(e4.value) == "foo1() takes exactly one argument (2 given)" - assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)" - assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)" - assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)" + assert str(e5.value) in ["foo2 expected 2 arguments, got 0", + "foo2() takes exactly 2 arguments (0 given)"] + assert str(e6.value) in ["foo2 expected 2 arguments, got 1", + "foo2() takes exactly 2 arguments (1 given)"] + assert str(e7.value) in ["foo2 expected 2 arguments, got 3", + "foo2() takes exactly 2 arguments (3 given)"] def test_address_of_function(): ffi = FFI() @@ -1915,3 +1918,65 @@ ffi.cdef("bool f(void);") lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") assert lib.f() == 1 + +def test_bool_in_cpp_2(): + ffi = FFI() + ffi.cdef('int add(int a, int b);') + lib = verify(ffi, "test_bool_bug_cpp", ''' + typedef bool _Bool; /* there is a Windows header with this line */ + int add(int a, int b) + { + return a + b; + }''', source_extension='.cpp') + c = lib.add(2, 3) + assert c == 5 + +def test_struct_field_opaque(): + ffi = FFI() + ffi.cdef("struct a { struct b b; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + ffi = FFI() + ffi.cdef("struct a { struct b b[2]; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + ffi = FFI() + ffi.cdef("struct a { struct b b[]; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + +def test_function_arg_opaque(): + py.test.skip("can currently declare a function with an opaque struct " + "as argument, but AFAICT it's impossible to call it later") + +def test_function_returns_opaque(): + ffi = FFI() + ffi.cdef("struct a foo(int);") + e = py.test.raises(TypeError, verify, + ffi, "test_function_returns_opaque", "?") + assert str(e.value) == ("function foo: 'struct a' is used as result type," + " but is opaque") + +def test_function_returns_union(): + ffi = FFI() + ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") + lib = verify(ffi, "test_function_returns_union", """ + union u1 { int a, b; }; + static union u1 f1(int x) { union u1 u; u.b = x; return u; } + """) + assert lib.f1(51).a == 51 + +def test_function_returns_partial_struct(): + ffi = FFI() + ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") + lib = verify(ffi, "test_function_returns_partial_struct", """ + struct aaa { int b, a, c; }; + static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } + """) + assert lib.f1(52).a == 52