diff -Nru python-cffi-0.8.6/c/_cffi_backend.c python-cffi-0.9.2/c/_cffi_backend.c --- python-cffi-0.8.6/c/_cffi_backend.c 2014-07-05 10:57:39.000000000 -0700 +++ python-cffi-0.9.2/c/_cffi_backend.c 2015-03-13 02:02:08.000000000 -0700 @@ -27,6 +27,24 @@ typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; # else # include # endif @@ -68,7 +86,7 @@ # define PyText_FromStringAndSize PyString_FromStringAndSize # define PyText_InternInPlace PyString_InternInPlace # define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op)) -#endif +#endif #if PY_MAJOR_VERSION >= 3 # define PyInt_FromLong PyLong_FromLong @@ -112,6 +130,7 @@ #define CT_IS_FILE 262144 #define CT_IS_VOID_PTR 524288 #define CT_WITH_VAR_ARRAY 1048576 +#define CT_IS_UNSIZED_CHAR_A 2097152 #define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ CT_PRIMITIVE_UNSIGNED | \ CT_PRIMITIVE_CHAR | \ @@ -206,6 +225,12 @@ } CDataObject_own_structptr; typedef struct { + CDataObject head; + Py_ssize_t length; /* same as CDataObject_own_length up to here */ + Py_buffer *bufferview; +} CDataObject_owngc_frombuf; + +typedef struct { ffi_cif cif; /* the following information is used when doing the call: - a buffer of size 'exchange_size' is malloced @@ -740,95 +765,90 @@ return (unsigned PY_LONG_LONG)-1; } +#define _read_raw_data(type) \ + do { \ + if (size == sizeof(type)) { \ + type r; \ + memcpy(&r, target, sizeof(type)); \ + return r; \ + } \ + } while(0) + static PY_LONG_LONG read_raw_signed_data(char *target, int size) { - if (size == sizeof(signed char)) - return *((signed char*)target); - else if (size == sizeof(short)) - return *((short*)target); - else if (size == sizeof(int)) - return *((int*)target); - else if (size == sizeof(long)) - return *((long*)target); - else if (size == sizeof(PY_LONG_LONG)) - return *((PY_LONG_LONG*)target); - else { - Py_FatalError("read_raw_signed_data: bad integer size"); - return 0; - } + _read_raw_data(signed char); + _read_raw_data(short); + _read_raw_data(int); + _read_raw_data(long); + _read_raw_data(PY_LONG_LONG); + Py_FatalError("read_raw_signed_data: bad integer size"); + return 0; } static unsigned PY_LONG_LONG read_raw_unsigned_data(char *target, int size) { - if (size == sizeof(unsigned char)) - return *((unsigned char*)target); - else if (size == sizeof(unsigned short)) - return *((unsigned short*)target); - else if (size == sizeof(unsigned int)) - return *((unsigned int*)target); - else if (size == sizeof(unsigned long)) - return *((unsigned long*)target); - else if (size == sizeof(unsigned PY_LONG_LONG)) - return *((unsigned PY_LONG_LONG*)target); - else { - Py_FatalError("read_raw_unsigned_data: bad integer size"); - return 0; - } + _read_raw_data(unsigned char); + _read_raw_data(unsigned short); + _read_raw_data(unsigned int); + _read_raw_data(unsigned long); + _read_raw_data(unsigned PY_LONG_LONG); + Py_FatalError("read_raw_unsigned_data: bad integer size"); + return 0; } +#define _write_raw_data(type) \ + do { \ + if (size == sizeof(type)) { \ + type r = (type)source; \ + memcpy(target, &r, sizeof(type)); \ + return; \ + } \ + } while(0) + static void write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size) { - if (size == sizeof(unsigned char)) - *((unsigned char*)target) = (unsigned char)source; - else if (size == sizeof(unsigned short)) - *((unsigned short*)target) = (unsigned short)source; - else if (size == sizeof(unsigned int)) - *((unsigned int*)target) = (unsigned int)source; - else if (size == sizeof(unsigned long)) - *((unsigned long*)target) = (unsigned long)source; - else if (size == sizeof(unsigned PY_LONG_LONG)) - *((unsigned PY_LONG_LONG*)target) = source; - else - Py_FatalError("write_raw_integer_data: bad integer size"); + _write_raw_data(unsigned char); + _write_raw_data(unsigned short); + _write_raw_data(unsigned int); + _write_raw_data(unsigned long); + _write_raw_data(unsigned PY_LONG_LONG); + Py_FatalError("write_raw_integer_data: bad integer size"); } static double read_raw_float_data(char *target, int size) { - if (size == sizeof(float)) - return *((float*)target); - else if (size == sizeof(double)) - return *((double*)target); - else { - Py_FatalError("read_raw_float_data: bad float size"); - return 0; - } + _read_raw_data(float); + _read_raw_data(double); + Py_FatalError("read_raw_float_data: bad float size"); + return 0; } static long double read_raw_longdouble_data(char *target) { - return *((long double*)target); + int size = sizeof(long double); + _read_raw_data(long double); + Py_FatalError("read_raw_longdouble_data: bad long double size"); + return 0; } static void write_raw_float_data(char *target, double source, int size) { - if (size == sizeof(float)) - *((float*)target) = (float)source; - else if (size == sizeof(double)) - *((double*)target) = source; - else - Py_FatalError("write_raw_float_data: bad float size"); + _write_raw_data(float); + _write_raw_data(double); + Py_FatalError("write_raw_float_data: bad float size"); } static void write_raw_longdouble_data(char *target, long double source) { - *((long double*)target) = source; + int size = sizeof(long double); + _write_raw_data(long double); } static PyObject * @@ -1248,6 +1268,16 @@ return _convert_error(init, ct->ct_name, expected); } +#ifdef __GNUC__ +# if __GNUC__ >= 4 +/* Don't go inlining this huge function. Needed because occasionally + it gets inlined in places where is causes a warning: call to + __builtin___memcpy_chk will always overflow destination buffer + (which is places where the 'ct' should never represent such a large + primitive type anyway). */ +__attribute__((noinline)) +# endif +#endif static int convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) { @@ -1523,6 +1553,11 @@ Py_XDECREF(args); cffi_closure_free(closure); } + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + PyBuffer_Release(view); + PyObject_Free(view); + } cdata_dealloc(cd); } @@ -1537,6 +1572,10 @@ PyObject *args = (PyObject *)(closure->user_data); Py_VISIT(args); } + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + Py_VISIT(view->obj); + } return 0; } @@ -1554,6 +1593,10 @@ closure->user_data = NULL; Py_XDECREF(args); } + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + PyBuffer_Release(view); + } return 0; } @@ -1656,35 +1699,40 @@ static PyObject *cdataowning_repr(CDataObject *cd) { Py_ssize_t size; - if (cd->c_type->ct_flags & CT_POINTER) { - if (cd->c_type->ct_flags & CT_IS_VOID_PTR) - goto handle_repr; + 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 if (cd->c_type->ct_flags & CT_FUNCTIONPTR) - goto callback_repr; else size = cd->c_type->ct_size; return PyText_FromFormat("", cd->c_type->ct_name, size); +} - callback_repr: - { +static PyObject *cdataowninggc_repr(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ + PyObject *x = (PyObject *)(cd->c_data + 42); + return _cdata_repr2(cd, "handle to", x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data; if (args == NULL) return cdata_repr(cd); else return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1)); } - - handle_repr: - { - PyObject *x = (PyObject *)(cd->c_data + 42); - return _cdata_repr2(cd, "handle to", x); + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + Py_ssize_t buflen = get_array_length(cd); + return PyText_FromFormat( + "", + cd->c_type->ct_name, + buflen, + view->obj ? Py_TYPE(view->obj)->tp_name : "(null)"); } + return cdataowning_repr(cd); } static int cdata_nonzero(CDataObject *cd) @@ -1959,7 +2007,7 @@ if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) && (get_array_length((CDataObject *)v) == length)) { /* fast path: copying from exactly the correct type */ - memcpy(cdata, ((CDataObject *)v)->c_data, itemsize * length); + memmove(cdata, ((CDataObject *)v)->c_data, itemsize * length); return 0; } } @@ -2374,7 +2422,7 @@ ct = ((CDataObject *)obj)->c_type; if (ct->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_SIGNED)) { - if (ct->ct_size < sizeof(int)) { + if (ct->ct_size < (Py_ssize_t)sizeof(int)) { ct = _get_ct_int(); if (ct == NULL) goto error; @@ -2603,14 +2651,14 @@ static PyTypeObject CDataOwningGC_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataOwnGC", - sizeof(CDataObject), + sizeof(CDataObject_owngc_frombuf), 0, (destructor)cdataowninggc_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + (reprfunc)cdataowninggc_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -3065,6 +3113,7 @@ char *data = ((CDataObject *)io)->c_data; /*READ(data, sizeof(long double)*/ lvalue = read_raw_longdouble_data(data); + Py_DECREF(io); cd = _new_casted_primitive(ct); if (cd != NULL) write_raw_longdouble_data(cd->c_data, lvalue); @@ -3317,8 +3366,26 @@ EPTYPE(u32, uint32_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(i64, int64_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(u64, uint64_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il8, int_least8_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul8, uint_least8_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il16, int_least16_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul16, uint_least16_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il32, int_least32_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul32, uint_least32_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il64, int_least64_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul64, uint_least64_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if8, int_fast8_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf8, uint_fast8_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if16, int_fast16_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf16, uint_fast16_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if32, int_fast32_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf32, uint_fast32_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if64, int_fast64_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf64, uint_fast64_t, CT_PRIMITIVE_UNSIGNED) \ EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED) \ EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(im, intmax_t, CT_PRIMITIVE_SIGNED) \ + 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) @@ -3412,11 +3479,11 @@ td->ct_extra = ffitype; td->ct_flags = ptypes->flags; if (td->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_CHAR)) { - if (td->ct_size <= sizeof(long)) + if (td->ct_size <= (Py_ssize_t)sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) { - if (td->ct_size < sizeof(long)) + if (td->ct_size < (Py_ssize_t)sizeof(long)) td->ct_flags |= CT_PRIMITIVE_FITS_LONG; } td->ct_name_position = strlen(td->ct_name); @@ -3424,7 +3491,8 @@ bad_ffi_type: PyErr_Format(PyExc_NotImplementedError, - "primitive type '%s' with a non-standard size %d", + "primitive type '%s' has size %d; " + "the supported sizes are 1, 2, 4, 8", name, (int)ptypes->size); return NULL; } @@ -3478,6 +3546,7 @@ CTypeDescrObject *td, *ctitem; char extra_text[32]; Py_ssize_t length, arraysize; + int flags = CT_ARRAY; if (!(ctptr->ct_flags & CT_POINTER)) { PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype"); @@ -3494,6 +3563,9 @@ sprintf(extra_text, "[]"); length = -1; arraysize = -1; + if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && + ctitem->ct_size == sizeof(char)) + flags |= CT_IS_UNSIZED_CHAR_A; } else { length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError); @@ -3518,7 +3590,7 @@ td->ct_stuff = (PyObject *)ctptr; td->ct_size = arraysize; td->ct_length = length; - td->ct_flags = CT_ARRAY; + td->ct_flags = flags; return (PyObject *)td; } @@ -3622,7 +3694,7 @@ #ifdef MS_WIN32 sflags |= SF_MSVC_BITFIELDS; #else -# ifdef __arm__ +# if defined(__arm__) || defined(__aarch64__) sflags |= SF_GCC_ARM_BITFIELDS; # else sflags |= SF_GCC_X86_BITFIELDS; @@ -4023,24 +4095,26 @@ cf = (CFieldObject *)ct->ct_extra; for (i=0; icf_bitshift >= 0) { - PyErr_SetString(PyExc_NotImplementedError, - "cannot pass as argument or return value " - "a struct with bit fields"); + PyErr_Format(PyExc_NotImplementedError, + "ctype '%s' not supported as argument or return value" + " (it is a struct with bit fields)", + ct->ct_name); return NULL; } flat = 1; - ct = cf->cf_type; - while (ct->ct_flags & CT_ARRAY) { - flat *= ct->ct_length; - ct = ct->ct_itemdescr; + ct1 = cf->cf_type; + while (ct1->ct_flags & CT_ARRAY) { + flat *= ct1->ct_length; + ct1 = ct1->ct_itemdescr; } if (flat <= 0) { - PyErr_SetString(PyExc_NotImplementedError, - "cannot pass as argument or return value " - "a struct with a zero-length array"); + PyErr_Format(PyExc_NotImplementedError, + "ctype '%s' not supported as argument or return value" + " (it is a struct with a zero-length array)", + ct->ct_name); return NULL; } nflat += flat; @@ -4079,9 +4153,10 @@ return ffistruct; } else { + const char *place = is_result_type ? "return value" : "argument"; PyErr_Format(PyExc_NotImplementedError, - "ctype '%s' not supported as argument or return value", - ct->ct_name); + "ctype '%s' (size %zd) not supported as %s", + ct->ct_name, ct->ct_size, place); return NULL; } } @@ -4310,11 +4385,6 @@ &fabi)) return NULL; - if (fresult->ct_flags & CT_UNION) { - PyErr_SetString(PyExc_NotImplementedError, - "function returning a union"); - return NULL; - } if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) || (fresult->ct_flags & CT_ARRAY)) { char *msg; @@ -4338,8 +4408,14 @@ cif_description_t *cif_descr; cif_descr = fb_prepare_cif(fargs, fresult, fabi); - if (cif_descr == NULL) - goto error; + if (cif_descr == NULL) { + if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) { + PyErr_Clear(); /* will get the exception if we see an + actual call */ + } + else + goto error; + } fct->ct_extra = (char *)cif_descr; } @@ -4439,7 +4515,7 @@ #endif f = PySys_GetObject("stderr"); if (f != NULL) { - PyFile_WriteString("From callback ", f); + PyFile_WriteString("From cffi callback ", f); PyFile_WriteObject(obj, f, 0); PyFile_WriteString(":\n", f); if (extra_error_line != NULL) @@ -4574,8 +4650,9 @@ cif_descr = (cif_description_t *)ct->ct_extra; if (cif_descr == NULL) { - PyErr_SetString(PyExc_NotImplementedError, - "callbacks with '...'"); + PyErr_Format(PyExc_NotImplementedError, + "%s: callback with unsupported argument or " + "return type or with '...'", ct->ct_name); goto error; } if (ffi_prep_closure(closure, &cif_descr->cif, @@ -4759,27 +4836,19 @@ CTypeDescrObject *ct; CFieldObject *cf; Py_ssize_t offset; + int following = 0; - if (!PyArg_ParseTuple(args, "O!O:typeof", - &CTypeDescr_Type, &ct, &fieldname)) + if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof", + &CTypeDescr_Type, &ct, &fieldname, &following)) return NULL; - if (fieldname == Py_None) { - if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) { - PyErr_SetString(PyExc_TypeError, - "expected a struct or union ctype"); - return NULL; - } - res = (PyObject *)ct; - offset = 0; - } - else { - if (ct->ct_flags & CT_POINTER) + if (PyTextAny_Check(fieldname)) { + if (!following && (ct->ct_flags & CT_POINTER)) ct = ct->ct_itemdescr; if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) || ct->ct_stuff == NULL) { PyErr_SetString(PyExc_TypeError, - "expected an initialized struct or union ctype, " - "or a pointer to one"); + "with a field name argument, expected an " + "initialized struct or union ctype"); return NULL; } cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname); @@ -4794,6 +4863,29 @@ res = (PyObject *)cf->cf_type; offset = cf->cf_offset; } + else { + ssize_t index = PyInt_AsSsize_t(fieldname); + if (index < 0 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "field name or array index expected"); + return NULL; + } + + if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) || + ct->ct_itemdescr->ct_size < 0) { + PyErr_SetString(PyExc_TypeError, "with an integer argument, " + "expected an array ctype or a " + "pointer to non-opaque"); + return NULL; + } + res = (PyObject *)ct->ct_itemdescr; + offset = 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"); + return NULL; + } + } return Py_BuildValue("(On)", res, offset); } @@ -4801,17 +4893,19 @@ { CTypeDescrObject *ct; CDataObject *cd; - Py_ssize_t offset = 0; + Py_ssize_t offset; + int accepted_flags; - if (!PyArg_ParseTuple(args, "O!O!|n:rawaddressof", + if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof", &CTypeDescr_Type, &ct, &CData_Type, &cd, &offset)) return NULL; - if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION|CT_IS_PTR_TO_OWNED)) == 0) { + accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; + if ((cd->c_type->ct_flags & accepted_flags) == 0) { PyErr_SetString(PyExc_TypeError, - "expected a 'cdata struct-or-union' object"); + "expected a cdata struct/union/array/pointer object"); return NULL; } if ((ct->ct_flags & CT_POINTER) == 0) { @@ -5038,6 +5132,132 @@ return x; } +static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view) +{ +#if PY_MAJOR_VERSION < 3 + /* Some objects only support the buffer interface and CPython doesn't + translate it into the memoryview interface, mess. Hack a very + minimal content for 'view'. Don't care if the other fields are + uninitialized: we only call PyBuffer_Release(), which only reads + 'view->obj'. */ + PyBufferProcs *pb = x->ob_type->tp_as_buffer; + if (pb && !pb->bf_releasebuffer) { + /* we used to try all three in some vaguely sensible order, + i.e. first the write. But trying to call the write on a + read-only buffer fails with TypeError. So we use a less- + sensible order now. See test_from_buffer_more_cases. */ + readbufferproc proc = (readbufferproc)pb->bf_getreadbuffer; + if (!proc) proc = (readbufferproc)pb->bf_getcharbuffer; + if (!proc) proc = (readbufferproc)pb->bf_getwritebuffer; + if (proc && pb->bf_getsegcount) { + if ((*pb->bf_getsegcount)(x, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, + "expected a single-segment buffer object"); + return -1; + } + view->len = (*proc)(x, 0, &view->buf); + if (view->len < 0) + return -1; + view->obj = x; + Py_INCREF(x); + return 0; + } + } +#endif + + if (PyObject_GetBuffer(x, view, PyBUF_SIMPLE) < 0) + return -1; + + if (!PyBuffer_IsContiguous(view, 'A')) { + PyBuffer_Release(view); + PyErr_SetString(PyExc_TypeError, "contiguous buffer expected"); + return -1; + } + return 0; +} + +static int invalid_input_buffer_type(PyObject *x) +{ +#if PY_MAJOR_VERSION < 3 + if (PyBuffer_Check(x)) { + /* XXX fish fish fish in an inofficial way */ + typedef struct { + PyObject_HEAD + PyObject *b_base; + } _my_PyBufferObject; + + _my_PyBufferObject *b = (_my_PyBufferObject *)x; + x = b->b_base; + if (x == NULL) + return 0; + } + else +#endif +#if PY_MAJOR_VERSION > 2 || PY_MINOR_VERSION > 6 + if (PyMemoryView_Check(x)) { + x = PyMemoryView_GET_BASE(x); + if (x == NULL) + return 0; + } + else +#endif + ; + + if (PyBytes_Check(x) || PyUnicode_Check(x)) + return 1; + if (PyByteArray_Check(x)) /* <= this one here for PyPy compatibility */ + return 1; + return 0; +} + +static PyObject *b_from_buffer(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + CDataObject *cd; + PyObject *x; + Py_buffer *view; + + if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x)) + return NULL; + + if (!(ct->ct_flags & CT_IS_UNSIZED_CHAR_A)) { + PyErr_Format(PyExc_TypeError, "needs 'char[]', got '%s'", ct->ct_name); + return NULL; + } + + if (invalid_input_buffer_type(x)) { + PyErr_SetString(PyExc_TypeError, + "from_buffer() cannot return the address of the " + "raw string within a "STR_OR_BYTES" or unicode or " + "bytearray object"); + return NULL; + } + + view = PyObject_Malloc(sizeof(Py_buffer)); + if (_my_PyObject_GetContiguousBuffer(x, view) < 0) + goto error1; + + cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf, + &CDataOwningGC_Type); + if (cd == NULL) + goto error2; + + Py_INCREF(ct); + cd->c_type = ct; + cd->c_data = view->buf; + cd->c_weakreflist = NULL; + ((CDataObject_owngc_frombuf *)cd)->length = view->len; + ((CDataObject_owngc_frombuf *)cd)->bufferview = view; + PyObject_GC_Track(cd); + return (PyObject *)cd; + + error2: + PyBuffer_Release(view); + error1: + PyObject_Free(view); + return NULL; +} + static PyObject *b__get_types(PyObject *self, PyObject *noarg) { return PyTuple_Pack(2, (PyObject *)&CData_Type, @@ -5257,6 +5477,67 @@ return PyLong_FromVoidPtr(f); } +#if PY_MAJOR_VERSION < 3 +static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored) +{ + return 1; +} +static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r) +{ + static char buf[] = "RDB"; + *r = buf; + return 3; +} +static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r) +{ + static char buf[] = "WRB"; + *r = buf; + return 3; +} +static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r) +{ + static char buf[] = "CHB"; + *r = buf; + return 3; +} +#endif +static int _test_getbuf(PyObject *self, Py_buffer *view, int flags) +{ + static char buf[] = "GTB"; + return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags); +} +static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags) +{ + static char buf[] = "ROB"; + return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags); +} + + +static PyObject *b__testbuff(PyObject *self, PyObject *args) +{ + /* for testing only */ + int methods; + PyTypeObject *obj; + if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods)) + return NULL; + + assert(obj->tp_as_buffer != NULL); + +#if PY_MAJOR_VERSION < 3 + obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc; + obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER; + obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; + if (methods & 1) obj->tp_as_buffer->bf_getreadbuffer = &_test_getreadbuf; + if (methods & 2) obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf; + if (methods & 4) obj->tp_as_buffer->bf_getcharbuffer = &_test_getcharbuf; +#endif + if (methods & 8) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf; + if (methods & 16) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf_ro; + + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef FFIBackendMethods[] = { {"load_library", b_load_library, METH_VARARGS}, {"new_primitive_type", b_new_primitive_type, METH_VARARGS}, @@ -5283,11 +5564,13 @@ {"set_errno", b_set_errno, METH_VARARGS}, {"newp_handle", b_newp_handle, METH_VARARGS}, {"from_handle", b_from_handle, METH_O}, + {"from_buffer", b_from_buffer, METH_VARARGS}, #ifdef MS_WIN32 {"getwinerror", b_getwinerror, METH_VARARGS}, #endif {"_get_types", b__get_types, METH_NOARGS}, {"_testfunc", b__testfunc, METH_VARARGS}, + {"_testbuff", b__testbuff, METH_VARARGS}, {NULL, NULL} /* Sentinel */ }; @@ -5504,7 +5787,7 @@ if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; - v = PyText_FromString("0.8.6"); + v = PyText_FromString("0.9.2"); if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) INITERROR; diff -Nru python-cffi-0.8.6/c/malloc_closure.h python-cffi-0.9.2/c/malloc_closure.h --- python-cffi-0.8.6/c/malloc_closure.h 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/c/malloc_closure.h 2015-03-13 02:02:08.000000000 -0700 @@ -14,6 +14,54 @@ # endif #endif +/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. + + This is, apparently, an undocumented change to ffi_prep_closure(): + depending on the Linux kernel we're running on, we must give it a + mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only + PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a + mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!), + but in that situation libffi is fine with only PROT_READ|PROT_WRITE. + There is nothing in the libffi API to know that, though, so we have + to guess by parsing /proc/self/status. "Meh." + */ +#ifdef __linux__ +#include + +static int emutramp_enabled = -1; + +static int +emutramp_enabled_check (void) +{ + char *buf = NULL; + size_t len = 0; + FILE *f; + int ret; + f = fopen ("/proc/self/status", "r"); + if (f == NULL) + return 0; + ret = 0; + + while (getline (&buf, &len, f) != -1) + if (!strncmp (buf, "PaX:", 4)) + { + char emutramp; + if (sscanf (buf, "%*s %*c%c", &emutramp) == 1) + ret = (emutramp == 'E'); + break; + } + free (buf); + fclose (f); + return ret; +} + +#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \ + : (emutramp_enabled = emutramp_enabled_check ())) +#else +#define is_emutramp_enabled() 0 +#endif + + /* 'allocate_num_pages' is dynamically adjusted starting from one page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is meant to handle both the common case of not needing a lot of pages, @@ -77,14 +125,19 @@ if (item == NULL) return; #else + { + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + if (is_emutramp_enabled ()) + prot &= ~PROT_EXEC; item = (union mmaped_block *)mmap(NULL, allocate_num_pages * _pagesize, - PROT_READ | PROT_WRITE | PROT_EXEC, + prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (item == (void *)MAP_FAILED) return; + } #endif #ifdef MALLOC_CLOSURE_DEBUG diff -Nru python-cffi-0.8.6/c/test_c.py python-cffi-0.9.2/c/test_c.py --- python-cffi-0.8.6/c/test_c.py 2014-07-05 10:58:24.000000000 -0700 +++ python-cffi-0.9.2/c/test_c.py 2015-03-13 02:02:08.000000000 -0700 @@ -408,7 +408,7 @@ def test_invalid_indexing(): p = new_primitive_type("int") x = cast(p, 42) - py.test.raises(TypeError, "p[0]") + py.test.raises(TypeError, "x[0]") def test_default_str(): BChar = new_primitive_type("char") @@ -1041,11 +1041,12 @@ BInt = new_primitive_type("int") BArray0 = new_array_type(new_pointer_type(BInt), 0) BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BArray0)]) - py.test.raises(NotImplementedError, new_function_type, - (BStruct,), BInt, False) - py.test.raises(NotImplementedError, new_function_type, - (BInt,), BStruct, False) + BFunc = new_function_type((BStruct,), BInt, False) + py.test.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) + BFunc2 = new_function_type((BInt,), BStruct, False) + py.test.raises(NotImplementedError, cast(BFunc2, 123), 123) def test_call_function_9(): BInt = new_primitive_type("int") @@ -1185,7 +1186,7 @@ assert sys.stderr.getvalue() == '' assert f(10000) == -42 assert matches(sys.stderr.getvalue(), """\ -From callback : +From cffi callback : Traceback (most recent call last): File "$", line $, in Zcb1 $ @@ -1197,7 +1198,7 @@ bigvalue = 20000 assert f(bigvalue) == -42 assert matches(sys.stderr.getvalue(), """\ -From callback : +From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' """) @@ -1816,7 +1817,8 @@ new_function_type((), new_pointer_type(BFunc)) BUnion = new_union_type("union foo_u") complete_struct_or_union(BUnion, []) - py.test.raises(NotImplementedError, new_function_type, (), BUnion) + BFunc = new_function_type((), BUnion) + py.test.raises(NotImplementedError, cast(BFunc, 123)) py.test.raises(TypeError, new_function_type, (), BArray) def test_struct_return_in_func(): @@ -2536,13 +2538,32 @@ ('a2', BChar, -1), ('a3', BChar, -1)]) py.test.raises(TypeError, typeoffsetof, BStructPtr, None) - assert typeoffsetof(BStruct, None) == (BStruct, 0) + py.test.raises(TypeError, typeoffsetof, BStruct, None) assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0) assert typeoffsetof(BStruct, 'a1') == (BChar, 0) assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1) assert typeoffsetof(BStruct, 'a3') == (BChar, 2) + assert typeoffsetof(BStructPtr, 'a2', 0) == (BChar, 1) + assert typeoffsetof(BStruct, u+'a3') == (BChar, 2) + py.test.raises(TypeError, typeoffsetof, BStructPtr, 'a2', 1) py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4') py.test.raises(KeyError, typeoffsetof, BStruct, 'a5') + py.test.raises(TypeError, typeoffsetof, BStruct, 42) + py.test.raises(TypeError, typeoffsetof, BChar, 'a1') + +def test_typeoffsetof_array(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, None) + py.test.raises(TypeError, typeoffsetof, BArray, None) + py.test.raises(TypeError, typeoffsetof, BArray, 'a1') + assert typeoffsetof(BArray, 51) == (BInt, 51 * size_of_int()) + assert typeoffsetof(BIntP, 51) == (BInt, 51 * size_of_int()) + assert typeoffsetof(BArray, -51) == (BInt, -51 * size_of_int()) + MAX = sys.maxsize // size_of_int() + assert typeoffsetof(BArray, MAX) == (BInt, MAX * size_of_int()) + assert typeoffsetof(BIntP, MAX) == (BInt, MAX * size_of_int()) + py.test.raises(OverflowError, typeoffsetof, BArray, MAX + 1) def test_typeoffsetof_no_bitfield(): BInt = new_primitive_type("int") @@ -2562,17 +2583,26 @@ assert repr(p) == "" s = p[0] assert repr(s) == "" - a = rawaddressof(BStructPtr, s) + a = rawaddressof(BStructPtr, s, 0) assert repr(a).startswith("" + p = new_pointer_type(new_primitive_type("unsigned short")) + cast(p, c)[1] += 500 + assert list(a) == [10000, 20500, 30000] + +def test_from_buffer_not_str_unicode_bytearray(): + 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") + py.test.raises(TypeError, from_buffer, BCharA, u"foo") + py.test.raises(TypeError, from_buffer, BCharA, bytearray(b"foo")) + try: + from __builtin__ import buffer + except ImportError: + pass + else: + py.test.raises(TypeError, from_buffer, BCharA, buffer(b"foo")) + py.test.raises(TypeError, from_buffer, BCharA, buffer(u"foo")) + py.test.raises(TypeError, from_buffer, BCharA, + buffer(bytearray(b"foo"))) + try: + from __builtin__ import memoryview + except ImportError: + pass + else: + py.test.raises(TypeError, from_buffer, BCharA, memoryview(b"foo")) + py.test.raises(TypeError, from_buffer, BCharA, + memoryview(bytearray(b"foo"))) + +def test_from_buffer_more_cases(): + try: + from _cffi_backend import _testbuff + except ImportError: + py.test.skip("not for pypy") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + # + def check1(bufobj, expected): + c = from_buffer(BCharA, bufobj) + assert typeof(c) is BCharA + if sys.version_info >= (3,): + expected = [bytes(c, "ascii") for c in expected] + assert list(c) == list(expected) + # + def check(methods, expected, expected_for_memoryview=None): + if sys.version_info >= (3,): + if methods <= 7: + return + if expected_for_memoryview is not None: + expected = expected_for_memoryview + class X(object): + pass + _testbuff(X, methods) + bufobj = X() + check1(bufobj, expected) + try: + from __builtin__ import buffer + bufobjb = buffer(bufobj) + except (TypeError, ImportError): + pass + else: + check1(bufobjb, expected) + try: + bufobjm = memoryview(bufobj) + except (TypeError, NameError): + pass + else: + check1(bufobjm, expected_for_memoryview or expected) + # + check(1, "RDB") + check(2, "WRB") + check(4, "CHB") + check(8, "GTB") + check(16, "ROB") + # + check(1 | 2, "RDB") + check(1 | 4, "RDB") + check(2 | 4, "CHB") + check(1 | 8, "RDB", "GTB") + check(1 | 16, "RDB", "ROB") + check(2 | 8, "WRB", "GTB") + check(2 | 16, "WRB", "ROB") + check(4 | 8, "CHB", "GTB") + check(4 | 16, "CHB", "ROB") + def test_version(): # this test is here mostly for PyPy - assert __version__ == "0.8.6" + assert __version__ == "0.9.2" diff -Nru python-cffi-0.8.6/cffi/api.py python-cffi-0.9.2/cffi/api.py --- python-cffi-0.8.6/cffi/api.py 2014-07-05 10:16:25.000000000 -0700 +++ python-cffi-0.9.2/cffi/api.py 2015-03-13 02:02:08.000000000 -0700 @@ -55,7 +55,8 @@ # _cffi_backend.so compiled. import _cffi_backend as backend from . import __version__ - assert backend.__version__ == __version__ + assert backend.__version__ == __version__, \ + "version mismatch, %s != %s" % (backend.__version__, __version__) # (If you insist you can also try to pass the option # 'backend=backend_ctypes.CTypesBackend()', but don't # rely on it! It's probably not going to work well.) @@ -69,6 +70,7 @@ self._function_caches = [] self._libraries = [] self._cdefsources = [] + self._windows_unicode = None if hasattr(backend, 'set_ffi'): backend.set_ffi(self) for name in backend.__dict__: @@ -77,6 +79,7 @@ # with self._lock: self.BVoidP = self._get_cached_btype(model.voidp_type) + self.BCharA = self._get_cached_btype(model.char_array_type) if isinstance(backend, types.ModuleType): # _cffi_backend: attach these constants to the class if not hasattr(FFI, 'NULL'): @@ -189,13 +192,16 @@ cdecl = self._typeof(cdecl) return self._backend.alignof(cdecl) - def offsetof(self, cdecl, fieldname): + def offsetof(self, cdecl, *fields_or_indexes): """Return the offset of the named field inside the given - structure, which must be given as a C type name. + structure or array, which must be given as a C type name. + You can give several field names in case of nested structures. + You can also give numeric values which correspond to array + items, in case of an array type. """ if isinstance(cdecl, basestring): cdecl = self._typeof(cdecl) - return self._backend.typeoffsetof(cdecl, fieldname)[1] + return self._typeoffsetof(cdecl, *fields_or_indexes)[1] def new(self, cdecl, init=None): """Allocate an instance according to the specified C type and @@ -264,6 +270,16 @@ """ return self._backend.buffer(cdata, size) + def from_buffer(self, python_buffer): + """Return a that points to the data of the + given Python object, which must support the buffer interface. + Note that this is not meant to be used on the built-in types str, + unicode, or bytearray (you can build 'char[]' arrays explicitly) + but only on objects containing large quantities of raw data + in some other format, like 'array.array' or numpy arrays. + """ + return self._backend.from_buffer(self.BCharA, python_buffer) + def callback(self, cdecl, python_callable=None, error=None): """Return a callback object or a decorator making such a callback object. 'cdecl' must name a C function pointer type. @@ -335,9 +351,23 @@ which requires binary compatibility in the signatures. """ from .verifier import Verifier, _caller_dir_pycache + # + # If set_unicode(True) was called, insert the UNICODE and + # _UNICODE macro declarations + if self._windows_unicode: + self._apply_windows_unicode(kwargs) + # + # Set the tmpdir here, and not in Verifier.__init__: it picks + # up the caller's directory, which we want to be the caller of + # ffi.verify(), as opposed to the caller of Veritier(). tmpdir = tmpdir or _caller_dir_pycache() + # + # Make a Verifier() and use it to load the library. self.verifier = Verifier(self, source, tmpdir, **kwargs) lib = self.verifier.load_library() + # + # Save the loaded library for keep-alive purposes, even + # if the caller doesn't keep it alive itself (it should). self._libraries.append(lib) return lib @@ -356,15 +386,29 @@ with self._lock: return model.pointer_cache(self, ctype) - def addressof(self, cdata, field=None): + def addressof(self, cdata, *fields_or_indexes): """Return the address of a . - If 'field' is specified, return the address of this field. + If 'fields_or_indexes' are given, returns the address of that + field or array item in the structure or array, recursively in + case of nested structures. """ ctype = self._backend.typeof(cdata) - ctype, offset = self._backend.typeoffsetof(ctype, field) + if fields_or_indexes: + ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) + else: + if ctype.kind == "pointer": + raise TypeError("addressof(pointer)") + offset = 0 ctypeptr = self._pointer_to(ctype) return self._backend.rawaddressof(ctypeptr, cdata, offset) + def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): + ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) + for field1 in fields_or_indexes: + ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) + offset += offset1 + return ctype, offset + def include(self, ffi_to_include): """Includes the typedefs, structs, unions and enums defined in another FFI instance. Usage is similar to a #include in C, @@ -387,6 +431,44 @@ def from_handle(self, x): return self._backend.from_handle(x) + def set_unicode(self, enabled_flag): + """Windows: if 'enabled_flag' is True, enable the UNICODE and + _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR + to be (pointers to) wchar_t. If 'enabled_flag' is False, + declare these types to be (pointers to) plain 8-bit characters. + This is mostly for backward compatibility; you usually want True. + """ + if self._windows_unicode is not None: + raise ValueError("set_unicode() can only be called once") + enabled_flag = bool(enabled_flag) + if enabled_flag: + self.cdef("typedef wchar_t TBYTE;" + "typedef wchar_t TCHAR;" + "typedef const wchar_t *LPCTSTR;" + "typedef const wchar_t *PCTSTR;" + "typedef wchar_t *LPTSTR;" + "typedef wchar_t *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + else: + self.cdef("typedef char TBYTE;" + "typedef char TCHAR;" + "typedef const char *LPCTSTR;" + "typedef const char *PCTSTR;" + "typedef char *LPTSTR;" + "typedef char *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + self._windows_unicode = enabled_flag + + def _apply_windows_unicode(self, kwds): + defmacros = kwds.get('define_macros', ()) + if not isinstance(defmacros, (list, tuple)): + raise TypeError("'define_macros' must be a list or tuple") + defmacros = list(defmacros) + [('UNICODE', '1'), + ('_UNICODE', '1')] + kwds['define_macros'] = defmacros + def _load_backend_lib(backend, name, flags): if name is None: diff -Nru python-cffi-0.8.6/cffi/backend_ctypes.py python-cffi-0.9.2/cffi/backend_ctypes.py --- python-cffi-0.8.6/cffi/backend_ctypes.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/cffi/backend_ctypes.py 2015-03-13 02:02:08.000000000 -0700 @@ -2,11 +2,10 @@ from . import model if sys.version_info < (3,): - integer_types = (int, long) bytechr = chr else: unicode = str - integer_types = int + long = int xrange = range bytechr = lambda num: bytes([num]) @@ -169,6 +168,7 @@ class CTypesGenericPtr(CTypesData): __slots__ = ['_address', '_as_ctype_ptr'] _automatic_casts = False + kind = "pointer" @classmethod def _newp(cls, init): @@ -180,7 +180,7 @@ address = 0 elif isinstance(source, CTypesData): address = source._cast_to_integer() - elif isinstance(source, integer_types): + elif isinstance(source, (int, long)): address = source else: raise TypeError("bad type for cast to %r: %r" % @@ -357,7 +357,7 @@ is_signed = (ctype(-1).value == -1) # def _cast_source_to_int(source): - if isinstance(source, (integer_types, float)): + if isinstance(source, (int, long, float)): source = int(source) elif isinstance(source, CTypesData): source = source._cast_to_integer() @@ -370,10 +370,12 @@ (CTypesPrimitive, type(source).__name__)) return source # + kind1 = kind class CTypesPrimitive(CTypesGenericPrimitive): __slots__ = ['_value'] _ctype = ctype _reftypename = '%s &' % name + kind = kind1 def __init__(self, value): self._value = value @@ -396,7 +398,7 @@ if kind == 'bool': @classmethod def _cast_from(cls, source): - if not isinstance(source, (integer_types, float)): + if not isinstance(source, (int, long, float)): source = _cast_source_to_int(source) return cls(bool(source)) def __int__(self): @@ -435,7 +437,7 @@ if kind == 'int' or kind == 'byte' or kind == 'bool': @staticmethod def _to_ctypes(x): - if not isinstance(x, integer_types): + if not isinstance(x, (int, long)): if isinstance(x, CTypesData): x = int(x) else: @@ -462,7 +464,7 @@ if kind == 'float': @staticmethod def _to_ctypes(x): - if not isinstance(x, (integer_types, float, CTypesData)): + if not isinstance(x, (int, long, float, CTypesData)): raise TypeError("float expected, got %s" % type(x).__name__) return ctype(x).value @@ -526,14 +528,14 @@ self._own = True def __add__(self, other): - if isinstance(other, integer_types): + if isinstance(other, (int, long)): return self._new_pointer_at(self._address + other * self._bitem_size) else: return NotImplemented def __sub__(self, other): - if isinstance(other, integer_types): + if isinstance(other, (int, long)): return self._new_pointer_at(self._address - other * self._bitem_size) elif type(self) is type(other): @@ -608,7 +610,7 @@ def __init__(self, init): if length is None: - if isinstance(init, integer_types): + if isinstance(init, (int, long)): len1 = init init = None elif kind == 'char' and isinstance(init, bytes): @@ -683,7 +685,7 @@ return CTypesPtr._arg_to_ctypes(value) def __add__(self, other): - if isinstance(other, integer_types): + if isinstance(other, (int, long)): return CTypesPtr._new_pointer_at( ctypes.addressof(self._blob) + other * ctypes.sizeof(BItem._ctype)) @@ -703,12 +705,13 @@ class struct_or_union(base_ctypes_class): pass struct_or_union.__name__ = '%s_%s' % (kind, name) + kind1 = kind # class CTypesStructOrUnion(CTypesBaseStructOrUnion): __slots__ = ['_blob'] _ctype = struct_or_union _reftypename = '%s &' % (name,) - _kind = kind + _kind = kind = kind1 # CTypesStructOrUnion._fix_class() return CTypesStructOrUnion @@ -994,27 +997,42 @@ def getcname(self, BType, replace_with): return BType._get_c_name(replace_with) - def typeoffsetof(self, BType, fieldname): - if fieldname is not None and issubclass(BType, CTypesGenericPtr): - BType = BType._BItem - if not issubclass(BType, CTypesBaseStructOrUnion): - raise TypeError("expected a struct or union ctype") - if fieldname is None: - return (BType, 0) - else: + def typeoffsetof(self, BType, fieldname, num=0): + if isinstance(fieldname, str): + if num == 0 and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") BField = BType._bfield_types[fieldname] if BField is Ellipsis: raise TypeError("not supported for bitfields") return (BField, BType._offsetof(fieldname)) + elif isinstance(fieldname, (int, long)): + if issubclass(BType, CTypesGenericArray): + BType = BType._CTPtr + if not issubclass(BType, CTypesGenericPtr): + raise TypeError("expected an array or ptr ctype") + BItem = BType._BItem + offset = BItem._get_size() * fieldname + if offset > sys.maxsize: + raise OverflowError + return (BItem, offset) + else: + raise TypeError(type(fieldname)) - def rawaddressof(self, BTypePtr, cdata, offset): + def rawaddressof(self, BTypePtr, cdata, offset=None): if isinstance(cdata, CTypesBaseStructOrUnion): ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) elif isinstance(cdata, CTypesGenericPtr): + if offset is None or not issubclass(type(cdata)._BItem, + CTypesBaseStructOrUnion): + raise TypeError("unexpected cdata type") + ptr = type(cdata)._to_ctypes(cdata) + elif isinstance(cdata, CTypesGenericArray): ptr = type(cdata)._to_ctypes(cdata) else: raise TypeError("expected a ") - if offset != 0: + if offset: ptr = ctypes.cast( ctypes.c_void_p( ctypes.cast(ptr, ctypes.c_void_p).value + offset), diff -Nru python-cffi-0.8.6/cffi/commontypes.py python-cffi-0.9.2/cffi/commontypes.py --- python-cffi-0.8.6/cffi/commontypes.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/cffi/commontypes.py 2015-03-13 02:02:08.000000000 -0700 @@ -29,6 +29,9 @@ result = model.PointerType(resolve_common_type(result[:-2])) elif result in model.PrimitiveType.ALL_PRIMITIVE_TYPES: result = model.PrimitiveType(result) + elif result == 'set-unicode-needed': + raise api.FFIError("The Windows type %r is only available after " + "you call ffi.set_unicode()" % (commontype,)) else: if commontype == result: raise api.FFIError("Unsupported type: %r. Please file a bug " @@ -86,8 +89,6 @@ "ULONGLONG": "unsigned long long", "WCHAR": "wchar_t", "SHORT": "short", - "TBYTE": "WCHAR", - "TCHAR": "WCHAR", "UCHAR": "unsigned char", "UINT": "unsigned int", "UINT8": "unsigned char", @@ -157,14 +158,12 @@ "LPCVOID": model.const_voidp_type, "LPCWSTR": "const WCHAR *", - "LPCTSTR": "LPCWSTR", "LPDWORD": "DWORD *", "LPHANDLE": "HANDLE *", "LPINT": "int *", "LPLONG": "long *", "LPSTR": "CHAR *", "LPWSTR": "WCHAR *", - "LPTSTR": "LPWSTR", "LPVOID": model.voidp_type, "LPWORD": "WORD *", "LRESULT": "LONG_PTR", @@ -173,7 +172,6 @@ "PBYTE": "BYTE *", "PCHAR": "CHAR *", "PCSTR": "const CHAR *", - "PCTSTR": "LPCWSTR", "PCWSTR": "const WCHAR *", "PDWORD": "DWORD *", "PDWORDLONG": "DWORDLONG *", @@ -200,9 +198,6 @@ "PSIZE_T": "SIZE_T *", "PSSIZE_T": "SSIZE_T *", "PSTR": "CHAR *", - "PTBYTE": "TBYTE *", - "PTCHAR": "TCHAR *", - "PTSTR": "LPWSTR", "PUCHAR": "UCHAR *", "PUHALF_PTR": "UHALF_PTR *", "PUINT": "UINT *", @@ -240,6 +235,15 @@ "USN": "LONGLONG", "VOID": model.void_type, "WPARAM": "UINT_PTR", + + "TBYTE": "set-unicode-needed", + "TCHAR": "set-unicode-needed", + "LPCTSTR": "set-unicode-needed", + "PCTSTR": "set-unicode-needed", + "LPTSTR": "set-unicode-needed", + "PTSTR": "set-unicode-needed", + "PTBYTE": "set-unicode-needed", + "PTCHAR": "set-unicode-needed", }) return result diff -Nru python-cffi-0.8.6/cffi/cparser.py python-cffi-0.9.2/cffi/cparser.py --- python-cffi-0.8.6/cffi/cparser.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/cffi/cparser.py 2015-03-13 02:02:08.000000000 -0700 @@ -1,4 +1,3 @@ - from . import api, model from .commontypes import COMMON_TYPES, resolve_common_type try: @@ -209,6 +208,8 @@ def _add_constants(self, key, val): if key in self._int_constants: + if self._int_constants[key] == val: + return # ignore identical double declarations raise api.FFIError( "multiple declarations of constant: %s" % (key,)) self._int_constants[key] = val @@ -228,12 +229,18 @@ pyvalue = int(int_str, 0) self._add_constants(key, pyvalue) + self._declare('macro ' + key, pyvalue) elif value == '...': self._declare('macro ' + key, value) else: - raise api.CDefError('only supports the syntax "#define ' - '%s ..." (literally) or "#define ' - '%s 0x1FF" for now' % (key, key)) + raise api.CDefError( + 'only supports one of the following syntax:\n' + ' #define %s ... (literally dot-dot-dot)\n' + ' #define %s NUMBER (with NUMBER an integer' + ' constant, decimal/hex/octal)\n' + 'got:\n' + ' #define %s %s' + % (key, key, key, value)) def _parse_decl(self, decl): node = decl.type @@ -460,6 +467,8 @@ elif kind == 'union': tp = model.UnionType(explicit_name, None, None, None) elif kind == 'enum': + if explicit_name == '__dotdotdot__': + raise CDefError("Enums cannot be declared with ...") tp = self._build_enum_type(explicit_name, type.values) else: raise AssertionError("kind = %r" % (kind,)) @@ -532,9 +541,24 @@ def _parse_constant(self, exprnode, partial_length_ok=False): # for now, limited to expressions that are an immediate number - # or negative number + # or positive/negative number if isinstance(exprnode, pycparser.c_ast.Constant): - return int(exprnode.value, 0) + s = exprnode.value + if s.startswith('0'): + if s.startswith('0x') or s.startswith('0X'): + return int(s, 16) + return int(s, 8) + elif '1' <= s[0] <= '9': + return int(s, 10) + elif s[0] == "'" and s[-1] == "'" and ( + len(s) == 3 or (len(s) == 4 and s[1] == "\\")): + return ord(s[-2]) + else: + raise api.CDefError("invalid constant %r" % (s,)) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '+'): + return self._parse_constant(exprnode.expr) # if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and exprnode.op == '-'): diff -Nru python-cffi-0.8.6/cffi/ffiplatform.py python-cffi-0.9.2/cffi/ffiplatform.py --- python-cffi-0.8.6/cffi/ffiplatform.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/cffi/ffiplatform.py 2015-03-13 02:02:08.000000000 -0700 @@ -11,6 +11,9 @@ """ +LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', + 'extra_objects', 'depends'] + def get_extension(srcfilename, modname, sources=(), **kwds): from distutils.core import Extension allsources = [srcfilename] diff -Nru python-cffi-0.8.6/cffi/__init__.py python-cffi-0.9.2/cffi/__init__.py --- python-cffi-0.8.6/cffi/__init__.py 2014-07-05 10:57:32.000000000 -0700 +++ python-cffi-0.9.2/cffi/__init__.py 2015-03-13 02:02:08.000000000 -0700 @@ -4,5 +4,10 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "0.8.6" -__version_info__ = (0, 8, 6) +__version__ = "0.9.2" +__version_info__ = (0, 9, 2) + +# 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__ +# if nothing is clearly incompatible. +__version_verifier_modules__ = "0.8.6" diff -Nru python-cffi-0.8.6/cffi/model.py python-cffi-0.9.2/cffi/model.py --- python-cffi-0.8.6/cffi/model.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/cffi/model.py 2015-03-13 02:02:08.000000000 -0700 @@ -235,6 +235,8 @@ BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) +char_array_type = ArrayType(PrimitiveType('char'), None) + class StructOrUnionOrEnum(BaseTypeByIdentity): _attrs_ = ('name',) @@ -478,7 +480,7 @@ try: res = getattr(ffi._backend, funcname)(*args) except NotImplementedError as e: - raise NotImplementedError("%r: %s" % (srctype, e)) + raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) # note that setdefault() on WeakValueDictionary is not atomic # and contains a rare bug (http://bugs.python.org/issue19542); # we have to use a lock and do it ourselves diff -Nru python-cffi-0.8.6/cffi/vengine_cpy.py python-cffi-0.9.2/cffi/vengine_cpy.py --- python-cffi-0.8.6/cffi/vengine_cpy.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/cffi/vengine_cpy.py 2015-03-13 02:02:08.000000000 -0700 @@ -65,7 +65,7 @@ # The following two 'chained_list_constants' items contains # the head of these two chained lists, as a string that gives the # call to do, if any. - self._chained_list_constants = ['0', '0'] + self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] # prnt = self._prnt # first paste some standard set of lines that are mostly '#define' @@ -138,15 +138,22 @@ prnt() prnt('#endif') - def load_library(self): + def load_library(self, flags=None): # XXX review all usages of 'self' here! # import it as a new extension module + if hasattr(sys, "getdlopenflags"): + previous_flags = sys.getdlopenflags() try: + if hasattr(sys, "setdlopenflags") and flags is not None: + sys.setdlopenflags(flags) module = imp.load_dynamic(self.verifier.get_module_name(), self.verifier.modulefilename) except ImportError as e: error = "importing %r: %s" % (self.verifier.modulefilename, e) raise ffiplatform.VerificationError(error) + finally: + if hasattr(sys, "setdlopenflags"): + sys.setdlopenflags(previous_flags) # # call loading_cpy_struct() to get the struct layout inferred by # the C compiler @@ -228,7 +235,8 @@ converter = '_cffi_to_c_int' extraarg = ', %s' % tp.name else: - converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),) + converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), + tp.name.replace(' ', '_')) errvalue = '-1' # elif isinstance(tp, model.PointerType): @@ -267,8 +275,8 @@ self._prnt(' if (datasize != 0) {') self._prnt(' if (datasize < 0)') self._prnt(' %s;' % errcode) - self._prnt(' %s = alloca(datasize);' % (tovar,)) - self._prnt(' memset((void *)%s, 0, datasize);' % (tovar,)) + self._prnt(' %s = alloca((size_t)datasize);' % (tovar,)) + self._prnt(' memset((void *)%s, 0, (size_t)datasize);' % (tovar,)) self._prnt(' if (_cffi_convert_array_from_object(' '(char *)%s, _cffi_type(%d), %s) < 0)' % ( tovar, self._gettypenum(tp), fromvar)) @@ -336,7 +344,7 @@ prnt = self._prnt numargs = len(tp.args) if numargs == 0: - argname = 'no_arg' + argname = 'noarg' elif numargs == 1: argname = 'arg0' else: @@ -386,6 +394,9 @@ prnt(' Py_END_ALLOW_THREADS') prnt() # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') if result_code: prnt(' return %s;' % self._convert_expr_from_c(tp.result, 'result', 'result type')) @@ -452,6 +463,7 @@ prnt('static void %s(%s *p)' % (checkfuncname, cname)) prnt('{') prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') for fname, ftype, fbitsize in tp.enumfields(): if (isinstance(ftype, model.PrimitiveType) and ftype.is_integer_type()) or fbitsize >= 0: @@ -482,6 +494,8 @@ prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) prnt(' -1') prnt(' };') + prnt(' (void)self; /* unused */') + prnt(' (void)noarg; /* unused */') prnt(' return _cffi_get_struct_layout(nums);') prnt(' /* the next line is not executed, but compiled */') prnt(' %s(0);' % (checkfuncname,)) @@ -578,7 +592,8 @@ # constants, likely declared with '#define' def _generate_cpy_const(self, is_int, name, tp=None, category='const', - vartp=None, delayed=True, size_too=False): + vartp=None, delayed=True, size_too=False, + check_value=None): prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) prnt('static int %s(PyObject *lib)' % funcname) @@ -590,6 +605,9 @@ else: assert category == 'const' # + if check_value is not None: + self._check_int_constant_value(name, check_value) + # if not is_int: if category == 'var': realexpr = '&' + name @@ -637,6 +655,27 @@ # ---------- # enums + def _check_int_constant_value(self, name, value, err_prefix=''): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % + name) + prnt(' PyErr_Format(_cffi_VerificationError,') + prnt(' "%s%s has the real value %s, not %s",') + prnt(' "%s", "%s", buf, "%d");' % ( + err_prefix, name, value)) + prnt(' return -1;') + prnt(' }') + def _enum_funcname(self, prefix, name): # "$enum_$1" => "___D_enum____D_1" name = name.replace('$', '___D_') @@ -653,25 +692,8 @@ prnt('static int %s(PyObject *lib)' % funcname) prnt('{') for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - if enumvalue < 0: - prnt(' if ((%s) >= 0 || (long)(%s) != %dL) {' % ( - enumerator, enumerator, enumvalue)) - else: - prnt(' if ((%s) < 0 || (unsigned long)(%s) != %dUL) {' % ( - enumerator, enumerator, enumvalue)) - prnt(' char buf[64];') - prnt(' if ((%s) < 0)' % enumerator) - prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % enumerator) - prnt(' else') - prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % - enumerator) - prnt(' PyErr_Format(_cffi_VerificationError,') - prnt(' "enum %s: %s has the real value %s, ' - 'not %s",') - prnt(' "%s", "%s", buf, "%d");' % ( - name, enumerator, enumvalue)) - prnt(' return -1;') - prnt(' }') + self._check_int_constant_value(enumerator, enumvalue, + "enum %s: " % name) prnt(' return %s;' % self._chained_list_constants[True]) self._chained_list_constants[True] = funcname + '(lib)' prnt('}') @@ -695,8 +717,11 @@ # macros: for now only for integers def _generate_cpy_macro_decl(self, tp, name): - assert tp == '...' - self._generate_cpy_const(True, name) + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) _generate_cpy_macro_collecttype = _generate_nothing _generate_cpy_macro_method = _generate_nothing @@ -783,6 +808,24 @@ typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; # else # include # endif @@ -828,12 +871,15 @@ PyLong_FromLongLong((long long)(x))) #define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? PyInt_FromLong(x) : \ - sizeof(type) == sizeof(long) ? PyLong_FromUnsignedLong(x) : \ - PyLong_FromUnsignedLongLong(x)) \ - : (sizeof(type) <= sizeof(long) ? PyInt_FromLong(x) : \ - PyLong_FromLongLong(x))) + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) #define _cffi_to_c_int(o, type) \ (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ @@ -844,7 +890,7 @@ : (type)_cffi_to_c_i32(o)) : \ sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), 0)) + (Py_FatalError("unsupported size for type " #type), (type)0)) #define _cffi_to_c_i8 \ ((int(*)(PyObject *))_cffi_exports[1]) @@ -907,6 +953,7 @@ { PyObject *library; int was_alive = (_cffi_types != NULL); + (void)self; /* unused */ if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, &library)) return NULL; diff -Nru python-cffi-0.8.6/cffi/vengine_gen.py python-cffi-0.9.2/cffi/vengine_gen.py --- python-cffi-0.8.6/cffi/vengine_gen.py 2014-07-05 10:49:29.000000000 -0700 +++ python-cffi-0.9.2/cffi/vengine_gen.py 2015-03-13 02:02:09.000000000 -0700 @@ -58,12 +58,12 @@ modname = self.verifier.get_module_name() prnt("void %s%s(void) { }\n" % (prefix, modname)) - def load_library(self): + def load_library(self, flags=0): # import it with the CFFI backend backend = self.ffi._backend # needs to make a path that contains '/', on Posix filename = os.path.join(os.curdir, self.verifier.modulefilename) - module = backend.load_library(filename) + module = backend.load_library(filename, flags) # # call loading_gen_struct() to get the struct layout inferred by # the C compiler @@ -235,6 +235,7 @@ prnt('static void %s(%s *p)' % (checkfuncname, cname)) prnt('{') prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') for fname, ftype, fbitsize in tp.enumfields(): if (isinstance(ftype, model.PrimitiveType) and ftype.is_integer_type()) or fbitsize >= 0: @@ -354,11 +355,20 @@ # ---------- # constants, likely declared with '#define' - def _generate_gen_const(self, is_int, name, tp=None, category='const'): + def _generate_gen_const(self, is_int, name, tp=None, category='const', + check_value=None): prnt = self._prnt funcname = '_cffi_%s_%s' % (category, name) self.export_symbols.append(funcname) - if is_int: + if check_value is not None: + assert is_int + assert category == 'const' + prnt('int %s(char *out_error)' % funcname) + prnt('{') + self._check_int_constant_value(name, check_value) + prnt(' return 0;') + prnt('}') + elif is_int: assert category == 'const' prnt('int %s(long long *out_value)' % funcname) prnt('{') @@ -367,6 +377,7 @@ prnt('}') else: assert tp is not None + assert check_value is None prnt(tp.get_c_name(' %s(void)' % funcname, name),) prnt('{') if category == 'var': @@ -383,9 +394,13 @@ _loading_gen_constant = _loaded_noop - def _load_constant(self, is_int, tp, name, module): + def _load_constant(self, is_int, tp, name, module, check_value=None): funcname = '_cffi_const_%s' % name - if is_int: + if check_value is not None: + assert is_int + self._load_known_int_constant(module, funcname) + value = check_value + elif is_int: BType = self.ffi._typeof_locked("long long*")[0] BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] function = module.load_function(BFunc, funcname) @@ -396,6 +411,7 @@ BLongLong = self.ffi._typeof_locked("long long")[0] value += (1 << (8*self.ffi.sizeof(BLongLong))) else: + assert check_value is None BFunc = self.ffi._typeof_locked(tp.get_c_name('(*)(void)', name))[0] function = module.load_function(BFunc, funcname) value = function() @@ -410,6 +426,36 @@ # ---------- # enums + def _check_int_constant_value(self, name, value): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % + name) + prnt(' sprintf(out_error, "%s has the real value %s, not %s",') + prnt(' "%s", buf, "%d");' % (name[:100], value)) + prnt(' return -1;') + prnt(' }') + + def _load_known_int_constant(self, module, funcname): + BType = self.ffi._typeof_locked("char[]")[0] + BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType, 256) + if function(p) < 0: + error = self.ffi.string(p) + if sys.version_info >= (3,): + error = str(error, 'utf-8') + raise ffiplatform.VerificationError(error) + def _enum_funcname(self, prefix, name): # "$enum_$1" => "___D_enum____D_1" name = name.replace('$', '___D_') @@ -427,24 +473,7 @@ prnt('int %s(char *out_error)' % funcname) prnt('{') for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - if enumvalue < 0: - prnt(' if ((%s) >= 0 || (long)(%s) != %dL) {' % ( - enumerator, enumerator, enumvalue)) - else: - prnt(' if ((%s) < 0 || (unsigned long)(%s) != %dUL) {' % ( - enumerator, enumerator, enumvalue)) - prnt(' char buf[64];') - prnt(' if ((%s) < 0)' % enumerator) - prnt(' sprintf(buf, "%%ld", (long)(%s));' % enumerator) - prnt(' else') - prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % - enumerator) - prnt(' sprintf(out_error,' - ' "%s has the real value %s, not %s",') - prnt(' "%s", buf, "%d");' % ( - enumerator[:100], enumvalue)) - prnt(' return -1;') - prnt(' }') + self._check_int_constant_value(enumerator, enumvalue) prnt(' return 0;') prnt('}') prnt() @@ -456,16 +485,8 @@ tp.enumvalues = tuple(enumvalues) tp.partial_resolved = True else: - BType = self.ffi._typeof_locked("char[]")[0] - BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] funcname = self._enum_funcname(prefix, name) - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType, 256) - if function(p) < 0: - error = self.ffi.string(p) - if sys.version_info >= (3,): - error = str(error, 'utf-8') - raise ffiplatform.VerificationError(error) + self._load_known_int_constant(module, funcname) def _loaded_gen_enum(self, tp, name, module, library): for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): @@ -476,13 +497,21 @@ # macros: for now only for integers def _generate_gen_macro_decl(self, tp, name): - assert tp == '...' - self._generate_gen_const(True, name) + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_gen_const(True, name, check_value=check_value) _loading_gen_macro = _loaded_noop def _loaded_gen_macro(self, tp, name, module, library): - value = self._load_constant(True, tp, name, module) + if tp == '...': + check_value = None + else: + check_value = tp # an integer + value = self._load_constant(True, tp, name, module, + check_value=check_value) setattr(library, name, value) type(library)._cffi_dir.append(name) @@ -565,6 +594,24 @@ typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; # else # include # endif diff -Nru python-cffi-0.8.6/cffi/verifier.py python-cffi-0.9.2/cffi/verifier.py --- python-cffi-0.8.6/cffi/verifier.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/cffi/verifier.py 2015-03-13 02:02:09.000000000 -0700 @@ -1,12 +1,33 @@ -import sys, os, binascii, imp, shutil -from . import __version__ +import sys, os, binascii, shutil, io +from . import __version_verifier_modules__ from . import ffiplatform +if sys.version_info >= (3, 3): + import importlib.machinery + def _extension_suffixes(): + return importlib.machinery.EXTENSION_SUFFIXES[:] +else: + import imp + def _extension_suffixes(): + return [suffix for suffix, _, type in imp.get_suffixes() + if type == imp.C_EXTENSION] + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super(NativeIO, self).write(s) + class Verifier(object): def __init__(self, ffi, preamble, tmpdir=None, modulename=None, - ext_package=None, tag='', force_generic_engine=False, **kwds): + ext_package=None, tag='', force_generic_engine=False, + source_extension='.c', flags=None, relative_to=None, **kwds): self.ffi = ffi self.preamble = preamble if not modulename: @@ -14,14 +35,15 @@ vengine_class = _locate_engine_class(ffi, force_generic_engine) self._vengine = vengine_class(self) self._vengine.patch_extension_kwds(kwds) - self.kwds = kwds + self.flags = flags + self.kwds = self.make_relative_to(kwds, relative_to) # if modulename: if tag: raise TypeError("can't specify both 'modulename' and 'tag'") else: - key = '\x00'.join([sys.version[:3], __version__, preamble, - flattened_kwds] + + key = '\x00'.join([sys.version[:3], __version_verifier_modules__, + preamble, flattened_kwds] + ffi._cdefsources) if sys.version_info >= (3,): key = key.encode('utf-8') @@ -33,7 +55,7 @@ k1, k2) suffix = _get_so_suffixes()[0] self.tmpdir = tmpdir or _caller_dir_pycache() - self.sourcefilename = os.path.join(self.tmpdir, modulename + '.c') + self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) self.ext_package = ext_package self._has_source = False @@ -97,6 +119,20 @@ def generates_python_module(self): return self._vengine._gen_python_module + def make_relative_to(self, kwds, relative_to): + if relative_to and os.path.dirname(relative_to): + dirname = os.path.dirname(relative_to) + kwds = kwds.copy() + for key in ffiplatform.LIST_OF_FILE_NAMES: + if key in kwds: + lst = kwds[key] + if not isinstance(lst, (list, tuple)): + raise TypeError("keyword '%s' should be a list or tuple" + % (key,)) + lst = [os.path.join(dirname, fn) for fn in lst] + kwds[key] = lst + return kwds + # ---------- def _locate_module(self): @@ -118,19 +154,36 @@ self._vengine.collect_types() self._has_module = True - def _write_source(self, file=None): - must_close = (file is None) - if must_close: - _ensure_dir(self.sourcefilename) - file = open(self.sourcefilename, 'w') + def _write_source_to(self, file): self._vengine._f = file try: self._vengine.write_source_to_f() finally: del self._vengine._f - if must_close: - file.close() - if must_close: + + def _write_source(self, file=None): + if file is not None: + self._write_source_to(file) + else: + # Write our source file to an in memory file. + f = NativeIO() + self._write_source_to(f) + source_data = f.getvalue() + + # Determine if this matches the current file + if os.path.exists(self.sourcefilename): + with open(self.sourcefilename, "r") as fp: + needs_written = not (fp.read() == source_data) + else: + needs_written = True + + # Actually write the file out if it doesn't match + if needs_written: + _ensure_dir(self.sourcefilename) + with open(self.sourcefilename, "w") as fp: + fp.write(source_data) + + # Set this flag self._has_source = True def _compile_module(self): @@ -148,7 +201,10 @@ def _load_library(self): assert self._has_module - return self._vengine.load_library() + if self.flags is not None: + return self._vengine.load_library(self.flags) + else: + return self._vengine.load_library() # ____________________________________________________________ @@ -181,6 +237,9 @@ def _caller_dir_pycache(): if _TMPDIR: return _TMPDIR + result = os.environ.get('CFFI_TMPDIR') + if result: + return result filename = sys._getframe(2).f_code.co_filename return os.path.abspath(os.path.join(os.path.dirname(filename), '__pycache__')) @@ -222,11 +281,7 @@ pass def _get_so_suffixes(): - suffixes = [] - for suffix, mode, type in imp.get_suffixes(): - if type == imp.C_EXTENSION: - suffixes.append(suffix) - + suffixes = _extension_suffixes() if not suffixes: # bah, no C_EXTENSION available. Occurs on pypy without cpyext if sys.platform == 'win32': diff -Nru python-cffi-0.8.6/cffi.egg-info/PKG-INFO python-cffi-0.9.2/cffi.egg-info/PKG-INFO --- python-cffi-0.8.6/cffi.egg-info/PKG-INFO 2014-07-05 11:06:59.000000000 -0700 +++ python-cffi-0.9.2/cffi.egg-info/PKG-INFO 2015-03-13 02:03:30.000000000 -0700 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 0.8.6 +Version: 0.9.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff -Nru python-cffi-0.8.6/cffi.egg-info/SOURCES.txt python-cffi-0.9.2/cffi.egg-info/SOURCES.txt --- python-cffi-0.8.6/cffi.egg-info/SOURCES.txt 2014-07-05 11:06:59.000000000 -0700 +++ python-cffi-0.9.2/cffi.egg-info/SOURCES.txt 2015-03-13 02:03:30.000000000 -0700 @@ -82,22 +82,15 @@ testing/udir.py testing/snippets/distutils_module/setup.py testing/snippets/distutils_module/snip_basic_verify.py -testing/snippets/distutils_module/build/lib.linux-x86_64-2.7/snip_basic_verify.py testing/snippets/distutils_package_1/setup.py -testing/snippets/distutils_package_1/build/lib.linux-x86_64-2.7/snip_basic_verify1/__init__.py testing/snippets/distutils_package_1/snip_basic_verify1/__init__.py testing/snippets/distutils_package_2/setup.py -testing/snippets/distutils_package_2/build/lib.linux-x86_64-2.7/snip_basic_verify2/__init__.py testing/snippets/distutils_package_2/snip_basic_verify2/__init__.py testing/snippets/infrastructure/setup.py -testing/snippets/infrastructure/build/lib.linux-x86_64-2.7/snip_infrastructure/__init__.py testing/snippets/infrastructure/snip_infrastructure/__init__.py testing/snippets/setuptools_module/setup.py testing/snippets/setuptools_module/snip_setuptools_verify.py -testing/snippets/setuptools_module/build/lib.linux-x86_64-2.7/snip_setuptools_verify.py testing/snippets/setuptools_package_1/setup.py -testing/snippets/setuptools_package_1/build/lib.linux-x86_64-2.7/snip_setuptools_verify1/__init__.py testing/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py testing/snippets/setuptools_package_2/setup.py -testing/snippets/setuptools_package_2/build/lib.linux-x86_64-2.7/snip_setuptools_verify2/__init__.py testing/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py \ No newline at end of file diff -Nru python-cffi-0.8.6/debian/changelog python-cffi-0.9.2/debian/changelog --- python-cffi-0.8.6/debian/changelog 2014-07-24 01:55:19.000000000 -0700 +++ python-cffi-0.9.2/debian/changelog 2015-03-19 09:10:17.000000000 -0700 @@ -1,3 +1,35 @@ +python-cffi (0.9.2-1ubuntu1) vivid; urgency=medium + + * Merge from Debian experimental. Remaining changes: + - Drop the build dependency on python-virtualenv (universe), it is an + optional test. + + -- Stefano Rivera Tue, 17 Mar 2015 21:24:23 -0700 + +python-cffi (0.9.2-1) experimental; urgency=medium + + * New upstream release. + + -- Stefano Rivera Tue, 17 Mar 2015 21:24:23 -0700 + +python-cffi (0.9.0-1) experimental; urgency=medium + + * New upstream release. (Closes: #733517, 774787) + * Drop patches, applied upstream. + * Point watch file at pypi.debian.net. + * Update copyright years. + * Bump Standards-Version to 3.9.6, no changes needed. + * Upload to experimental, due to the freeze. + + -- Stefano Rivera Wed, 04 Mar 2015 14:58:17 +0200 + +python-cffi (0.8.6-1ubuntu1) vivid; urgency=medium + + * Drop the build dependency on python-virtualenv (universe), it is + an optional test. + + -- Matthias Klose Tue, 10 Mar 2015 01:20:21 +0100 + python-cffi (0.8.6-1) unstable; urgency=medium * New upstream release. @@ -72,3 +104,4 @@ * Initial package (Closes: #700084) -- Stefano Rivera Mon, 25 Feb 2013 12:12:31 +0200 + diff -Nru python-cffi-0.8.6/debian/control python-cffi-0.9.2/debian/control --- python-cffi-0.8.6/debian/control 2014-07-21 15:39:22.000000000 -0700 +++ python-cffi-0.9.2/debian/control 2015-03-19 09:08:43.000000000 -0700 @@ -13,14 +13,13 @@ python-pycparser, python-pytest, python-setuptools, - virtualenv | python-virtualenv (<< 1.11.6), python3-all-dbg, python3-all-dev (>= 3.1.2-6~), python3-py, python3-pycparser, python3-pytest, python3-setuptools -Standards-Version: 3.9.5 +Standards-Version: 3.9.6 Homepage: http://cffi.readthedocs.org/ Vcs-Svn: svn://anonscm.debian.org/python-modules/packages/python-cffi/trunk/ Vcs-Browser: http://anonscm.debian.org/viewvc/python-modules/packages/python-cffi/trunk/ diff -Nru python-cffi-0.8.6/debian/copyright python-cffi-0.9.2/debian/copyright --- python-cffi-0.8.6/debian/copyright 2014-07-21 15:09:27.000000000 -0700 +++ python-cffi-0.9.2/debian/copyright 2015-03-04 04:41:45.000000000 -0800 @@ -4,8 +4,8 @@ Source: http://pypi.python.org/pypi/cffi/ Files: * -Copyright: 2012-2014, Armin Rigo - 2012-2014, Maciej Fijalkowski +Copyright: 2012-2015, Armin Rigo + 2012-2013, Maciej Fijalkowski License: Expat Files: c/libffi_msvc/* @@ -17,7 +17,7 @@ License: Expat Files: debian/* -Copyright: 2012-2014, Stefano Rivera +Copyright: 2012-2015, Stefano Rivera License: Expat License: Expat diff -Nru python-cffi-0.8.6/debian/patches/arm64 python-cffi-0.9.2/debian/patches/arm64 --- python-cffi-0.8.6/debian/patches/arm64 2014-07-24 01:55:08.000000000 -0700 +++ python-cffi-0.9.2/debian/patches/arm64 1969-12-31 16:00:00.000000000 -0800 @@ -1,59 +0,0 @@ -Description: ARM64 support -Author: Will Newton -Author: Stefano Rivera -Bug-Upstream: https://bitbucket.org/cffi/cffi/issue/136/cffi-tests-fail-on-aarch64 -Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-cffi/+bug/1271256 -Origin: upstream, https://bitbucket.org/cffi/cffi/commits/af4e381b5e99c27c466377145a84eeece6e5c199/ -Last-Update: 2014-07-24 - ---- a/c/_cffi_backend.c -+++ b/c/_cffi_backend.c -@@ -3622,7 +3622,7 @@ - #ifdef MS_WIN32 - sflags |= SF_MSVC_BITFIELDS; - #else --# ifdef __arm__ -+# if defined(__arm__) || defined(__aarch64__) - sflags |= SF_GCC_ARM_BITFIELDS; - # else - sflags |= SF_GCC_X86_BITFIELDS; ---- a/testing/test_ffi_backend.py -+++ b/testing/test_ffi_backend.py -@@ -122,7 +122,7 @@ - self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8) - self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4) - -- @pytest.mark.skipif("platform.machine().startswith('arm')") -+ @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_anonymous_no_align(self): - L = FFI().alignof("long long") - self.check("char y; int :1;", 0, 1, 2) -@@ -135,7 +135,8 @@ - self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) - self.check("char x; long long :57; char y;", L + 8, 1, L + 9) - -- @pytest.mark.skipif("not platform.machine().startswith('arm')") -+ @pytest.mark.skipif( -+ "not platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_anonymous_align_arm(self): - L = FFI().alignof("long long") - self.check("char y; int :1;", 0, 4, 4) -@@ -148,7 +149,7 @@ - self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) - self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L) - -- @pytest.mark.skipif("platform.machine().startswith('arm')") -+ @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_zero(self): - L = FFI().alignof("long long") - self.check("char y; int :0;", 0, 1, 4) -@@ -159,7 +160,8 @@ - self.check("char x; int :0; short b:1; char y;", 5, 2, 6) - self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) - -- @pytest.mark.skipif("not platform.machine().startswith('arm')") -+ @pytest.mark.skipif( -+ "not platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_zero_arm(self): - L = FFI().alignof("long long") - self.check("char y; int :0;", 0, 4, 4) diff -Nru python-cffi-0.8.6/debian/patches/series python-cffi-0.9.2/debian/patches/series --- python-cffi-0.8.6/debian/patches/series 2014-07-23 14:12:21.000000000 -0700 +++ python-cffi-0.9.2/debian/patches/series 1969-12-31 16:00:00.000000000 -0800 @@ -1 +0,0 @@ -arm64 diff -Nru python-cffi-0.8.6/debian/watch python-cffi-0.9.2/debian/watch --- python-cffi-0.8.6/debian/watch 2013-08-07 07:23:04.000000000 -0700 +++ python-cffi-0.9.2/debian/watch 2015-03-04 04:41:45.000000000 -0800 @@ -1,3 +1,3 @@ version=3 - -https://pypi.python.org/packages/source/c/cffi/cffi-([0-9.]+).tar.gz +opts=uversionmangle=s/(rc|a|b|c)/~$1/ \ +http://pypi.debian.net/cffi/cffi-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff -Nru python-cffi-0.8.6/doc/source/conf.py python-cffi-0.9.2/doc/source/conf.py --- python-cffi-0.8.6/doc/source/conf.py 2014-07-05 10:57:59.000000000 -0700 +++ python-cffi-0.9.2/doc/source/conf.py 2015-03-13 02:02:09.000000000 -0700 @@ -45,9 +45,9 @@ # built documents. # # The short X.Y version. -version = '0.8' +version = '0.9' # The full version, including alpha/beta/rc tags. -release = '0.8.6' +release = '0.9.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -Nru python-cffi-0.8.6/doc/source/index.rst python-cffi-0.9.2/doc/source/index.rst --- python-cffi-0.8.6/doc/source/index.rst 2014-07-05 10:58:13.000000000 -0700 +++ python-cffi-0.9.2/doc/source/index.rst 2015-03-13 02:02:09.000000000 -0700 @@ -88,7 +88,7 @@ Download and Installation: -* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.6.tar.gz +* http://pypi.python.org/packages/source/c/cffi/cffi-0.9.2.tar.gz - Or grab the most current version by following the instructions below. @@ -150,8 +150,7 @@ :: brew install pkg-config libffi - export PKG_CONFIG_PATH=/usr/local/Cellar/libffi/3.0.13/lib/pkgconfig/ # May change with libffi version - pip install cffi + PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig pip install cffi Aternatively, **on OS/X 10.6** (Thanks Juraj Sukop for this) @@ -381,7 +380,10 @@ Alternatively, you can just completely remove the ``__pycache__`` directory. - +An alternative cache directory can be given as the ``tmpdir`` argument +to ``verify()``, via the environment variable ``CFFI_TMPDIR``, or by +calling ``cffi.verifier.set_tmpdir(path)`` prior to calling +``verify``. ======================================================= @@ -431,7 +433,13 @@ ``c_f``. * *New in version 0.6:* all `common Windows types`_ are defined if you run - on Windows (``DWORD``, ``LPARAM``, etc.). + on Windows (``DWORD``, ``LPARAM``, etc.). *Changed in version 0.9:* the + types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are no + longer automatically defined; see ``ffi.set_unicode()`` below. + +* *New in version 0.9:* the other standard integer types from stdint.h, + as long as they map to integers of 1, 2, 4 or 8 bytes. Larger integers + are not supported. .. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx @@ -439,6 +447,7 @@ .. "versionadded:: 0.6": bool .. "versionadded:: 0.4": FILE .. "versionadded:: 0.6": Wintypes +.. "versionadded:: 0.9": intmax_t etc. As we will see on `the verification step`_ below, the declarations can also contain "``...``" at various places; these are placeholders that will @@ -498,7 +507,7 @@ The verification step --------------------- -``ffi.verify(source, tmpdir=.., ext_package=.., modulename=.., **kwargs)``: +``ffi.verify(source, tmpdir=.., ext_package=.., modulename=.., flags=.., **kwargs)``: verifies that the current ffi signatures compile on this machine, and return a dynamic library object. The dynamic library can be used to call functions and access global @@ -637,8 +646,9 @@ .. versionadded:: 0.4 The ``tmpdir`` argument to ``verify()`` controls where the C - files are created and compiled. By default it is - ``directory_containing_the_py_file/__pycache__``, using the + files are created and compiled. Unless the ``CFFI_TMPDIR`` environment + variable is set, the default is + ``directory_containing_the_py_file/__pycache__`` using the directory name of the .py file that contains the actual call to ``ffi.verify()``. (This is a bit of a hack but is generally consistent with the location of the .pyc files for your library. @@ -664,6 +674,42 @@ check. Be sure to have other means of clearing the ``tmpdir`` whenever you change your sources. +.. versionadded:: 0.9 + You can give C++ source code in ``ffi.verify()``: + +:: + + ext = ffi.verify(r''' + extern "C" { + int somefunc(int somearg) { return real_cpp_func(somearg); } + } + ''', source_extension='.cpp', extra_compile_args=['-std=c++11']) + +.. versionadded:: 0.9 + The optional ``flags`` argument has been added, see ``man dlopen`` (ignored + on Windows). It defaults to ``ffi.RTLD_NOW``. + +.. versionadded:: 0.9 + The optional ``relative_to`` argument is useful if you need to list + local files passed to the C compiler: + +:: + + ext = ffi.verify(..., sources=['foo.c'], relative_to=__file__) + +The line above is roughly the same as:: + + ext = ffi.verify(..., sources=['/path/to/this/file/foo.c']) + +except that the default name of the produced library is built from the +CRC checkum of the argument ``sources``, as well as most other arguments +you give to ``ffi.verify()`` -- but not ``relative_to``. So if you used +the second line, it would stop finding the already-compiled library +after your project is installed, because the ``'/path/to/this/file'`` +suddenly changed. The first line does not have this problem. + +--------------------- + This function returns a "library" object that gets closed when it goes out of scope. Make sure you keep the library object around as long as needed. @@ -856,7 +902,9 @@ .. versionadded:: 0.8.2 The ``ffi.cdef()`` call takes an optional argument ``packed``: if - True, then all structs declared within this cdef are "packed". This + True, then all structs declared within this cdef are "packed". + (If you need both packed and non-packed structs, + use several cdefs in sequence.) This has a meaning similar to ``__attribute__((packed))`` in GCC. It specifies that all structure fields should have an alignment of one byte. (Note that the packed attribute has no effect on bit fields so @@ -1112,7 +1160,7 @@ Misc methods on ffi ------------------- -``ffi.include(other_ffi)``: includes the typedefs, structs, unions and +**ffi.include(other_ffi)**: includes the typedefs, structs, unions and enum types defined in another FFI instance. Usage is similar to a ``#include`` in C, where a part of the program might include types defined in another part for its own usage. Note that the include() @@ -1128,11 +1176,11 @@ .. "versionadded:: 0.5" --- inlined in the previous paragraph -``ffi.errno``: the value of ``errno`` received from the most recent C call +**ffi.errno**: the value of ``errno`` received from the most recent C call in this thread, and passed to the following C call, is available via reads and writes of the property ``ffi.errno``. -``ffi.getwinerror(code=-1)``: on Windows, in addition to ``errno`` we +**ffi.getwinerror(code=-1)**: on Windows, in addition to ``errno`` we also save and restore the ``GetLastError()`` value across function calls. This function returns this error code as a tuple ``(code, message)``, adding a readable message like Python does when raising @@ -1143,7 +1191,7 @@ .. "versionadded:: 0.8" --- inlined in the previous paragraph -``ffi.string(cdata, [maxlen])``: return a Python string (or unicode +**ffi.string(cdata, [maxlen])**: return a Python string (or unicode string) from the 'cdata'. *New in version 0.3.* .. "versionadded:: 0.3" --- inlined in the previous paragraph @@ -1167,7 +1215,7 @@ integer. -``ffi.buffer(cdata, [size])``: return a buffer object that references +**ffi.buffer(cdata, [size])**: return a buffer object that references the raw C data pointed to by the given 'cdata', of 'size' bytes. The 'cdata' must be a pointer or an array. If unspecified, the size of the buffer is either the size of what ``cdata`` points to, or the whole size @@ -1207,8 +1255,22 @@ gives inconsistent results on regular byte strings). Use ``buf[:]`` instead. +**ffi.from_buffer(python_buffer)**: return a ```` that +points to the data of the given Python object, which must support the +buffer interface. This is the opposite of ``ffi.buffer()``. It gives +a (read-write) 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 bytearray. It is meant to be used on objects +containing large quantities of raw data, like ``array.array`` or numpy +arrays. It supports both the old buffer API (in Python 2.x) and the +new memoryview API. The original object is kept alive (and, in case +of memoryview, locked) as long as the cdata object returned by +``ffi.from_buffer()`` is alive. *New in version 0.9.* -``ffi.typeof("C type" or cdata object)``: return an object of type +.. "versionadded:: 0.9" --- inlined in the previous paragraph + + +**ffi.typeof("C type" or cdata object)**: return an object of type ```` corresponding to the parsed string, or to the C type of the cdata instance. Usually you don't need to call this function or to explicitly manipulate ```` objects in your code: any place that @@ -1222,7 +1284,7 @@ ... .. versionadded:: 0.4 - ``ffi.CData, ffi.CType``: the Python type of the objects referred to + **ffi.CData, ffi.CType**: the Python type of the objects referred to as ```` and ```` in the rest of this document. Note that some cdata objects may be actually of a subclass of ``ffi.CData``, and similarly with ctype, so you should check with @@ -1232,17 +1294,24 @@ ``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``, ``abi``, ``elements`` and ``relements``. -``ffi.sizeof("C type" or cdata object)``: return the size of the +**ffi.sizeof("C type" or cdata object)**: return the size of the argument in bytes. The argument can be either a C type, or a cdata object, like in the equivalent ``sizeof`` operator in C. -``ffi.alignof("C type")``: return the alignment of the C type. +**ffi.alignof("C type")**: return the alignment of the C type. Corresponds to the ``__alignof__`` operator in GCC. -``ffi.offsetof("C struct type", "fieldname")``: return the offset within -the struct of the given field. Corresponds to ``offsetof()`` in C. +**ffi.offsetof("C struct or array type", *fields_or_indexes)**: return the +offset within the struct of the given field. Corresponds to ``offsetof()`` +in C. + +.. versionchanged:: 0.9 + You can give several field names in case of nested structures. You + can also give numeric values which correspond to array items, in case + of a pointer or array type. For example, ``ffi.offsetof("int[5]", 2)`` + is equal to the size of two integers, as is ``ffi.offsetof("int *", 2)``. -``ffi.getctype("C type" or , extra="")``: return the string +**ffi.getctype("C type" or , extra="")**: return the string representation of the given C type. If non-empty, the "extra" string is appended (or inserted at the right place in more complicated cases); it can be the name of a variable to declare, or an extra part of the type @@ -1251,7 +1320,7 @@ of the C type "pointer to the same type than x"; and ``ffi.getctype("char[80]", "a") == "char a[80]"``. -``ffi.gc(cdata, destructor)``: return a new cdata object that points to the +**ffi.gc(cdata, destructor)**: return a new cdata object that points to the same data. Later, when this new cdata object is garbage-collected, ``destructor(old_cdata_object)`` will be called. Example of usage: ``ptr = ffi.gc(lib.malloc(42), lib.free)``. Note that like objects @@ -1273,10 +1342,10 @@ .. "versionadded:: 0.3" --- inlined in the previous paragraph -``ffi.new_handle(python_object)``: return a non-NULL cdata of type +**ffi.new_handle(python_object)**: return a non-NULL cdata of type ``void *`` that contains an opaque reference to ``python_object``. You can pass it around to C functions or store it into C structures. Later, -you can use ``ffi.from_handle(p)`` to retrive the original +you can use **ffi.from_handle(p)** to retrive the original ``python_object`` from a value with the same ``void *`` pointer. *Calling ffi.from_handle(p) is invalid and will likely crash if the cdata object returned by new_handle() is not kept alive!* @@ -1293,21 +1362,52 @@ .. "versionadded:: 0.7" --- inlined in the previous paragraph -``ffi.addressof(cdata, field=None)``: from a cdata whose type is -``struct foo_s``, return its "address", as a cdata whose type is -``struct foo_s *``. Also works on unions, but not on any other type. -(It would be difficult because only structs and unions are internally -stored as an indirect pointer to the data. If you need a C int whose -address can be taken, use ``ffi.new("int[1]")`` in the first place; -similarly, if it's a C pointer, use ``ffi.new("foo_t *[1]")``.) -If ``field`` is given, -returns the address of that field in the structure. The returned -pointer is only valid as long as the original ``cdata`` object is; be -sure to keep it alive if it was obtained directly from ``ffi.new()``. -*New in version 0.4.* +**ffi.addressof(cdata, *fields_or_indexes)**: equivalent to the C +expression ``&cdata`` or ``&cdata.field`` or ``&cdata->field`` or +``&cdata[index]`` (or any combination of fields and indexes). Works +with the same ctypes where one of the above expressions would work in +C, with one exception: if no ``fields_or_indexes`` is specified, it +cannot be used to take the address of a primitive or pointer (it would +be difficult to implement because only structs and unions and arrays +are internally stored as an indirect pointer to the data. If you need +a C int whose address can be taken, use ``ffi.new("int[1]")`` in the +first place; similarly, for a pointer, use ``ffi.new("foo_t *[1]")``.) + +The returned pointer is only valid as long as the original ``cdata`` +object is; be sure to keep it alive if it was obtained directly from +``ffi.new()``. *New in version 0.4.* + +.. versionchanged:: 0.9 + You can give several field names in case of nested structures, and + you can give numeric values for array items. Note that + ``&cdata[index]`` can also be expressed as simply ``cdata + index``, + both in C and in CFFI. .. "versionadded:: 0.4" --- inlined in the previous paragraph +**ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is +True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and +declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE +PTCHAR`` to be (pointers to) ``wchar_t``. If ``enabled_flag`` is +False, declare these types to be (pointers to) plain 8-bit characters. +(These types are not predeclared at all if you don't call +``set_unicode()``.) *New in version 0.9.* + +The reason behind this method is that a lot of standard functions have +two versions, like ``MessageBoxA()`` and ``MessageBoxW()``. The +official interface is ``MessageBox()`` with arguments like +``LPTCSTR``. Depending on whether ``UNICODE`` is defined or not, the +standard header renames the generic function name to one of the two +specialized versions, and declares the correct (unicode or not) types. + +Usually, the right thing to do is to call this method with True. Be +aware (particularly on Python 2) that you then need to pass unicode +strings as arguments, not byte strings. (Before cffi version 0.9, +``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, +inconsistently, not defined by default.) + +.. "versionadded:: 0.9" --- inlined in the previous paragraph + Unimplemented features ---------------------- diff -Nru python-cffi-0.8.6/PKG-INFO python-cffi-0.9.2/PKG-INFO --- python-cffi-0.8.6/PKG-INFO 2014-07-05 11:06:59.000000000 -0700 +++ python-cffi-0.9.2/PKG-INFO 2015-03-13 02:03:30.000000000 -0700 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 0.8.6 +Version: 0.9.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff -Nru python-cffi-0.8.6/setup.py python-cffi-0.9.2/setup.py --- python-cffi-0.8.6/setup.py 2014-07-05 10:49:51.000000000 -0700 +++ python-cffi-0.9.2/setup.py 2015-03-13 02:02:09.000000000 -0700 @@ -41,6 +41,15 @@ # resultlist[:] = res +def no_working_compiler_found(): + sys.stderr.write(""" + No working compiler found, or bogus compiler options + passed to the compiler from Python's distutils module. + See the error messages above. + (If they are about -mno-fused-madd and you are on OS/X 10.8, + see http://stackoverflow.com/questions/22313407/ .)\n""") + sys.exit(1) + def ask_supports_thread(): from distutils.core import Distribution from distutils.sysconfig import get_config_vars @@ -50,16 +59,31 @@ if ok: define_macros.append(('USE__THREAD', None)) else: + ok1 = config.try_compile('int some_regular_variable_42;') + if not ok1: + no_working_compiler_found() 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 use_pkg_config(): + if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'): + use_homebrew_for_libffi() + _ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True) _ask_pkg_config(extra_compile_args, '--cflags-only-other') _ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True) _ask_pkg_config(extra_link_args, '--libs-only-other') _ask_pkg_config(libraries, '--libs-only-l', '-l') +def use_homebrew_for_libffi(): + # We can build by setting: + # PKG_CONFIG_PATH = $(brew --prefix libffi)/lib/pkgconfig + with os.popen('brew --prefix libffi') as brew_prefix_cmd: + prefix = brew_prefix_cmd.read().strip() + pkgconfig = os.path.join(prefix, 'lib', 'pkgconfig') + os.environ['PKG_CONFIG_PATH'] = ( + os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig) + if sys.platform == 'win32': COMPILE_LIBFFI = 'c/libffi_msvc' # from the CPython distribution @@ -115,7 +139,7 @@ `Mailing list `_ """, - version='0.8.6', + version='0.9.2', packages=['cffi'], zip_safe=False, diff -Nru python-cffi-0.8.6/testing/backend_tests.py python-cffi-0.9.2/testing/backend_tests.py --- python-cffi-0.8.6/testing/backend_tests.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/testing/backend_tests.py 2015-03-13 02:02:09.000000000 -0700 @@ -1,7 +1,7 @@ import py import platform import sys, ctypes -from cffi import FFI, CDefError +from cffi import FFI, CDefError, FFIError from testing.support import * SIZE_OF_INT = ctypes.sizeof(ctypes.c_int) @@ -916,6 +916,16 @@ assert int(invalid_value) == 2 assert ffi.string(invalid_value) == "2" + def test_enum_char_hex_oct(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};") + assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A" + assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B" + assert ffi.string(ffi.cast("enum foo", 16)) == "C" + assert ffi.string(ffi.cast("enum foo", 8)) == "D" + assert ffi.string(ffi.cast("enum foo", -16)) == "E" + assert ffi.string(ffi.cast("enum foo", -8)) == "F" + def test_array_of_struct(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { int a, b; };") @@ -949,6 +959,25 @@ assert ffi.offsetof("struct foo", "b") == 4 assert ffi.offsetof("struct foo", "c") == 8 + def test_offsetof_nested(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef("struct foo { int a, b, c; };" + "struct bar { struct foo d, e; };") + assert ffi.offsetof("struct bar", "e") == 12 + py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a") + assert ffi.offsetof("struct bar", "e", "a") == 12 + assert ffi.offsetof("struct bar", "e", "b") == 16 + assert ffi.offsetof("struct bar", "e", "c") == 20 + + def test_offsetof_array(self): + ffi = FFI(backend=self.Backend()) + assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") + assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") + ffi.cdef("struct bar { int a, b; int c[99]; };") + assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int") + assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int") + assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int") + def test_alignof(self): ffi = FFI(backend=self.Backend()) ffi.cdef("struct foo { char a; short b; char c; };") @@ -1481,8 +1510,10 @@ p = ffi.new("struct foo_s *") a = ffi.addressof(p[0]) assert repr(a).startswith(" -#include -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/snippets/distutils_package_1/build/lib.linux-x86_64-2.7/snip_basic_verify1/__init__.py python-cffi-0.9.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-2.7/snip_basic_verify1/__init__.py --- python-cffi-0.8.6/testing/snippets/distutils_package_1/build/lib.linux-x86_64-2.7/snip_basic_verify1/__init__.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/distutils_package_1/build/lib.linux-x86_64-2.7/snip_basic_verify1/__init__.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include -#include -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/snippets/distutils_package_2/build/lib.linux-x86_64-2.7/snip_basic_verify2/__init__.py python-cffi-0.9.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-2.7/snip_basic_verify2/__init__.py --- python-cffi-0.8.6/testing/snippets/distutils_package_2/build/lib.linux-x86_64-2.7/snip_basic_verify2/__init__.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/distutils_package_2/build/lib.linux-x86_64-2.7/snip_basic_verify2/__init__.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,18 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include -#include -""", libraries=[], # or a list of libraries to link with - ext_package='snip_basic_verify2', - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/snippets/infrastructure/build/lib.linux-x86_64-2.7/snip_infrastructure/__init__.py python-cffi-0.9.2/testing/snippets/infrastructure/build/lib.linux-x86_64-2.7/snip_infrastructure/__init__.py --- python-cffi-0.8.6/testing/snippets/infrastructure/build/lib.linux-x86_64-2.7/snip_infrastructure/__init__.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/infrastructure/build/lib.linux-x86_64-2.7/snip_infrastructure/__init__.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,3 +0,0 @@ - -def func(): - return 42 diff -Nru python-cffi-0.8.6/testing/snippets/setuptools_module/build/lib.linux-x86_64-2.7/snip_setuptools_verify.py python-cffi-0.9.2/testing/snippets/setuptools_module/build/lib.linux-x86_64-2.7/snip_setuptools_verify.py --- python-cffi-0.8.6/testing/snippets/setuptools_module/build/lib.linux-x86_64-2.7/snip_setuptools_verify.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/setuptools_module/build/lib.linux-x86_64-2.7/snip_setuptools_verify.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include -#include -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-2.7/snip_setuptools_verify1/__init__.py python-cffi-0.9.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-2.7/snip_setuptools_verify1/__init__.py --- python-cffi-0.8.6/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-2.7/snip_setuptools_verify1/__init__.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/setuptools_package_1/build/lib.linux-x86_64-2.7/snip_setuptools_verify1/__init__.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include -#include -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-2.7/snip_setuptools_verify2/__init__.py python-cffi-0.9.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-2.7/snip_setuptools_verify2/__init__.py --- python-cffi-0.8.6/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-2.7/snip_setuptools_verify2/__init__.py 2014-04-30 02:16:17.000000000 -0700 +++ python-cffi-0.9.2/testing/snippets/setuptools_package_2/build/lib.linux-x86_64-2.7/snip_setuptools_verify2/__init__.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,18 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include -#include -""", libraries=[], # or a list of libraries to link with - ext_package='snip_setuptools_verify2', - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff -Nru python-cffi-0.8.6/testing/test_cdata.py python-cffi-0.9.2/testing/test_cdata.py --- python-cffi-0.8.6/testing/test_cdata.py 2014-06-29 02:06:01.000000000 -0700 +++ python-cffi-0.9.2/testing/test_cdata.py 2015-03-13 02:02:09.000000000 -0700 @@ -19,6 +19,8 @@ return FakeType("void") def new_pointer_type(self, x): return FakeType('ptr-to-%r' % (x,)) + def new_array_type(self, x, y): + return FakeType('array-from-%r-len-%r' % (x, y)) def cast(self, x, y): return 'casted!' def _get_types(self): diff -Nru python-cffi-0.8.6/testing/test_ffi_backend.py python-cffi-0.9.2/testing/test_ffi_backend.py --- python-cffi-0.8.6/testing/test_ffi_backend.py 2014-06-29 02:06:01.000000000 -0700 +++ python-cffi-0.9.2/testing/test_ffi_backend.py 2015-03-13 02:02:09.000000000 -0700 @@ -19,8 +19,8 @@ ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };") e = py.test.raises(NotImplementedError, ffi.callback, "struct foo_s foo(void)", lambda: 42) - assert str(e.value) == (": " - "cannot pass as argument or return value a struct with bit fields") + assert str(e.value) == ("struct foo_s(*)(): " + "callback with unsupported argument or return type or with '...'") def test_inspecttype(self): ffi = FFI(backend=self.Backend()) @@ -122,7 +122,7 @@ self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8) self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4) - @pytest.mark.skipif("platform.machine().startswith('arm')") + @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_anonymous_no_align(self): L = FFI().alignof("long long") self.check("char y; int :1;", 0, 1, 2) @@ -135,7 +135,8 @@ self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) self.check("char x; long long :57; char y;", L + 8, 1, L + 9) - @pytest.mark.skipif("not platform.machine().startswith('arm')") + @pytest.mark.skipif( + "not platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_anonymous_align_arm(self): L = FFI().alignof("long long") self.check("char y; int :1;", 0, 4, 4) @@ -148,7 +149,7 @@ self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L) - @pytest.mark.skipif("platform.machine().startswith('arm')") + @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_zero(self): L = FFI().alignof("long long") self.check("char y; int :0;", 0, 1, 4) @@ -159,7 +160,8 @@ self.check("char x; int :0; short b:1; char y;", 5, 2, 6) self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) - @pytest.mark.skipif("not platform.machine().startswith('arm')") + @pytest.mark.skipif( + "not platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_zero_arm(self): L = FFI().alignof("long long") self.check("char y; int :0;", 0, 4, 4) @@ -211,3 +213,12 @@ code, message = ffi.getwinerror(-1) assert code == 2 assert message == "The system cannot find the file specified" + + def test_from_buffer(self): + import array + ffi = FFI() + a = array.array('H', [10000, 20000, 30000]) + c = ffi.from_buffer(a) + assert ffi.typeof(c) is ffi.typeof("char[]") + ffi.cast("unsigned short *", c)[1] += 500 + assert list(a) == [10000, 20500, 30000] diff -Nru python-cffi-0.8.6/testing/test_ownlib.py python-cffi-0.9.2/testing/test_ownlib.py --- python-cffi-0.8.6/testing/test_ownlib.py 2014-06-29 02:06:02.000000000 -0700 +++ python-cffi-0.9.2/testing/test_ownlib.py 2015-03-13 02:02:09.000000000 -0700 @@ -7,34 +7,137 @@ SOURCE = """\ #include -int test_getting_errno(void) { +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT int test_getting_errno(void) { errno = 123; return -1; } -int test_setting_errno(void) { +EXPORT int test_setting_errno(void) { return errno; +}; + +typedef struct { + long x; + long y; +} POINT; + +typedef struct { + long left; + long top; + long right; + long bottom; +} RECT; + + +EXPORT int PointInRect(RECT *prc, POINT pt) +{ + if (pt.x < prc->left) + return 0; + if (pt.x > prc->right) + return 0; + if (pt.y < prc->top) + return 0; + if (pt.y > prc->bottom) + return 0; + return 1; +}; + +EXPORT long left = 10; +EXPORT long top = 20; +EXPORT long right = 30; +EXPORT long bottom = 40; + +EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, + RECT *er, POINT fp, RECT gr) +{ + /*Check input */ + if (ar.left + br->left + dr.left + er->left + gr.left != left * 5) + { + ar.left = 100; + return ar; + } + if (ar.right + br->right + dr.right + er->right + gr.right != right * 5) + { + ar.right = 100; + return ar; + } + if (cp.x != fp.x) + { + ar.left = -100; + } + if (cp.y != fp.y) + { + ar.left = -200; + } + switch(i) + { + case 0: + return ar; + break; + case 1: + return dr; + break; + case 2: + return gr; + break; + + } + return ar; } -int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; +EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; """ class TestOwnLib(object): Backend = CTypesBackend def setup_class(cls): - if sys.platform == 'win32': - return + cls.module = None from testing.udir import udir udir.join('testownlib.c').write(SOURCE) - subprocess.check_call( - 'gcc testownlib.c -shared -fPIC -o testownlib.so', - cwd=str(udir), shell=True) - cls.module = str(udir.join('testownlib.so')) + if sys.platform == 'win32': + import os + # did we already build it? + if os.path.exists(str(udir.join('testownlib.dll'))): + cls.module = str(udir.join('testownlib.dll')) + return + # try (not too hard) to find the version used to compile this python + # no mingw + from distutils.msvc9compiler import get_build_version + version = get_build_version() + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + if toolsdir is None: + return + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + # 64? + arch = 'x86' + if sys.maxsize > 2**32: + arch = 'amd64' + if os.path.isfile(vcvarsall): + cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ + ' /LD /Fetestownlib.dll' + subprocess.check_call(cmd, cwd = str(udir), shell=True) + cls.module = str(udir.join('testownlib.dll')) + else: + subprocess.check_call( + 'gcc testownlib.c -shared -fPIC -o testownlib.so', + cwd=str(udir), shell=True) + cls.module = str(udir.join('testownlib.so')) def test_getting_errno(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if sys.platform == 'win32': + py.test.skip("fails, errno at multiple addresses") ffi = FFI(backend=self.Backend()) ffi.cdef(""" int test_getting_errno(void); @@ -45,8 +148,10 @@ assert ffi.errno == 123 def test_setting_errno(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if sys.platform == 'win32': + py.test.skip("fails, errno at multiple addresses") if self.Backend is CTypesBackend and '__pypy__' in sys.modules: py.test.skip("XXX errno issue with ctypes on pypy?") ffi = FFI(backend=self.Backend()) @@ -60,7 +165,7 @@ assert ffi.errno == 42 def test_my_array_7(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" @@ -80,7 +185,7 @@ assert ownlib.my_array[i] == i def test_my_array_no_length(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") if self.Backend is CTypesBackend: py.test.skip("not supported by the ctypes backend") @@ -100,7 +205,7 @@ assert ownlib.my_array[i] == i def test_keepalive_lib(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" @@ -118,7 +223,7 @@ assert res == -1 def test_keepalive_ffi(self): - if sys.platform == 'win32': + if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" @@ -134,4 +239,46 @@ assert ownlib_r() is not None # kept alive by ffi res = func() assert res == -1 - assert ffi.errno == 123 + if sys.platform != 'win32': # else, errno at multiple addresses + assert ffi.errno == 123 + + def test_struct_by_value(self): + if self.module is None: + py.test.skip("fix the auto-generation of the tiny test lib") + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + typedef struct { + long x; + long y; + } POINT; + + typedef struct { + long left; + long top; + long right; + long bottom; + } RECT; + + long left, top, right, bottom; + + RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, + RECT *er, POINT fp, RECT gr); + """) + ownlib = ffi.dlopen(self.module) + + rect = ffi.new('RECT[1]') + pt = ffi.new('POINT[1]') + pt[0].x = 15 + pt[0].y = 25 + rect[0].left = ownlib.left + rect[0].right = ownlib.right + rect[0].top = ownlib.top + rect[0].bottom = ownlib.bottom + + for i in range(4): + ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0], + rect, pt[0], rect[0]) + assert ret.left == ownlib.left + assert ret.right == ownlib.right + assert ret.top == ownlib.top + assert ret.bottom == ownlib.bottom diff -Nru python-cffi-0.8.6/testing/test_parsing.py python-cffi-0.9.2/testing/test_parsing.py --- python-cffi-0.8.6/testing/test_parsing.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/testing/test_parsing.py 2015-03-13 02:02:09.000000000 -0700 @@ -163,8 +163,12 @@ ffi = FFI(backend=FakeBackend()) e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"') assert str(e.value) == ( - 'only supports the syntax "#define FOO ..." (literally)' - ' or "#define FOO 0x1FF" for now') + 'only supports one of the following syntax:\n' + ' #define FOO ... (literally dot-dot-dot)\n' + ' #define FOO NUMBER (with NUMBER an integer' + ' constant, decimal/hex/octal)\n' + 'got:\n' + ' #define FOO "blah"') def test_unnamed_struct(): ffi = FFI(backend=FakeBackend()) @@ -247,7 +251,8 @@ ct = win_common_types(maxsize) clear_all(ct) for key in sorted(ct): - resolve_common_type(key) + if ct[key] != 'set-unicode-needed': + resolve_common_type(key) # assert did not crash # now try to use e.g. WPARAM (-> UINT_PTR -> unsigned 32/64-bit) for maxsize in [2**32-1, 2**64-1]: @@ -288,3 +293,14 @@ decl = ast.children()[0][1] node = decl.type assert p._is_constant_globalvar(node) == expected_output + +def test_enum(): + ffi = FFI() + ffi.cdef(""" + enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1}; + """) + C = ffi.dlopen(None) + assert C.POS == 1 + assert C.TWO == 2 + assert C.NIL == 0 + assert C.NEG == -1 diff -Nru python-cffi-0.8.6/testing/test_verify.py python-cffi-0.9.2/testing/test_verify.py --- python-cffi-0.8.6/testing/test_verify.py 2014-07-05 07:47:47.000000000 -0700 +++ python-cffi-0.9.2/testing/test_verify.py 2015-03-13 02:02:09.000000000 -0700 @@ -1,6 +1,6 @@ import py, re import sys, os, math, weakref -from cffi import FFI, VerificationError, VerificationMissing, model +from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from testing.support import * @@ -14,12 +14,13 @@ else: if (sys.platform == 'darwin' and [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): + # assume a standard clang or gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] # special things for clang - extra_compile_args = [ - '-Werror', '-Qunused-arguments', '-Wno-error=shorten-64-to-32'] + extra_compile_args.append('-Qunused-arguments') else: # assume a standard gcc - extra_compile_args = ['-Werror'] + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] class FFI(FFI): def verify(self, *args, **kwds): @@ -89,11 +90,48 @@ lib = ffi.verify('#include ', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) +def _Wconversion(cdef, source, **kargs): + if sys.platform == 'win32': + py.test.skip("needs GCC or Clang") + ffi = FFI() + ffi.cdef(cdef) + py.test.raises(VerificationError, ffi.verify, source, **kargs) + extra_compile_args_orig = extra_compile_args[:] + extra_compile_args.remove('-Wconversion') + try: + lib = ffi.verify(source, **kargs) + finally: + extra_compile_args[:] = extra_compile_args_orig + return lib + +def test_Wconversion_unsigned(): + _Wconversion("unsigned foo(void);", + "int foo(void) { return -1;}") + +def test_Wconversion_integer(): + _Wconversion("short foo(void);", + "long long foo(void) { return 1<", libraries=lib_m) + res = lib.sin(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_Wconversion_float2int(): + _Wconversion("int sinf(float);", + "#include ", libraries=lib_m) + +def test_Wconversion_double2int(): + _Wconversion("int sin(double);", + "#include ", libraries=lib_m) + def test_rounding_1(): ffi = FFI() - ffi.cdef("float sin(double x);") + ffi.cdef("double sinf(float x);") lib = ffi.verify('#include ', libraries=lib_m) - res = lib.sin(1.23) + res = lib.sinf(1.23) assert res != math.sin(1.23) # not exact, because of double->float assert abs(res - math.sin(1.23)) < 1E-5 @@ -112,14 +150,21 @@ assert lib.strlen(b"hi there!") == 9 def test_strlen_approximate(): - ffi = FFI() - ffi.cdef("int strlen(char *s);") - lib = ffi.verify("#include ") + lib = _Wconversion("int strlen(char *s);", + "#include ") assert lib.strlen(b"hi there!") == 9 +def test_return_approximate(): + for typename in ['short', 'int', 'long', 'long long']: + ffi = FFI() + ffi.cdef("%s foo(signed char x);" % typename) + lib = ffi.verify("signed char foo(signed char x) { return x;}") + assert lib.foo(-128) == -128 + assert lib.foo(+127) == +127 + def test_strlen_array_of_char(): ffi = FFI() - ffi.cdef("int strlen(char[]);") + ffi.cdef("size_t strlen(char[]);") lib = ffi.verify("#include ") assert lib.strlen(b"hello") == 5 @@ -208,8 +253,8 @@ ffi = FFI() ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) for tp in typenames])) - lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return x+1; }" % - (tp, tp.replace(' ', '_'), tp) + lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" % + (tp, tp.replace(' ', '_'), tp, tp) for tp in typenames])) for typename in typenames: foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) @@ -315,7 +360,7 @@ def test_char_type(): ffi = FFI() ffi.cdef("char foo(char);") - lib = ffi.verify("char foo(char x) { return x+1; }") + lib = ffi.verify("char foo(char x) { return ++x; }") assert lib.foo(b"A") == b"B" py.test.raises(TypeError, lib.foo, b"bar") py.test.raises(TypeError, lib.foo, "bar") @@ -385,7 +430,7 @@ ffi = FFI() ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") lib = ffi.verify("typedef struct foo_s foo_t;\n" - "int bar(foo_t *f) { return 42; }\n") + "int bar(foo_t *f) { (void)f; return 42; }\n") assert lib.bar(ffi.NULL) == 42 def test_ffi_full_struct(): @@ -896,7 +941,7 @@ static int foo(token_t *tk) { if (!tk) return -42; - *tk += 1.601; + *tk += 1.601f; return (int)*tk; } #define TOKEN_SIZE sizeof(token_t) @@ -991,7 +1036,7 @@ long a; }; int foo(struct foo_s s) { - return s.a - (int)s.b; + return (int)s.a - (int)s.b; } """) s = ffi.new("struct foo_s *", [100, 1]) @@ -1008,7 +1053,7 @@ long a; }; int foo1(struct foo_s s) { - return s.a - (int)s.b; + return (int)s.a - (int)s.b; } int (*foo)(struct foo_s s) = &foo1; """) @@ -1067,7 +1112,7 @@ def test_array_as_argument(): ffi = FFI() ffi.cdef(""" - int strlen(char string[]); + size_t strlen(char string[]); """) ffi.verify("#include ") @@ -1079,7 +1124,7 @@ """) lib = ffi.verify(""" enum foo_e { AA, CC, BB }; - int foo_func(enum foo_e e) { return e; } + int foo_func(enum foo_e e) { return (int)e; } """) assert lib.foo_func(lib.BB) == 2 py.test.raises(TypeError, lib.foo_func, "BB") @@ -1092,7 +1137,7 @@ """) lib = ffi.verify(""" enum foo_e { AA, CC, BB }; - enum foo_e foo_func(int x) { return x; } + enum foo_e foo_func(int x) { return (enum foo_e)x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 @@ -1127,6 +1172,19 @@ assert lib.AA == 0 assert lib.BB == 2 +def test_typedef_enum_as_argument(): + ffi = FFI() + ffi.cdef(""" + typedef enum { AA, BB, ... } foo_t; + int foo_func(foo_t); + """) + lib = ffi.verify(""" + typedef enum { AA, CC, BB } foo_t; + int foo_func(foo_t e) { return (int)e; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + py.test.raises(TypeError, lib.foo_func, "BB") + def test_typedef_enum_as_function_result(): ffi = FFI() ffi.cdef(""" @@ -1135,7 +1193,7 @@ """) lib = ffi.verify(""" typedef enum { AA, CC, BB } foo_t; - foo_t foo_func(int x) { return x; } + foo_t foo_func(int x) { return (foo_t)x; } """) assert lib.foo_func(lib.BB) == lib.BB == 2 @@ -1162,6 +1220,8 @@ import platform if platform.machine().startswith('sparc'): py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') + elif platform.machine() == 'mips64' and sys.maxsize > 2**32: + py.test.skip('Segfaults on mips64el') # XXX bad abuse of "struct { ...; }". It only works a bit by chance # anyway. XXX think about something better :-( ffi = FFI() @@ -1291,7 +1351,7 @@ """) def test_tmpdir(): - import tempfile, os, shutil + import tempfile, os from testing.udir import udir tmpdir = tempfile.mkdtemp(dir=str(udir)) ffi = FFI() @@ -1300,6 +1360,20 @@ assert os.listdir(tmpdir) assert lib.foo(100) == 142 +def test_relative_to(): + import tempfile, os + from testing.udir import udir + tmpdir = tempfile.mkdtemp(dir=str(udir)) + ffi = FFI() + ffi.cdef("int foo(int);") + f = open(os.path.join(tmpdir, 'foo.h'), 'w') + f.write("int foo(int a) { return a + 42; }\n") + f.close() + lib = ffi.verify('#include "foo.h"', + include_dirs=['.'], + relative_to=os.path.join(tmpdir, 'x')) + assert lib.foo(100) == 142 + def test_bug1(): ffi = FFI() ffi.cdef(""" @@ -1676,7 +1750,7 @@ static int c_callback(int how_many, ...) { va_list ap; /* collect the "..." arguments into the values[] array */ - int i, *values = alloca(how_many * sizeof(int)); + int i, *values = alloca((size_t)how_many * sizeof(int)); va_start(ap, how_many); for (i=0; i + """, libraries=['Kernel32']) + outbuf = ffi.new("TCHAR[]", 200) + n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) + assert 0 < n < 500 + for i in range(n): + #print repr(outbuf[i]) + assert ord(outbuf[i]) != 0 + assert ord(outbuf[n]) == 0 + assert ord(outbuf[0]) < 128 # should be a letter, or '\' + +def test_use_local_dir(): + ffi = FFI() + lib = ffi.verify("", modulename="test_use_local_dir") + this_dir = os.path.dirname(__file__) + pycache_files = os.listdir(os.path.join(this_dir, '__pycache__')) + assert any('test_use_local_dir' in s for s in pycache_files) + +def test_define_known_value(): + ffi = FFI() + ffi.cdef("#define FOO 0x123") + lib = ffi.verify("#define FOO 0x123") + assert lib.FOO == 0x123 + +def test_define_wrong_value(): + ffi = FFI() + ffi.cdef("#define FOO 123") + e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") + assert str(e.value).endswith("FOO has the real value 124, not 123") diff -Nru python-cffi-0.8.6/testing/test_version.py python-cffi-0.9.2/testing/test_version.py --- python-cffi-0.8.6/testing/test_version.py 2014-07-05 10:16:25.000000000 -0700 +++ python-cffi-0.9.2/testing/test_version.py 2015-03-13 02:02:09.000000000 -0700 @@ -16,6 +16,7 @@ def test_version(): v = cffi.__version__ version_info = '.'.join(str(i) for i in cffi.__version_info__) + version_info = version_info.replace('.plus', '+') assert v == version_info #v = BACKEND_VERSIONS.get(v, v) assert v == _cffi_backend.__version__ @@ -31,7 +32,7 @@ def test_doc_version_file(): parent = os.path.dirname(os.path.dirname(__file__)) - v = cffi.__version__ + v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'index.rst') content = open(p).read() assert ("cffi/cffi-%s.tar.gz" % v) in content @@ -41,7 +42,7 @@ p = os.path.join(parent, 'setup.py') content = open(p).read() # - v = cffi.__version__ + v = cffi.__version__.replace('+', '') assert ("version='%s'" % v) in content def test_c_version(): diff -Nru python-cffi-0.8.6/testing/test_zdistutils.py python-cffi-0.9.2/testing/test_zdistutils.py --- python-cffi-0.8.6/testing/test_zdistutils.py 2014-06-29 02:06:03.000000000 -0700 +++ python-cffi-0.9.2/testing/test_zdistutils.py 2015-03-13 02:02:09.000000000 -0700 @@ -15,6 +15,10 @@ if distutils.ccompiler.get_default_compiler() == 'msvc': self.lib_m = 'msvcrt' + def teardown_class(self): + if udir.isdir(): + udir.remove(ignore_errors=True) + def test_locate_engine_class(self): cls = _locate_engine_class(FFI(), self.generic) if self.generic: @@ -160,7 +164,8 @@ assert lib.sin(12.3) == math.sin(12.3) v = ffi.verifier ext = v.get_extension() - assert 'distutils.extension.Extension' in str(ext.__class__) + assert 'distutils.extension.Extension' in str(ext.__class__) or \ + 'setuptools.extension.Extension' in str(ext.__class__) assert ext.sources == [maybe_relative_path(v.sourcefilename)] assert ext.name == v.get_module_name() assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')] @@ -189,7 +194,8 @@ assert lib.test1eoes(7.0) == 42.0 v = ffi.verifier ext = v.get_extension() - assert 'distutils.extension.Extension' in str(ext.__class__) + assert 'distutils.extension.Extension' in str(ext.__class__) or \ + 'setuptools.extension.Extension' in str(ext.__class__) assert ext.sources == [maybe_relative_path(v.sourcefilename), extra_source] assert ext.name == v.get_module_name() diff -Nru python-cffi-0.8.6/testing/test_zintegration.py python-cffi-0.9.2/testing/test_zintegration.py --- python-cffi-0.8.6/testing/test_zintegration.py 2014-06-29 02:06:04.000000000 -0700 +++ python-cffi-0.9.2/testing/test_zintegration.py 2015-03-13 02:02:09.000000000 -0700 @@ -3,6 +3,9 @@ import subprocess from testing.udir import udir +if sys.platform == 'win32': + py.test.skip('snippets do not run on win32') + def create_venv(name): tmpdir = udir.join(name) try: @@ -12,6 +15,23 @@ except OSError as e: py.test.skip("Cannot execute virtualenv: %s" % (e,)) + try: + deepcopy = os.symlink + except: + import shutil, errno + def deepcopy(src, dst): + try: + shutil.copytree(src, dst) + except OSError as e: + if e.errno in (errno.ENOTDIR, errno.EINVAL): + shutil.copy(src, dst) + else: + print('got errno') + print(e.errno) + print('not') + print(errno.ENOTDIR) + raise + site_packages = None for dirpath, dirnames, filenames in os.walk(str(tmpdir)): if os.path.basename(dirpath) == 'site-packages': @@ -31,7 +51,7 @@ modules += ('ply',) # needed for older versions of pycparser for module in modules: target = imp.find_module(module)[1] - os.symlink(target, os.path.join(site_packages, + deepcopy(target, os.path.join(site_packages, os.path.basename(target))) return tmpdir @@ -50,7 +70,11 @@ python_f.write(py.code.Source(python_snippet)) try: os.chdir(str(SNIPPET_DIR.join(dirname))) - vp = str(venv_dir.join('bin/python')) + if os.name == 'nt': + bindir = 'Scripts' + else: + bindir = 'bin' + vp = str(venv_dir.join(bindir).join('python')) subprocess.check_call((vp, 'setup.py', 'clean')) subprocess.check_call((vp, 'setup.py', 'install')) subprocess.check_call((vp, str(python_f))) @@ -72,50 +96,55 @@ assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py'))) assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py'))) -def test_infrastructure(): - run_setup_and_program('infrastructure', ''' - import snip_infrastructure - assert snip_infrastructure.func() == 42 - ''') - -def test_distutils_module(): - run_setup_and_program("distutils_module", ''' - import snip_basic_verify - p = snip_basic_verify.C.getpwuid(0) - assert snip_basic_verify.ffi.string(p.pw_name) == b"root" - ''') - -def test_distutils_package_1(): - run_setup_and_program("distutils_package_1", ''' - import snip_basic_verify1 - p = snip_basic_verify1.C.getpwuid(0) - assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" - ''') - -def test_distutils_package_2(): - run_setup_and_program("distutils_package_2", ''' - import snip_basic_verify2 - p = snip_basic_verify2.C.getpwuid(0) - assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" - ''') - -def test_setuptools_module(): - run_setup_and_program("setuptools_module", ''' - import snip_setuptools_verify - p = snip_setuptools_verify.C.getpwuid(0) - assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" - ''') - -def test_setuptools_package_1(): - run_setup_and_program("setuptools_package_1", ''' - import snip_setuptools_verify1 - p = snip_setuptools_verify1.C.getpwuid(0) - assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" - ''') - -def test_setuptools_package_2(): - run_setup_and_program("setuptools_package_2", ''' - import snip_setuptools_verify2 - p = snip_setuptools_verify2.C.getpwuid(0) - assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" - ''') +class TestZIntegration(object): + def teardown_class(self): + if udir.isdir(): + udir.remove(ignore_errors=True) + + def test_infrastructure(self): + run_setup_and_program('infrastructure', ''' + import snip_infrastructure + assert snip_infrastructure.func() == 42 + ''') + + def test_distutils_module(self): + run_setup_and_program("distutils_module", ''' + import snip_basic_verify + p = snip_basic_verify.C.getpwuid(0) + assert snip_basic_verify.ffi.string(p.pw_name) == b"root" + ''') + + def test_distutils_package_1(self): + run_setup_and_program("distutils_package_1", ''' + import snip_basic_verify1 + p = snip_basic_verify1.C.getpwuid(0) + assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" + ''') + + def test_distutils_package_2(self): + run_setup_and_program("distutils_package_2", ''' + import snip_basic_verify2 + p = snip_basic_verify2.C.getpwuid(0) + assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" + ''') + + def test_setuptools_module(self): + run_setup_and_program("setuptools_module", ''' + import snip_setuptools_verify + p = snip_setuptools_verify.C.getpwuid(0) + assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" + ''') + + def test_setuptools_package_1(self): + run_setup_and_program("setuptools_package_1", ''' + import snip_setuptools_verify1 + p = snip_setuptools_verify1.C.getpwuid(0) + assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" + ''') + + def test_setuptools_package_2(self): + run_setup_and_program("setuptools_package_2", ''' + import snip_setuptools_verify2 + p = snip_setuptools_verify2.C.getpwuid(0) + assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" + ''')