[pypy-commit] cffi verifier2: hg merge default
arigo
noreply at buildbot.pypy.org
Sat Aug 4 16:36:22 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: verifier2
Changeset: r769:f18c1ff6dc86
Date: 2012-08-04 16:23 +0200
http://bitbucket.org/cffi/cffi/changeset/f18c1ff6dc86/
Log: hg merge default
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -28,7 +28,7 @@
#define CT_PRIMITIVE_SIGNED 1 /* signed integer */
#define CT_PRIMITIVE_UNSIGNED 2 /* unsigned integer */
#define CT_PRIMITIVE_CHAR 4 /* char, wchar_t */
-#define CT_PRIMITIVE_FLOAT 8 /* float, double */
+#define CT_PRIMITIVE_FLOAT 8 /* float, double, long double */
#define CT_POINTER 16 /* pointer, excluding ptr-to-func */
#define CT_ARRAY 32 /* array */
#define CT_STRUCT 64 /* struct */
@@ -43,6 +43,7 @@
#define CT_IS_ENUM 8192
#define CT_IS_PTR_TO_OWNED 16384
#define CT_CUSTOM_FIELD_POS 32768
+#define CT_IS_LONGDOUBLE 65536
#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \
CT_PRIMITIVE_UNSIGNED | \
CT_PRIMITIVE_CHAR | \
@@ -64,7 +65,8 @@
Py_ssize_t ct_size; /* size of instances, or -1 if unknown */
Py_ssize_t ct_length; /* length of arrays, or -1 if unknown;
- or alignment of primitive and struct types */
+ or alignment of primitive and struct types;
+ always -1 for pointers */
int ct_flags; /* CT_xxx flags */
int ct_name_position; /* index in ct_name of where to put a var name */
@@ -103,6 +105,7 @@
unsigned long long m_longlong;
float m_float;
double m_double;
+ long double m_longdouble;
} union_alignment;
typedef struct {
@@ -504,6 +507,12 @@
}
}
+static long double
+read_raw_longdouble_data(char *target)
+{
+ return *((long double*)target);
+}
+
static void
write_raw_float_data(char *target, double source, int size)
{
@@ -515,6 +524,12 @@
Py_FatalError("write_raw_float_data: bad float size");
}
+static void
+write_raw_longdouble_data(char *target, long double source)
+{
+ *((long double*)target) = source;
+}
+
static PyObject *
new_simple_cdata(char *data, CTypeDescrObject *ct)
{
@@ -554,6 +569,8 @@
return d_value;
}
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/
+
static PyObject *
convert_to_object(char *data, CTypeDescrObject *ct)
{
@@ -602,8 +619,17 @@
return PyLong_FromUnsignedLongLong(value);
}
else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
- double value = read_raw_float_data(data, ct->ct_size);
- return PyFloat_FromDouble(value);
+ if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) {
+ double value = read_raw_float_data(data, ct->ct_size);
+ return PyFloat_FromDouble(value);
+ }
+ else {
+ long double value = read_raw_longdouble_data(data);
+ CDataObject *cd = _new_casted_primitive(ct);
+ if (cd != NULL)
+ write_raw_longdouble_data(cd->c_data, value);
+ return (PyObject *)cd;
+ }
}
else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
if (ct->ct_size == sizeof(char))
@@ -735,78 +761,91 @@
}
static int
+convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
+{
+ /* used by convert_from_object(), and also to decode lists/tuples/unicodes
+ passed as function arguments. 'ct' is an CT_ARRAY in the first case
+ and a CT_POINTER in the second case. */
+ const char *expected;
+ CTypeDescrObject *ctitem = ct->ct_itemdescr;
+
+ if (PyList_Check(init) || PyTuple_Check(init)) {
+ PyObject **items;
+ Py_ssize_t i, n;
+ n = PySequence_Fast_GET_SIZE(init);
+ if (ct->ct_length >= 0 && n > ct->ct_length) {
+ PyErr_Format(PyExc_IndexError,
+ "too many initializers for '%s' (got %zd)",
+ ct->ct_name, n);
+ return -1;
+ }
+ items = PySequence_Fast_ITEMS(init);
+ for (i=0; i<n; i++) {
+ if (convert_from_object(data, ctitem, items[i]) < 0)
+ return -1;
+ data += ctitem->ct_size;
+ }
+ return 0;
+ }
+ else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
+ if (ctitem->ct_size == sizeof(char)) {
+ char *srcdata;
+ Py_ssize_t n;
+ if (!PyString_Check(init)) {
+ expected = "str or list or tuple";
+ goto cannot_convert;
+ }
+ n = PyString_GET_SIZE(init);
+ if (ct->ct_length >= 0 && n > ct->ct_length) {
+ PyErr_Format(PyExc_IndexError,
+ "initializer string is too long for '%s' "
+ "(got %zd characters)", ct->ct_name, n);
+ return -1;
+ }
+ if (n != ct->ct_length)
+ n++;
+ srcdata = PyString_AS_STRING(init);
+ memcpy(data, srcdata, n);
+ return 0;
+ }
+#ifdef HAVE_WCHAR_H
+ else {
+ Py_ssize_t n;
+ if (!PyUnicode_Check(init)) {
+ expected = "unicode or list or tuple";
+ goto cannot_convert;
+ }
+ n = _my_PyUnicode_SizeAsWideChar(init);
+ if (ct->ct_length >= 0 && n > ct->ct_length) {
+ PyErr_Format(PyExc_IndexError,
+ "initializer unicode is too long for '%s' "
+ "(got %zd characters)", ct->ct_name, n);
+ return -1;
+ }
+ if (n != ct->ct_length)
+ n++;
+ _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n);
+ return 0;
+ }
+#endif
+ }
+ else {
+ expected = "list or tuple";
+ goto cannot_convert;
+ }
+
+ cannot_convert:
+ return _convert_error(init, ct->ct_name, expected);
+}
+
+static int
convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
{
const char *expected;
char buf[sizeof(PY_LONG_LONG)];
if (ct->ct_flags & CT_ARRAY) {
- CTypeDescrObject *ctitem = ct->ct_itemdescr;
-
- if (PyList_Check(init) || PyTuple_Check(init)) {
- PyObject **items;
- Py_ssize_t i, n;
- n = PySequence_Fast_GET_SIZE(init);
- if (ct->ct_length >= 0 && n > ct->ct_length) {
- PyErr_Format(PyExc_IndexError,
- "too many initializers for '%s' (got %zd)",
- ct->ct_name, n);
- return -1;
- }
- items = PySequence_Fast_ITEMS(init);
- for (i=0; i<n; i++) {
- if (convert_from_object(data, ctitem, items[i]) < 0)
- return -1;
- data += ctitem->ct_size;
- }
- return 0;
- }
- else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
- if (ctitem->ct_size == sizeof(char)) {
- char *srcdata;
- Py_ssize_t n;
- if (!PyString_Check(init)) {
- expected = "str or list or tuple";
- goto cannot_convert;
- }
- n = PyString_GET_SIZE(init);
- if (ct->ct_length >= 0 && n > ct->ct_length) {
- PyErr_Format(PyExc_IndexError,
- "initializer string is too long for '%s' "
- "(got %zd characters)", ct->ct_name, n);
- return -1;
- }
- if (n != ct->ct_length)
- n++;
- srcdata = PyString_AS_STRING(init);
- memcpy(data, srcdata, n);
- return 0;
- }
-#ifdef HAVE_WCHAR_H
- else {
- Py_ssize_t n;
- if (!PyUnicode_Check(init)) {
- expected = "unicode or list or tuple";
- goto cannot_convert;
- }
- n = _my_PyUnicode_SizeAsWideChar(init);
- if (ct->ct_length >= 0 && n > ct->ct_length) {
- PyErr_Format(PyExc_IndexError,
- "initializer unicode is too long for '%s' "
- "(got %zd characters)", ct->ct_name, n);
- return -1;
- }
- if (n != ct->ct_length)
- n++;
- _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n);
- return 0;
- }
-#endif
- }
- else {
- expected = "list or tuple";
- goto cannot_convert;
- }
+ return convert_array_from_object(data, ct, init);
}
if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
char *ptrdata;
@@ -879,10 +918,22 @@
return 0;
}
if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
- double value = PyFloat_AsDouble(init);
+ double value;
+ if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+ CData_Check(init) &&
+ (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+ long double lvalue;
+ lvalue = read_raw_longdouble_data(((CDataObject *)init)->c_data);
+ write_raw_longdouble_data(data, lvalue);
+ return 0;
+ }
+ value = PyFloat_AsDouble(init);
if (value == -1.0 && PyErr_Occurred())
return -1;
- write_raw_float_data(data, value, ct->ct_size);
+ if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+ write_raw_float_data(data, value, ct->ct_size);
+ else
+ write_raw_longdouble_data(data, (long double)value);
return 0;
}
if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
@@ -1100,20 +1151,32 @@
return 0;
}
+static PyObject *cdata_float(CDataObject *cd); /*forward*/
+
static PyObject *cdata_repr(CDataObject *cd)
{
char *p, *extra;
PyObject *result, *s = NULL;
if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
- PyObject *o = convert_to_object(cd->c_data, cd->c_type);
- if (o == NULL)
- return NULL;
- s = PyObject_Repr(o);
- Py_DECREF(o);
- if (s == NULL)
- return NULL;
- p = PyString_AS_STRING(s);
+ if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+ PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+ if (o == NULL)
+ return NULL;
+ s = PyObject_Repr(o);
+ Py_DECREF(o);
+ if (s == NULL)
+ return NULL;
+ p = PyString_AS_STRING(s);
+ }
+ else {
+ long double lvalue = read_raw_longdouble_data(cd->c_data);
+ s = PyString_FromStringAndSize(NULL, 128); /* big enough */
+ if (s == NULL)
+ return NULL;
+ p = PyString_AS_STRING(s);
+ sprintf(p, "%LE", lvalue);
+ }
}
else {
if (cd->c_data != NULL) {
@@ -1138,89 +1201,6 @@
return result;
}
-static PyObject *cdata_str(CDataObject *cd)
-{
- if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
- cd->c_type->ct_size == sizeof(char)) {
- return PyString_FromStringAndSize(cd->c_data, 1);
- }
- else if (cd->c_type->ct_itemdescr != NULL &&
- cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
- cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
- Py_ssize_t length;
-
- if (cd->c_type->ct_flags & CT_ARRAY) {
- const char *start = cd->c_data;
- const char *end;
- length = get_array_length(cd);
- end = (const char *)memchr(start, 0, length);
- if (end != NULL)
- length = end - start;
- }
- else {
- if (cd->c_data == NULL) {
- PyObject *s = cdata_repr(cd);
- if (s != NULL) {
- PyErr_Format(PyExc_RuntimeError,
- "cannot use str() on %s",
- PyString_AS_STRING(s));
- Py_DECREF(s);
- }
- return NULL;
- }
- length = strlen(cd->c_data);
- }
-
- return PyString_FromStringAndSize(cd->c_data, length);
- }
- else if (cd->c_type->ct_flags & CT_IS_ENUM)
- return convert_to_object(cd->c_data, cd->c_type);
- else
- return Py_TYPE(cd)->tp_repr((PyObject *)cd);
-}
-
-#ifdef HAVE_WCHAR_H
-static PyObject *cdata_unicode(CDataObject *cd)
-{
- if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
- cd->c_type->ct_size == sizeof(wchar_t)) {
- return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
- }
- else if (cd->c_type->ct_itemdescr != NULL &&
- cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR &&
- cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)) {
- Py_ssize_t length;
- const wchar_t *start = (wchar_t *)cd->c_data;
-
- if (cd->c_type->ct_flags & CT_ARRAY) {
- const Py_ssize_t lenmax = get_array_length(cd);
- length = 0;
- while (length < lenmax && start[length])
- length++;
- }
- else {
- if (cd->c_data == NULL) {
- PyObject *s = cdata_repr(cd);
- if (s != NULL) {
- PyErr_Format(PyExc_RuntimeError,
- "cannot use unicode() on %s",
- PyString_AS_STRING(s));
- Py_DECREF(s);
- }
- return NULL;
- }
- length = 0;
- while (start[length])
- length++;
- }
-
- return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length);
- }
- else
- return Py_TYPE(cd)->tp_repr((PyObject *)cd);
-}
-#endif
-
static PyObject *cdataowning_repr(CDataObject *cd)
{
Py_ssize_t size;
@@ -1280,7 +1260,7 @@
#endif
}
else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
- PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+ PyObject *o = cdata_float(cd);
PyObject *r = o ? PyNumber_Int(o) : NULL;
Py_XDECREF(o);
return r;
@@ -1304,7 +1284,14 @@
static PyObject *cdata_float(CDataObject *cd)
{
if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
- return convert_to_object(cd->c_data, cd->c_type);
+ double value;
+ if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+ value = read_raw_float_data(cd->c_data, cd->c_type->ct_size);
+ }
+ else {
+ value = (double)read_raw_longdouble_data(cd->c_data);
+ }
+ return PyFloat_FromDouble(value);
}
PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'",
cd->c_type->ct_name);
@@ -1599,14 +1586,72 @@
return ct_int;
}
+static PyObject *
+_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init)
+{
+ /* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an
+ initializer for an array 'ITEM[]'. This includes the case of
+ passing a Python string to a 'char *' argument. */
+ Py_ssize_t length, datasize;
+ CTypeDescrObject *ctitem = ctptr->ct_itemdescr;
+ PyObject *result;
+ char *data;
+
+ /* XXX some code duplication, how to avoid it? */
+ if (PyString_Check(init)) {
+ /* from a string: just returning the string here is fine.
+ We assume that the C code won't modify the 'char *' data. */
+ if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) &&
+ (ctitem->ct_size == sizeof(char))) {
+ Py_INCREF(init);
+ return init;
+ }
+ else
+ return Py_None;
+ }
+ else if (PyList_Check(init) || PyTuple_Check(init)) {
+ length = PySequence_Fast_GET_SIZE(init);
+ }
+ else if (PyUnicode_Check(init)) {
+ /* from a unicode, we add the null terminator */
+ length = _my_PyUnicode_SizeAsWideChar(init) + 1;
+ }
+ else {
+ /* refuse to receive just an integer (and interpret it
+ as the array size) */
+ return Py_None;
+ }
+
+ if (ctitem->ct_size <= 0)
+ return Py_None;
+ datasize = length * ctitem->ct_size;
+ if ((datasize / ctitem->ct_size) != length) {
+ PyErr_SetString(PyExc_OverflowError,
+ "array size would overflow a Py_ssize_t");
+ return NULL;
+ }
+
+ result = PyString_FromStringAndSize(NULL, datasize);
+ if (result == NULL)
+ return NULL;
+
+ data = PyString_AS_STRING(result);
+ memset(data, 0, datasize);
+ if (convert_array_from_object(data, ctptr, init) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
static PyObject*
cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
{
char *buffer;
void** buffer_array;
cif_description_t *cif_descr;
- Py_ssize_t i, nargs, nargs_declared;
- PyObject *signature, *res, *fvarargs;
+ Py_ssize_t i, nargs, nargs_declared, free_me_until = 0;
+ PyObject *signature, *res = NULL, *fvarargs;
CTypeDescrObject *fresult;
char *resultdata;
char *errormsg;
@@ -1636,7 +1681,10 @@
/* regular case: this function does not take '...' arguments */
if (nargs != nargs_declared) {
errormsg = "'%s' expects %zd arguments, got %zd";
- goto bad_number_of_arguments;
+ bad_number_of_arguments:
+ PyErr_Format(PyExc_TypeError, errormsg,
+ cd->c_type->ct_name, nargs_declared, nargs);
+ goto error;
}
}
else {
@@ -1708,26 +1756,21 @@
else
argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
- if ((argtype->ct_flags & CT_POINTER) &&
- (argtype->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR)) {
- if (argtype->ct_itemdescr->ct_size == sizeof(char)) {
- if (PyString_Check(obj)) {
- /* special case: Python string -> cdata 'char *' */
- *(char **)data = PyString_AS_STRING(obj);
+ if (argtype->ct_flags & CT_POINTER) {
+ PyObject *string;
+ if (!CData_Check(obj)) {
+ string = _prepare_pointer_call_argument(argtype, obj);
+ if (string != Py_None) {
+ if (string == NULL)
+ goto error;
+ ((char **)data)[0] = PyString_AS_STRING(string);
+ ((char **)data)[1] = (char *)string;
+ assert(i < nargs_declared); /* otherwise, obj is a CData */
+ free_me_until = i + 1;
continue;
}
}
-#ifdef HAVE_WCHAR_H
- else {
- if (PyUnicode_Check(obj)) {
- /* Python Unicode string -> cdata 'wchar_t *':
- not supported yet */
- PyErr_SetString(PyExc_NotImplementedError,
- "automatic unicode-to-'wchar_t *' conversion");
- goto error;
- }
- }
-#endif
+ ((char **)data)[1] = NULL;
}
if (convert_from_object(data, argtype, obj) < 0) {
if (CData_Check(obj) && (argtype->ct_flags & CT_POINTER) &&
@@ -1745,10 +1788,12 @@
resultdata = buffer + cif_descr->exchange_offset_arg[0];
+ Py_BEGIN_ALLOW_THREADS
restore_errno();
ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data),
resultdata, buffer_array);
save_errno();
+ Py_END_ALLOW_THREADS
if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
CT_PRIMITIVE_UNSIGNED)) {
@@ -1771,23 +1816,26 @@
else {
res = convert_to_object(resultdata, fresult);
}
- PyObject_Free(buffer);
- done:
+ /* fall-through */
+
+ error:
+ for (i=0; i<free_me_until; i++) {
+ CTypeDescrObject *argtype;
+ argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i);
+ if (argtype->ct_flags & CT_POINTER) {
+ char *data = buffer + cif_descr->exchange_offset_arg[1 + i];
+ PyObject *string_or_null = (PyObject *)(((char **)data)[1]);
+ Py_XDECREF(string_or_null);
+ }
+ }
+ if (buffer)
+ PyObject_Free(buffer);
if (fvarargs != NULL) {
Py_DECREF(fvarargs);
if (cif_descr != NULL) /* but only if fvarargs != NULL, if variadic */
PyObject_Free(cif_descr);
}
return res;
-
- bad_number_of_arguments:
- PyErr_Format(PyExc_TypeError, errormsg,
- cd->c_type->ct_name, nargs_declared, nargs);
- error:
- if (buffer)
- PyObject_Free(buffer);
- res = NULL;
- goto done;
}
static PyObject *cdata_iter(CDataObject *);
@@ -1830,11 +1878,6 @@
(objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
};
-static PyMethodDef CData_methods[] = {
- {"__unicode__", (PyCFunction)cdata_unicode, METH_NOARGS},
- {NULL, NULL} /* sentinel */
-};
-
static PyTypeObject CData_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_cffi_backend.CData",
@@ -1851,7 +1894,7 @@
&CData_as_mapping, /* tp_as_mapping */
(hashfunc)cdata_hash, /* tp_hash */
(ternaryfunc)cdata_call, /* tp_call */
- (reprfunc)cdata_str, /* tp_str */
+ 0, /* tp_str */
(getattrofunc)cdata_getattro, /* tp_getattro */
(setattrofunc)cdata_setattro, /* tp_setattro */
0, /* tp_as_buffer */
@@ -1863,7 +1906,6 @@
0, /* tp_weaklistoffset */
(getiterfunc)cdata_iter, /* tp_iter */
0, /* tp_iternext */
- CData_methods, /* tp_methods */
};
static PyTypeObject CDataOwning_Type = {
@@ -2253,6 +2295,16 @@
}
value = (unsigned char)PyString_AS_STRING(io)[0];
}
+ else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+ CData_Check(io) &&
+ (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+ long double lvalue;
+ lvalue = read_raw_longdouble_data(((CDataObject *)io)->c_data);
+ cd = _new_casted_primitive(ct);
+ if (cd != NULL)
+ write_raw_longdouble_data(cd->c_data, lvalue);
+ return (PyObject *)cd;
+ }
else {
value = PyFloat_AsDouble(io);
}
@@ -2261,8 +2313,12 @@
return NULL;
cd = _new_casted_primitive(ct);
- if (cd != NULL)
- write_raw_float_data(cd->c_data, value, ct->ct_size);
+ if (cd != NULL) {
+ if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+ write_raw_float_data(cd->c_data, value, ct->ct_size);
+ else
+ write_raw_longdouble_data(cd->c_data, (long double)value);
+ }
return (PyObject *)cd;
}
else {
@@ -2504,7 +2560,8 @@
EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED ) \
EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED ) \
EPTYPE(f, float, CT_PRIMITIVE_FLOAT ) \
- EPTYPE(d, double, CT_PRIMITIVE_FLOAT )
+ EPTYPE(d, double, CT_PRIMITIVE_FLOAT ) \
+ EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE )
#ifdef HAVE_WCHAR_H
# define ENUM_PRIMITIVE_TYPES_WCHAR \
EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR )
@@ -2570,6 +2627,8 @@
ffitype = &ffi_type_float;
else if (strcmp(ptypes->name, "double") == 0)
ffitype = &ffi_type_double;
+ else if (strcmp(ptypes->name, "long double") == 0)
+ ffitype = &ffi_type_longdouble;
else
goto bad_ffi_type;
}
@@ -2629,6 +2688,7 @@
return NULL;
td->ct_size = sizeof(void *);
+ td->ct_length = -1;
td->ct_flags = CT_POINTER;
if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
td->ct_flags |= CT_IS_PTR_TO_OWNED;
@@ -3141,6 +3201,15 @@
exchange_offset = ALIGN_ARG(exchange_offset);
cif_descr->exchange_offset_arg[1 + i] = exchange_offset;
exchange_offset += atype->size;
+ /* if 'farg' is a pointer type 'ITEM *', then we might receive
+ as argument to the function call what is an initializer
+ for an array 'ITEM[]'. This includes the case of passing a
+ Python string to a 'char *' argument. In this case, we
+ convert the initializer to a cdata 'ITEM[]' that gets
+ temporarily stored here: */
+ if (farg->ct_flags & CT_POINTER) {
+ exchange_offset += sizeof(PyObject *);
+ }
}
}
@@ -3421,6 +3490,9 @@
{
save_errno();
{
+#ifdef WITH_THREAD
+ PyGILState_STATE state = PyGILState_Ensure();
+#endif
PyObject *cb_args = (PyObject *)userdata;
CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0);
PyObject *signature = ct->ct_stuff;
@@ -3455,6 +3527,9 @@
Py_XDECREF(py_args);
Py_XDECREF(py_res);
Py_DECREF(cb_args);
+#ifdef WITH_THREAD
+ PyGILState_Release(state);
+#endif
restore_errno();
return;
@@ -3734,6 +3809,84 @@
return s;
}
+static PyObject *b_string(PyObject *self, PyObject *args)
+{
+ CDataObject *cd;
+ Py_ssize_t maxlen = -1;
+ if (!PyArg_ParseTuple(args, "O!|n:string",
+ &CData_Type, &cd, &maxlen))
+ return NULL;
+
+ if (cd->c_type->ct_itemdescr != NULL &&
+ cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR |
+ CT_PRIMITIVE_SIGNED |
+ CT_PRIMITIVE_UNSIGNED)) {
+ Py_ssize_t length = maxlen;
+ if (cd->c_data == NULL) {
+ PyObject *s = cdata_repr(cd);
+ if (s != NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "cannot use string() on %s",
+ PyString_AS_STRING(s));
+ Py_DECREF(s);
+ }
+ return NULL;
+ }
+ if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) {
+ length = get_array_length(cd);
+ }
+ if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
+ const char *start = cd->c_data;
+ if (length < 0)
+ length = strlen(start);
+ else {
+ const char *end;
+ end = (const char *)memchr(start, 0, length);
+ if (end != NULL)
+ length = end - start;
+ }
+ return PyString_FromStringAndSize(start, length);
+ }
+#ifdef HAVE_WCHAR_H
+ else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) {
+ const wchar_t *start = (wchar_t *)cd->c_data;
+ assert(cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t));
+ if (length < 0) {
+ length = 0;
+ while (start[length])
+ length++;
+ }
+ else {
+ maxlen = length;
+ length = 0;
+ while (length < maxlen && start[length])
+ length++;
+ }
+ return _my_PyUnicode_FromWideChar(start, length);
+ }
+#endif
+ }
+ else if (cd->c_type->ct_flags & CT_IS_ENUM) {
+ return convert_to_object(cd->c_data, cd->c_type);
+ }
+ else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR |
+ CT_PRIMITIVE_SIGNED |
+ CT_PRIMITIVE_UNSIGNED)) {
+ if (cd->c_type->ct_size == sizeof(char)) {
+ return PyString_FromStringAndSize(cd->c_data, 1);
+ }
+#ifdef HAVE_WCHAR_H
+ else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
+ assert(cd->c_type->ct_size == sizeof(wchar_t));
+ return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
+ }
+#endif
+ }
+ PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument",
+ cd->c_type->ct_name);
+ return NULL;
+}
+
static PyObject *b_buffer(PyObject *self, PyObject *args)
{
CDataObject *cd;
@@ -3913,6 +4066,19 @@
return ptr->a1 + ptr->a2;
}
+static int _testfunc19(struct _testfunc17_s *ptr)
+{
+ return ptr->a1 + (int)ptr->a2;
+}
+
+static long double _testfunc20(long double x)
+{
+ int i;
+ for (i=0; i<28; i++)
+ x += x;
+ return x;
+}
+
static PyObject *b__testfunc(PyObject *self, PyObject *args)
{
/* for testing only */
@@ -3940,6 +4106,8 @@
case 16: f = &_testfunc16; break;
case 17: f = &_testfunc17; break;
case 18: f = &_testfunc18; break;
+ case 19: f = &_testfunc19; break;
+ case 20: f = &_testfunc20; break;
default:
PyErr_SetNone(PyExc_ValueError);
return NULL;
@@ -3968,6 +4136,7 @@
{"typeof", b_typeof, METH_O},
{"offsetof", b_offsetof, METH_VARARGS},
{"getcname", b_getcname, METH_VARARGS},
+ {"string", b_string, METH_VARARGS},
{"buffer", b_buffer, METH_VARARGS},
{"get_errno", b_get_errno, METH_NOARGS},
{"set_errno", b_set_errno, METH_VARARGS},
@@ -4143,7 +4312,7 @@
if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
return;
- v = PyString_FromString("0.2.1");
+ v = PyString_FromString("0.3");
if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
return;
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -21,7 +21,8 @@
LPVOID p = TlsGetValue(cffi_tls_index);
if (p == NULL) {
- p = PyMem_Malloc(sizeof(struct cffi_errno_s));
+ /* XXX this malloc() leaks */
+ p = malloc(sizeof(struct cffi_errno_s));
if (p == NULL)
return NULL;
memset(p, 0, sizeof(struct cffi_errno_s));
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -130,7 +130,7 @@
assert long(cast(p, 'A')) == 65L
assert type(int(cast(p, 'A'))) is int
assert type(long(cast(p, 'A'))) is long
- assert str(cast(p, 'A')) == 'A'
+ assert str(cast(p, 'A')) == repr(cast(p, 'A'))
assert repr(cast(p, 'A')) == "<cdata 'char' 'A'>"
assert repr(cast(p, 255)) == r"<cdata 'char' '\xff'>"
assert repr(cast(p, 0)) == r"<cdata 'char' '\x00'>"
@@ -235,7 +235,9 @@
assert p[0] == 'A'
py.test.raises(TypeError, newp, BPtr, 65)
py.test.raises(TypeError, newp, BPtr, "foo")
- assert str(cast(BChar, 'A')) == 'A'
+ c = cast(BChar, 'A')
+ assert str(c) == repr(c)
+ assert int(c) == ord('A')
py.test.raises(TypeError, cast, BChar, 'foo')
def test_reading_pointer_to_pointer():
@@ -295,6 +297,9 @@
py.test.raises(TypeError, "p[0]")
def test_default_str():
+ BChar = new_primitive_type("char")
+ x = cast(BChar, 42)
+ assert str(x) == repr(x)
BInt = new_primitive_type("int")
x = cast(BInt, 42)
assert str(x) == repr(x)
@@ -320,7 +325,7 @@
y = cast(BInt, x)
assert int(y) == 42
y = cast(new_primitive_type("char"), x)
- assert str(y) == chr(42)
+ assert int(y) == 42
y = cast(new_primitive_type("float"), x)
assert float(y) == 42.0
#
@@ -461,7 +466,7 @@
#
p = new_primitive_type("char")
n = cast(p, cast(p, "A"))
- assert str(n) == "A"
+ assert int(n) == ord("A")
def test_new_primitive_from_cdata():
p = new_primitive_type("int")
@@ -763,12 +768,22 @@
BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
f = cast(BFunc6bis, _testfunc(6))
#
- py.test.raises(TypeError, f, [142])
+ res = f([142])
+ assert typeof(res) is BIntPtr
+ assert res[0] == 142 - 1000
+ #
+ res = f((143,))
+ assert typeof(res) is BIntPtr
+ assert res[0] == 143 - 1000
#
x = newp(BIntArray, [242])
res = f(x)
assert typeof(res) is BIntPtr
assert res[0] == 242 - 1000
+ #
+ py.test.raises(TypeError, f, 123456)
+ py.test.raises(TypeError, f, "foo")
+ py.test.raises(TypeError, f, u"bar")
def test_call_function_7():
BChar = new_primitive_type("char")
@@ -965,14 +980,14 @@
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
e = cast(BEnum, 0)
assert repr(e) == "<cdata 'enum foo' 'def'>"
- assert str(e) == 'def'
- assert str(cast(BEnum, -20)) == 'ab'
- assert str(cast(BEnum, 'c')) == 'c'
+ assert string(e) == 'def'
+ assert string(cast(BEnum, -20)) == 'ab'
+ assert string(cast(BEnum, 'c')) == 'c'
assert int(cast(BEnum, 'c')) == 1
assert int(cast(BEnum, 'def')) == 0
assert int(cast(BEnum, -242 + 2**128)) == -242
- assert str(cast(BEnum, -242 + 2**128)) == '#-242'
- assert str(cast(BEnum, '#-20')) == 'ab'
+ assert string(cast(BEnum, -242 + 2**128)) == '#-242'
+ assert string(cast(BEnum, '#-20')) == 'ab'
assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
@@ -1122,11 +1137,12 @@
BArray1 = new_array_type(new_pointer_type(BChar), 5)
BArray2 = new_array_type(new_pointer_type(BArray1), 5)
a = newp(BArray2, ["abc", "de", "ghij"])
- assert str(a[2]) == "ghij"
+ assert string(a[1]) == "de"
+ assert string(a[2]) == "ghij"
a[2] = "."
- assert str(a[2]) == "."
+ assert string(a[2]) == "."
a[2] = "12345"
- assert str(a[2]) == "12345"
+ assert string(a[2]) == "12345"
e = py.test.raises(IndexError, 'a[2] = "123456"')
assert 'char[5]' in str(e.value)
assert 'got 6 characters' in str(e.value)
@@ -1219,16 +1235,53 @@
p2 = newp(new_pointer_type(BFunc), p1)
assert p2[0] == p1
-def test_str():
+def test_string():
BChar = new_primitive_type("char")
+ assert string(cast(BChar, 42)) == '*'
+ assert string(cast(BChar, 0)) == '\x00'
BCharP = new_pointer_type(BChar)
BArray = new_array_type(BCharP, 10)
a = newp(BArray, "hello")
assert len(a) == 10
- assert str(a) == "hello"
+ assert string(a) == "hello"
p = a + 2
- assert str(p) == "llo"
- py.test.raises(RuntimeError, str, cast(BCharP, 0))
+ assert string(p) == "llo"
+ assert string(newp(new_array_type(BCharP, 4), "abcd")) == "abcd"
+ py.test.raises(RuntimeError, string, cast(BCharP, 0))
+ assert string(a, 4) == "hell"
+ assert string(a, 5) == "hello"
+ assert string(a, 6) == "hello"
+
+def test_string_byte():
+ BByte = new_primitive_type("signed char")
+ assert string(cast(BByte, 42)) == '*'
+ assert string(cast(BByte, 0)) == '\x00'
+ BArray = new_array_type(new_pointer_type(BByte), None)
+ a = newp(BArray, [65, 66, 67])
+ assert type(string(a)) is str and string(a) == 'ABC'
+ #
+ BByte = new_primitive_type("unsigned char")
+ assert string(cast(BByte, 42)) == '*'
+ assert string(cast(BByte, 0)) == '\x00'
+ BArray = new_array_type(new_pointer_type(BByte), None)
+ a = newp(BArray, [65, 66, 67])
+ assert type(string(a)) is str and string(a) == 'ABC'
+
+def test_string_wchar():
+ BWChar = new_primitive_type("wchar_t")
+ assert string(cast(BWChar, 42)) == u'*'
+ assert string(cast(BWChar, 0x4253)) == u'\u4253'
+ assert string(cast(BWChar, 0)) == u'\x00'
+ BArray = new_array_type(new_pointer_type(BWChar), None)
+ a = newp(BArray, [u'A', u'B', u'C'])
+ assert type(string(a)) is unicode and string(a) == u'ABC'
+ assert string(a, 10) == u'ABC'
+
+def test_string_typeerror():
+ BShort = new_primitive_type("short")
+ BArray = new_array_type(new_pointer_type(BShort), None)
+ a = newp(BArray, [65, 66, 67])
+ py.test.raises(TypeError, string, a)
def test_bug_convert_to_ptr():
BChar = new_primitive_type("char")
@@ -1245,12 +1298,12 @@
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
p = newp(BStructPtr, None)
- assert str(p.a1) == ''
+ assert string(p.a1) == ''
p.a1 = 'foo'
- assert str(p.a1) == 'foo'
+ assert string(p.a1) == 'foo'
assert list(p.a1) == ['f', 'o', 'o'] + ['\x00'] * 7
p.a1 = ['x', 'y']
- assert str(p.a1) == 'xyo'
+ assert string(p.a1) == 'xyo'
def test_invalid_function_result_types():
BFunc = new_function_type((), new_void_type())
@@ -1348,6 +1401,14 @@
assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
assert s.a1 == 40
assert s.a2 == 40.0 * 40.0
+ #
+ BStruct17Ptr = new_pointer_type(BStruct17)
+ BFunc18 = new_function_type((BStruct17Ptr,), BInt)
+ f = cast(BFunc18, _testfunc(18))
+ x = f([[40, 2.5]])
+ assert x == 42
+ x = f([{'a2': 43.1}])
+ assert x == 43
def test_cast_with_functionptr():
BFunc = new_function_type((), new_void_type())
@@ -1372,7 +1433,7 @@
if wchar4:
x = cast(BWChar, 0x12345)
assert str(x) == "<cdata 'wchar_t' u'\U00012345'>"
- assert unicode(x) == u'\U00012345'
+ assert int(x) == 0x12345
else:
assert not pyuni4
#
@@ -1403,20 +1464,20 @@
BWCharArray = new_array_type(BWCharP, None)
a = newp(BWCharArray, u'hello \u1234 world')
assert len(a) == 14 # including the final null
- assert unicode(a) == u'hello \u1234 world'
+ assert string(a) == u'hello \u1234 world'
a[13] = u'!'
- assert unicode(a) == u'hello \u1234 world!'
+ assert string(a) == u'hello \u1234 world!'
assert str(a) == repr(a)
assert a[6] == u'\u1234'
a[6] = u'-'
- assert unicode(a) == 'hello - world!'
+ assert string(a) == u'hello - world!'
assert str(a) == repr(a)
#
if wchar4:
u = u'\U00012345\U00012346\U00012347'
a = newp(BWCharArray, u)
assert len(a) == 4
- assert unicode(a) == u
+ assert string(a) == u
assert len(list(a)) == 4
expected = [u'\U00012345', u'\U00012346', u'\U00012347', unichr(0)]
assert list(a) == expected
@@ -1427,17 +1488,17 @@
w = cast(BWChar, 'a')
assert repr(w) == "<cdata 'wchar_t' u'a'>"
assert str(w) == repr(w)
- assert unicode(w) == u'a'
+ assert string(w) == u'a'
assert int(w) == ord('a')
w = cast(BWChar, 0x1234)
assert repr(w) == "<cdata 'wchar_t' u'\u1234'>"
assert str(w) == repr(w)
- assert unicode(w) == u'\u1234'
+ assert string(w) == u'\u1234'
assert int(w) == 0x1234
w = cast(BWChar, u'\u8234')
assert repr(w) == "<cdata 'wchar_t' u'\u8234'>"
assert str(w) == repr(w)
- assert unicode(w) == u'\u8234'
+ assert string(w) == u'\u8234'
assert int(w) == 0x8234
w = cast(BInt, u'\u1234')
assert repr(w) == "<cdata 'int' 4660>"
@@ -1445,7 +1506,7 @@
w = cast(BWChar, u'\U00012345')
assert repr(w) == "<cdata 'wchar_t' u'\U00012345'>"
assert str(w) == repr(w)
- assert unicode(w) == u'\U00012345'
+ assert string(w) == u'\U00012345'
assert int(w) == 0x12345
w = cast(BInt, u'\U00012345')
assert repr(w) == "<cdata 'int' 74565>"
@@ -1455,34 +1516,33 @@
#
a = newp(BWCharArray, u'hello - world')
p = cast(BWCharP, a)
- assert unicode(p) == u'hello - world'
+ assert string(p) == u'hello - world'
p[6] = u'\u2345'
- assert unicode(p) == u'hello \u2345 world'
+ assert string(p) == u'hello \u2345 world'
#
s = newp(BStructPtr, [u'\u1234', p])
assert s.a1 == u'\u1234'
assert s.a2 == p
assert str(s.a2) == repr(s.a2)
- assert unicode(s.a2) == u'hello \u2345 world'
+ assert string(s.a2) == u'hello \u2345 world'
#
q = cast(BWCharP, 0)
assert str(q) == repr(q)
- py.test.raises(RuntimeError, unicode, q)
+ py.test.raises(RuntimeError, string, q)
#
def cb(p):
assert repr(p).startswith("<cdata 'wchar_t *' 0x")
- return len(unicode(p))
+ return len(string(p))
BFunc = new_function_type((BWCharP,), BInt, False)
f = callback(BFunc, cb, -42)
- #assert f(u'a\u1234b') == 3 -- not implemented
- py.test.raises(NotImplementedError, f, u'a\u1234b')
+ assert f(u'a\u1234b') == 3
#
if wchar4 and not pyuni4:
# try out-of-range wchar_t values
x = cast(BWChar, 1114112)
- py.test.raises(ValueError, unicode, x)
+ py.test.raises(ValueError, string, x)
x = cast(BWChar, -1)
- py.test.raises(ValueError, unicode, x)
+ py.test.raises(ValueError, string, x)
def test_keepalive_struct():
# exception to the no-keepalive rule: p=newp(BStructPtr) returns a
@@ -1594,12 +1654,12 @@
assert c[2] == '-'
assert str(buf) == "hi-there\x00"
buf[:2] = 'HI'
- assert str(c) == 'HI-there'
+ assert string(c) == 'HI-there'
assert buf[:4:2] == 'H-'
if '__pypy__' not in sys.builtin_module_names:
# XXX pypy doesn't support the following assignment so far
buf[:4:2] = 'XY'
- assert str(c) == 'XIYthere'
+ assert string(c) == 'XIYthere'
def test_getcname():
BUChar = new_primitive_type("unsigned char")
@@ -1705,3 +1765,75 @@
assert x.a1 == 0
assert len(x.a2) == 2
assert list(x.a2) == [4, 5]
+
+def test_autocast_int():
+ BInt = new_primitive_type("int")
+ BIntPtr = new_pointer_type(BInt)
+ BLongLong = new_primitive_type("long long")
+ BULongLong = new_primitive_type("unsigned long long")
+ BULongLongPtr = new_pointer_type(BULongLong)
+ x = newp(BIntPtr, cast(BInt, 42))
+ assert x[0] == 42
+ x = newp(BIntPtr, cast(BLongLong, 42))
+ assert x[0] == 42
+ x = newp(BIntPtr, cast(BULongLong, 42))
+ assert x[0] == 42
+ x = newp(BULongLongPtr, cast(BInt, 42))
+ assert x[0] == 42
+ py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42))
+ x = cast(BInt, cast(BInt, 42))
+ assert int(x) == 42
+ x = cast(BInt, cast(BLongLong, 42))
+ assert int(x) == 42
+ x = cast(BInt, cast(BULongLong, 42))
+ assert int(x) == 42
+ x = cast(BULongLong, cast(BInt, 42))
+ assert int(x) == 42
+ x = cast(BULongLong, cast(BInt, -42))
+ assert int(x) == 2 ** 64 - 42
+ x = cast(BIntPtr, cast(BInt, 42))
+ assert int(cast(BInt, x)) == 42
+
+def test_autocast_float():
+ BFloat = new_primitive_type("float")
+ BDouble = new_primitive_type("float")
+ BFloatPtr = new_pointer_type(BFloat)
+ x = newp(BFloatPtr, cast(BDouble, 12.5))
+ assert x[0] == 12.5
+ x = cast(BFloat, cast(BDouble, 12.5))
+ assert float(x) == 12.5
+
+def test_longdouble():
+ py_py = 'PY_DOT_PY' in globals()
+ BLongDouble = new_primitive_type("long double")
+ BLongDoublePtr = new_pointer_type(BLongDouble)
+ BLongDoubleArray = new_array_type(BLongDoublePtr, None)
+ a = newp(BLongDoubleArray, 1)
+ x = a[0]
+ if not py_py:
+ assert repr(x).startswith("<cdata 'long double' 0.0")
+ assert float(x) == 0.0
+ assert int(x) == 0
+ #
+ b = newp(BLongDoubleArray, [1.23])
+ x = b[0]
+ if not py_py:
+ assert repr(x).startswith("<cdata 'long double' 1.23")
+ assert float(x) == 1.23
+ assert int(x) == 1
+ #
+ BFunc19 = new_function_type((BLongDouble,), BLongDouble)
+ f = cast(BFunc19, _testfunc(19))
+ start = 8
+ for i in range(107):
+ start = f(start)
+ if sizeof(BLongDouble) > sizeof(new_primitive_type("double")):
+ if not py_py:
+ assert repr(start).startswith("<cdata 'long double' 6.15")
+ assert repr(start).endswith("E+902>")
+ #
+ c = newp(BLongDoubleArray, [start])
+ x = c[0]
+ if not py_py:
+ assert repr(x).endswith("E+902>")
+ assert float(x) == float("inf")
diff --git a/cffi/__init__.py b/cffi/__init__.py
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -4,5 +4,5 @@
from .api import FFI, CDefError, FFIError
from .ffiplatform import VerificationError, VerificationMissing
-__version__ = "0.2.1"
-__version_info__ = (0, 2, 1)
+__version__ = "0.3"
+__version_info__ = (0, 3)
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -180,6 +180,24 @@
cdecl = self._typeof(cdecl)
return self._backend.cast(cdecl, source)
+ def string(self, cdata, maxlen=-1):
+ """Return a Python string (or unicode string) from the 'cdata'.
+ If 'cdata' is a pointer or array of characters or bytes, returns
+ the null-terminated string. The returned string extends until
+ the first null character, or at most 'maxlen' characters. If
+ 'cdata' is an array then 'maxlen' defaults to its length.
+
+ If 'cdata' is a pointer or array of wchar_t, returns a unicode
+ string following the same rules.
+
+ If 'cdata' is a single character or byte or a wchar_t, returns
+ it as a string or unicode string.
+
+ If 'cdata' is an enum, returns the value of the enumerator as a
+ string, or "#NUMBER" if the value is out of range.
+ """
+ return self._backend.string(cdata, maxlen)
+
def buffer(self, cdata, size=-1):
"""Return a read-write buffer object that references the raw C data
pointed to by the given 'cdata'. The 'cdata' must be a pointer or
@@ -259,41 +277,43 @@
#
backend = ffi._backend
backendlib = backend.load_library(path)
- function_cache = {}
+ #
+ def make_accessor(name):
+ key = 'function ' + name
+ if key in ffi._parser._declarations:
+ tp = ffi._parser._declarations[key]
+ BType = ffi._get_cached_btype(tp)
+ value = backendlib.load_function(BType, name)
+ library.__dict__[name] = value
+ return
+ #
+ key = 'variable ' + name
+ if key in ffi._parser._declarations:
+ tp = ffi._parser._declarations[key]
+ BType = ffi._get_cached_btype(tp)
+ read_variable = backendlib.read_variable
+ write_variable = backendlib.write_variable
+ setattr(FFILibrary, name, property(
+ lambda self: read_variable(BType, name),
+ lambda self, value: write_variable(BType, name, value)))
+ return
+ #
+ raise AttributeError(name)
#
class FFILibrary(object):
- def __getattribute__(self, name):
+ def __getattr__(self, name):
+ make_accessor(name)
+ return getattr(self, name)
+ def __setattr__(self, name, value):
try:
- return function_cache[name]
- except KeyError:
- pass
- #
- key = 'function ' + name
- if key in ffi._parser._declarations:
- tp = ffi._parser._declarations[key]
- BType = ffi._get_cached_btype(tp)
- value = backendlib.load_function(BType, name)
- function_cache[name] = value
- return value
- #
- key = 'variable ' + name
- if key in ffi._parser._declarations:
- tp = ffi._parser._declarations[key]
- BType = ffi._get_cached_btype(tp)
- return backendlib.read_variable(BType, name)
- #
- raise AttributeError(name)
-
- def __setattr__(self, name, value):
- key = 'variable ' + name
- if key in ffi._parser._declarations:
- tp = ffi._parser._declarations[key]
- BType = ffi._get_cached_btype(tp)
- backendlib.write_variable(BType, name, value)
- return
- #
- raise AttributeError(name)
+ property = getattr(self.__class__, name)
+ except AttributeError:
+ make_accessor(name)
+ setattr(self, name, value)
+ else:
+ property.__set__(self, value)
#
if libname is not None:
FFILibrary.__name__ = 'FFILibrary_%s' % libname
- return FFILibrary(), function_cache
+ library = FFILibrary()
+ return library, library.__dict__
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -1,4 +1,4 @@
-import ctypes, ctypes.util, operator
+import ctypes, ctypes.util, operator, sys
from . import model
class CTypesData(object):
@@ -116,6 +116,9 @@
def __hash__(self):
return hash(type(self)) ^ hash(self._convert_to_address(None))
+ def _to_string(self, maxlen):
+ raise TypeError("string(): %r" % (self,))
+
class CTypesGenericPrimitive(CTypesData):
__slots__ = []
@@ -314,7 +317,10 @@
elif name in ('float', 'double'):
kind = 'float'
else:
- kind = 'int'
+ if name in ('signed char', 'unsigned char'):
+ kind = 'byte'
+ else:
+ kind = 'int'
is_signed = (ctype(-1).value == -1)
#
def _cast_source_to_int(source):
@@ -345,7 +351,7 @@
return ctype()
return ctype(CTypesPrimitive._to_ctypes(init))
- if kind == 'int':
+ if kind == 'int' or kind == 'byte':
@classmethod
def _cast_from(cls, source):
source = _cast_source_to_int(source)
@@ -362,8 +368,6 @@
return cls(source)
def __int__(self):
return ord(self._value)
- def __str__(self):
- return self._value
if kind == 'float':
@classmethod
@@ -386,7 +390,7 @@
_cast_to_integer = __int__
- if kind == 'int':
+ if kind == 'int' or kind == 'byte':
@staticmethod
def _to_ctypes(x):
if not isinstance(x, (int, long)):
@@ -428,13 +432,24 @@
@staticmethod
def _initialize(blob, init):
blob.value = CTypesPrimitive._to_ctypes(init)
+
+ if kind == 'char':
+ def _to_string(self, maxlen):
+ return self._value
+ if kind == 'byte':
+ def _to_string(self, maxlen):
+ return chr(self._value & 0xff)
#
CTypesPrimitive._fix_class()
return CTypesPrimitive
def new_pointer_type(self, BItem):
- if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+ getbtype = self.ffi._get_cached_btype
+ if BItem is getbtype(model.PrimitiveType('char')):
kind = 'charp'
+ elif BItem in (getbtype(model.PrimitiveType('signed char')),
+ getbtype(model.PrimitiveType('unsigned char'))):
+ kind = 'bytep'
else:
kind = 'generic'
#
@@ -483,11 +498,6 @@
self._as_ctype_ptr[index] = BItem._to_ctypes(value)
if kind == 'charp':
- def __str__(self):
- n = 0
- while self._as_ctype_ptr[n] != '\x00':
- n += 1
- return ''.join([self._as_ctype_ptr[i] for i in range(n)])
@classmethod
def _arg_to_ctypes(cls, value):
if isinstance(value, str):
@@ -495,6 +505,17 @@
else:
return super(CTypesPtr, cls)._arg_to_ctypes(value)
+ if kind == 'charp' or kind == 'bytep':
+ def _to_string(self, maxlen):
+ if maxlen < 0:
+ maxlen = sys.maxint
+ p = ctypes.cast(self._as_ctype_ptr,
+ ctypes.POINTER(ctypes.c_char))
+ n = 0
+ while n < maxlen and p[n] != '\x00':
+ n += 1
+ return ''.join([p[i] for i in range(n)])
+
def _get_own_repr(self):
if getattr(self, '_own', False):
return 'owning %d bytes' % (
@@ -514,8 +535,12 @@
else:
brackets = ' &[%d]' % length
BItem = CTypesPtr._BItem
- if BItem is self.ffi._get_cached_btype(model.PrimitiveType('char')):
+ getbtype = self.ffi._get_cached_btype
+ if BItem is getbtype(model.PrimitiveType('char')):
kind = 'char'
+ elif BItem in (getbtype(model.PrimitiveType('signed char')),
+ getbtype(model.PrimitiveType('unsigned char'))):
+ kind = 'byte'
else:
kind = 'generic'
#
@@ -567,14 +592,16 @@
raise IndexError
self._blob[index] = BItem._to_ctypes(value)
- if kind == 'char':
- def __str__(self):
- s = ''.join(self._blob)
- try:
- s = s[:s.index('\x00')]
- except ValueError:
- pass
- return s
+ if kind == 'char' or kind == 'byte':
+ def _to_string(self, maxlen):
+ if maxlen < 0:
+ maxlen = len(self._blob)
+ p = ctypes.cast(self._blob,
+ ctypes.POINTER(ctypes.c_char))
+ n = 0
+ while n < maxlen and p[n] != '\x00':
+ n += 1
+ return ''.join([p[i] for i in range(n)])
def _get_own_repr(self):
if getattr(self, '_own', False):
@@ -840,7 +867,7 @@
__slots__ = []
_reftypename = 'enum %s &' % name
- def __str__(self):
+ def _to_string(self, maxlen):
return str(CTypesEnum._from_ctypes(self._value))
@classmethod
@@ -870,6 +897,9 @@
def set_errno(self, value):
ctypes.set_errno(value)
+ def string(self, b, maxlen=-1):
+ return b._to_string(maxlen)
+
def buffer(self, bptr, size=-1):
# haaaaaaaaaaaack
call = ctypes.pythonapi.PyBuffer_FromReadWriteMemory
diff --git a/demo/_curses.py b/demo/_curses.py
--- a/demo/_curses.py
+++ b/demo/_curses.py
@@ -120,9 +120,9 @@
globals().update(lib.__dict__)
for key in range(KEY_MIN, KEY_MAX):
key_n = keyname(key)
- if key_n == ffi.NULL or str(key_n) == "UNKNOWN KEY":
+ if key_n == ffi.NULL or ffi.string(key_n) == "UNKNOWN KEY":
continue
- key_n = str(key_n).replace('(', '').replace(')', '')
+ key_n = ffi.string(key_n).replace('(', '').replace(')', '')
globals()[key_n] = key
_setup()
diff --git a/demo/bsdopendirtype.py b/demo/bsdopendirtype.py
--- a/demo/bsdopendirtype.py
+++ b/demo/bsdopendirtype.py
@@ -49,7 +49,7 @@
if ffi.errno != 0:
raise _posix_error()
return
- name = str(dirent.d_name)
+ name = ffi.string(dirent.d_name)
if name == '.' or name == '..':
continue
name = dirname + name
diff --git a/demo/gmp.py b/demo/gmp.py
--- a/demo/gmp.py
+++ b/demo/gmp.py
@@ -27,4 +27,4 @@
lib.mpz_add(a, a, b) # a=a+b
s = lib.mpz_get_str(ffi.NULL, 10, a)
-print str(s)
+print ffi.string(s)
diff --git a/demo/pwuid.py b/demo/pwuid.py
--- a/demo/pwuid.py
+++ b/demo/pwuid.py
@@ -11,4 +11,4 @@
#include <sys/types.h>
#include <pwd.h>
""")
-print str(C.getpwuid(0).pw_name)
+print ffi.string(C.getpwuid(0).pw_name)
diff --git a/demo/readdir.py b/demo/readdir.py
--- a/demo/readdir.py
+++ b/demo/readdir.py
@@ -48,7 +48,7 @@
break
if result[0] == ffi.NULL:
break
- name = str(dirent.d_name)
+ name = ffi.string(dirent.d_name)
print '%3d %s' % (dirent.d_type, name)
if dirent.d_type == 4 and name != '.' and name != '..':
walk(dirfd, name)
diff --git a/demo/readdir2.py b/demo/readdir2.py
--- a/demo/readdir2.py
+++ b/demo/readdir2.py
@@ -55,7 +55,7 @@
break
if result[0] == ffi.NULL:
break
- name = str(dirent.d_name)
+ name = ffi.string(dirent.d_name)
print '%3d %s' % (dirent.d_type, name)
if dirent.d_type == ffi.C.DT_DIR and name != '.' and name != '..':
walk(dirfd, name)
diff --git a/demo/readdir_ctypes.py b/demo/readdir_ctypes.py
new file mode 100644
--- /dev/null
+++ b/demo/readdir_ctypes.py
@@ -0,0 +1,69 @@
+# A Linux-only demo
+#
+# For comparison purposes, this is a ctypes version of readdir.py.
+import sys
+import ctypes
+
+if not sys.platform.startswith('linux'):
+ raise Exception("Linux-only demo")
+
+
+DIR_p = ctypes.c_void_p
+ino_t = ctypes.c_long
+off_t = ctypes.c_long
+
+class DIRENT(ctypes.Structure):
+ _fields_ = [
+ ('d_ino', ino_t), # inode number
+ ('d_off', off_t), # offset to the next dirent
+ ('d_reclen', ctypes.c_ushort), # length of this record
+ ('d_type', ctypes.c_ubyte), # type of file; not supported
+ # by all file system types
+ ('d_name', ctypes.c_char * 256), # filename
+ ]
+DIRENT_p = ctypes.POINTER(DIRENT)
+DIRENT_pp = ctypes.POINTER(DIRENT_p)
+
+C = ctypes.CDLL(None)
+
+readdir_r = C.readdir_r
+readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp]
+readdir_r.restype = ctypes.c_int
+
+openat = C.openat
+openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]
+openat.restype = ctypes.c_int
+
+fdopendir = C.fdopendir
+fdopendir.argtypes = [ctypes.c_int]
+fdopendir.restype = DIR_p
+
+closedir = C.closedir
+closedir.argtypes = [DIR_p]
+closedir.restype = ctypes.c_int
+
+
+def walk(basefd, path):
+ print '{', path
+ dirfd = openat(basefd, path, 0)
+ if dirfd < 0:
+ # error in openat()
+ return
+ dir = fdopendir(dirfd)
+ dirent = DIRENT()
+ result = DIRENT_p()
+ while True:
+ if readdir_r(dir, dirent, result):
+ # error in readdir_r()
+ break
+ if not result:
+ break
+ name = dirent.d_name
+ print '%3d %s' % (dirent.d_type, name)
+ if dirent.d_type == 4 and name != '.' and name != '..':
+ walk(dirfd, name)
+ closedir(dir)
+ print '}'
+
+
+walk(-1, "/tmp")
diff --git a/doc/source/conf.py b/doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -45,9 +45,9 @@
# built documents.
#
# The short X.Y version.
-version = '0.2.1'
+version = '0.3'
# The full version, including alpha/beta/rc tags.
-release = '0.2.1'
+release = '0.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -87,9 +87,9 @@
* https://bitbucket.org/cffi/cffi/downloads
- - https://bitbucket.org/cffi/cffi/get/release-0.2.1.tar.bz2 has
- a MD5 of c4de415fda3e14209c8a997671a12b83 and SHA of
- 790f8bd96713713bbc3030eb698a85cdf43e44ab
+ - https://bitbucket.org/cffi/cffi/get/release-0.3.tar.bz2
+ has a MD5 of xxx and SHA of
+ xxx
- or get it via ``hg clone https://bitbucket.org/cffi/cffi``
@@ -194,7 +194,7 @@
#include <pwd.h>
""")
p = C.getpwuid(0)
- assert str(p.pw_name) == 'root'
+ assert ffi.string(p.pw_name) == 'root'
Note that the above example works independently of the exact layout of
``struct passwd``. It requires a C compiler the first time you run it,
@@ -345,7 +345,7 @@
* char, short, int, long, long long (both signed and unsigned)
-* float, double
+* float, double, long double
* intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t,
size_t, ssize_t
@@ -615,11 +615,11 @@
>>> x[5] # the last item in the array
'\x00'
>>> x[0] = 'H' # change the first item
- >>> str(x) # interpret 'x' as a regular null-terminated string
+ >>> ffi.string(x) # interpret 'x' as a regular null-terminated string
'Hello'
Similarly, arrays of wchar_t can be initialized from a unicode string,
-and calling ``unicode()`` on the cdata object returns the current unicode
+and calling ``ffi.string()`` on the cdata object returns the current unicode
string stored in the wchar_t array (encoding and decoding surrogates as
needed if necessary).
@@ -658,6 +658,7 @@
ffi.cdef("""
int main_like(int argv, char *argv[]);
""")
+ lib = ffi.dlopen("some_library.so")
Now, everything is simple, except, how do we create the ``char**`` argument
here?
@@ -665,20 +666,34 @@
.. code-block:: python
- argv = ffi.new("char *[]", ["arg0", "arg1"])
+ lib.main_like(2, ["arg0", "arg1"])
-Does not work, because the initializer receives python ``str`` instead of
-``char*``. Now, the following would almost work:
+does not work, because the initializer receives two Python ``str`` objects
+where it was expecting ``<cdata 'char *'>`` objects. You need to use
+``ffi.new()`` explicitly to make these objects:
.. code-block:: python
+ lib.main_like(2, [ffi.new("char[]", "arg0"),
+ ffi.new("char[]", "arg1")])
+
+Note that the two ``<cdata 'char[]'>`` objects are kept alive for the
+duration of the call: they are only freed when the list itself is freed,
+and the list is only freed when the call returns.
+
+If you want instead to build an "argv" variable that you want to reuse,
+then more care is needed:
+
+.. code-block:: python
+
+ # DOES NOT WORK!
argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"),
ffi.new("char[]", "arg1")])
-However, the two ``char[]`` objects will not be automatically kept alive.
-To keep them alive, one solution is to make sure that the list is stored
-somewhere for long enough.
-For example:
+In the above example, the inner "arg0" string is deallocated as soon
+as "argv" is built. You have to make sure that you keep a reference
+to the inner "char[]" objects, either directly or by keeping the list
+alive like this:
.. code-block:: python
@@ -686,7 +701,12 @@
ffi.new("char[]", "arg1")]
argv = ffi.new("char *[]", argv_keepalive)
-will work.
+
+.. versionchanged:: 0.3
+ In older versions, passing a list as the ``char *[]`` argument did
+ not work; you needed to make an ``argv_keepalive`` and an ``argv``
+ in all cases.
+
Function calls
--------------
@@ -714,11 +734,16 @@
assert C.strlen("hello") == 5
-So far passing unicode strings as ``wchar_t *`` arguments is not
-implemented. You need to write e.g.::
-
- >>> C.wcslen(ffi.new("wchar_t[]", u"foo"))
- 3
+You can also pass unicode strings as ``wchar_t *`` arguments. Note that
+in general, there is no difference between C argument declarations that
+use ``type *`` or ``type[]``. For example, ``int *`` is fully
+equivalent to ``int[]`` or ``int[5]``. So you can pass an ``int *`` as
+a list of integers::
+
+ ffi.cdef("""
+ void do_something_with_array(int *array);
+ """)
+ lib.do_something_with_array([1, 2, 3, 4, 5])
CFFI supports passing and returning structs to functions and callbacks.
Example (sketch)::
@@ -813,10 +838,31 @@
and restore the ``GetLastError()`` value, but to access it you need to
declare and call the ``GetLastError()`` function as usual.
+``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
+
+- If 'cdata' is a pointer or array of characters or bytes, returns the
+ null-terminated string. The returned string extends until the first
+ null character, or at most 'maxlen' characters. If 'cdata' is an
+ array then 'maxlen' defaults to its length.
+
+- If 'cdata' is a pointer or array of wchar_t, returns a unicode string
+ following the same rules.
+
+- If 'cdata' is a single character or byte or a wchar_t, returns it as a
+ string or unicode string. (Note that in some situation a single
+ wchar_t may require a Python unicode string of length 2.)
+
+- If 'cdata' is an enum, returns the value of the enumerator as a
+ string, or ``#NUMBER`` if the value is out of range.
+
``ffi.buffer(pointer, [size])``: return a read-write 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. To get a copy of it
-in a regular string, call str() on the result. If unspecified, the
+in a regular string, use ``ffi.buffer(..)[:]``. To change the content,
+use ``ffi.buffer(..)[:] = new_string``. If unspecified, the
default size of the buffer is ``sizeof(*pointer)`` or the whole size of
the array. Getting a buffer is useful because you can read from it
without an extra copy, or write into it to change the original value;
@@ -874,10 +920,10 @@
| | (but not a float!). | on the type | |
| | Must be within range. | | |
+---------------+------------------------+------------------+----------------+
-| ``char`` | a string of length 1 | a string of | str(), int() |
+| ``char`` | a string of length 1 | a string of | int() |
| | or another <cdata char>| length 1 | |
+---------------+------------------------+------------------+----------------+
-| ``wchar_t`` | a unicode of length 1 | a unicode of | unicode(), |
+| ``wchar_t`` | a unicode of length 1 | a unicode of | |
| | (or maybe 2 if | length 1 | int() |
| | surrogates) or | (or maybe 2 if | |
| | another <cdata wchar_t>| surrogates) | |
@@ -885,33 +931,27 @@
| ``float``, | a float or anything on | a Python float | float(), int() |
| ``double`` | which float() works | | |
+---------------+------------------------+------------------+----------------+
+|``long double``| another <cdata> with | a <cdata>, to | float(), int() |
+| | a ``long double``, or | avoid loosing | |
+| | anything on which | precision `(***)`| |
+| | float() works | | |
++---------------+------------------------+------------------+----------------+
| pointers | another <cdata> with | a <cdata> | ``[]``, ``+``, |
| | a compatible type (i.e.| | ``-`` |
| | same type or ``char*`` | | |
| | or ``void*``, or as an | | |
-| | array instead) | | |
-+---------------+------------------------+ +----------------+
-| ``void *`` | another <cdata> with | | |
-| | any pointer or array | | |
+| | array instead) `(*)` | | |
++---------------+------------------------+ | |
+| ``void *``, | another <cdata> with | | |
+| ``char *`` | any pointer or array | | |
| | type | | |
+---------------+------------------------+ +----------------+
-| ``char *`` | another <cdata> with | | ``[]``, |
-| | any pointer or array | | ``+``, ``-``, |
-| | type, or | | str() |
-| | a Python string when | | |
-| | passed as func argument| | |
-+---------------+------------------------+ +----------------+
-| ``wchar_t *`` | same as pointers | | ``[]``, |
-| | (passing a unicode as | | ``+``, ``-``, |
-| | func argument is not | | unicode() |
-| | implemented) | | |
-+---------------+------------------------+ +----------------+
-| pointers to | same as pointers | | ``[]``, |
+| pointers to | same as pointers `(*)` | | ``[]``, |
| structure or | | | ``+``, ``-``, |
| union | | | and read/write |
| | | | struct fields |
-+---------------+ | +----------------+
-| function | | | call |
++---------------+------------------------+ +----------------+
+| function | same as pointers | | call `(**)` |
| pointers | | | |
+---------------+------------------------+------------------+----------------+
| arrays | a list or tuple of | a <cdata> | len(), iter(), |
@@ -920,12 +960,12 @@
+---------------+------------------------+ +----------------+
| ``char[]`` | same as arrays, or a | | len(), iter(), |
| | Python string | | ``[]``, ``+``, |
-| | | | ``-``, str() |
+| | | | ``-`` |
+---------------+------------------------+ +----------------+
| ``wchar_t[]`` | same as arrays, or a | | len(), iter(), |
| | Python unicode | | ``[]``, |
-| | | | ``+``, ``-``, |
-| | | | unicode() |
+| | | | ``+``, ``-`` |
+| | | | |
+---------------+------------------------+------------------+----------------+
| structure | a list or tuple or | a <cdata> | read/write |
| | dict of the field | | fields |
@@ -935,12 +975,39 @@
| union | same as struct, but | | read/write |
| | with at most one field | | fields |
+---------------+------------------------+------------------+----------------+
-| enum | an integer, or the enum| the enum value | int(), str() |
+| enum | an integer, or the enum| the enum value | int() |
| | value as a string or | as a string, or | |
| | as ``"#NUMBER"`` | ``"#NUMBER"`` | |
| | | if out of range | |
+---------------+------------------------+------------------+----------------+
+.. versionchanged:: 0.3
+ `(*)` Note that when calling a function, as per C, a ``item *`` argument
+ is identical to a ``item[]`` argument. So you can pass an argument that
+ is accepted by either C type, like for example passing a Python string
+ to a ``char *`` argument (because it works for ``char[]`` arguments)
+ or a list of integers to a ``int *`` argument (it works for ``int[]``
+ arguments). Note that even if you want to pass a single ``item``,
+ you need to specify it in a list of length 1; for example, a ``struct
+ foo *`` argument might be passed as ``[[field1, field2...]]``.
+
+As an optimization, the CPython version of CFFI assumes that a function
+with a ``char *`` argument to which you pass a Python string will not
+actually modify the array of characters passed in, and so passes directly
+a pointer inside the Python string object.
+
+.. versionchanged:: 0.3
+ `(**)` C function calls are now done with the GIL released.
+
+.. versionadded:: 0.3
+ `(***)` ``long double`` support.
+ Such a number is passed around in a cdata object to avoid loosing
+ precision, because a normal Python floating-point number only contains
+ enough precision for a ``double``. To convert it to a regular float,
+ call ``float()``. If you want to operate on such numbers
+ without any precision loss, you need to define and use a family of C
+ functions like ``long double add(long double a, long double b);``.
+
Reference: verifier
-------------------
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -5,14 +5,15 @@
sources = ['c/_cffi_backend.c']
libraries = ['ffi']
-include_dirs = []
+include_dirs = ['/usr/include/ffi',
+ '/usr/include/libffi'] # may be changed by pkg-config
define_macros = []
library_dirs = []
extra_compile_args = []
extra_link_args = []
-def _ask_pkg_config(option, result_prefix=''):
+def _ask_pkg_config(resultlist, option, result_prefix=''):
try:
p = subprocess.Popen(['pkg-config', option, 'libffi'],
stdout=subprocess.PIPE, stderr=open('/dev/null', 'w'))
@@ -28,15 +29,14 @@
assert x.startswith(result_prefix)
res = [x[len(result_prefix):] for x in res]
#print 'PKG_CONFIG:', option, res
- return res
- return []
+ resultlist[:] = res
def use_pkg_config():
- include_dirs .extend(_ask_pkg_config('--cflags-only-I', '-I'))
- extra_compile_args.extend(_ask_pkg_config('--cflags-only-other'))
- library_dirs .extend(_ask_pkg_config('--libs-only-L', '-L'))
- extra_link_args .extend(_ask_pkg_config('--libs-only-other'))
- libraries[:] = _ask_pkg_config('--libs-only-l', '-l') or libraries
+ _ask_pkg_config(include_dirs, '--cflags-only-I', '-I')
+ _ask_pkg_config(extra_compile_args, '--cflags-only-other')
+ _ask_pkg_config(library_dirs, '--libs-only-L', '-L')
+ _ask_pkg_config(extra_link_args, '--libs-only-other')
+ _ask_pkg_config(libraries, '--libs-only-l', '-l')
if sys.platform == 'win32':
@@ -49,8 +49,8 @@
"On Windows, you need to copy the directory "
"Modules\\_ctypes\\libffi_msvc from the CPython sources (2.6 or 2.7) "
"into the top-level directory.")
- include_dirs.append(COMPILE_LIBFFI)
- libraries.remove('ffi')
+ include_dirs[:] = [COMPILE_LIBFFI]
+ libraries[:] = []
_filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)]
_filenames = [filename for filename in _filenames
if filename.endswith('.c') or
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -550,57 +550,64 @@
assert len(a) == 5
assert ffi.sizeof(a) == 5 * SIZE_OF_INT
- def test_str_from_char_pointer(self):
+ def test_string_from_char_pointer(self):
ffi = FFI(backend=self.Backend())
- assert str(ffi.new("char*", "x")) == "x"
- assert str(ffi.new("char*", "\x00")) == ""
+ x = ffi.new("char*", "x")
+ assert str(x) == repr(x)
+ assert ffi.string(x) == "x"
+ assert ffi.string(ffi.new("char*", "\x00")) == ""
def test_unicode_from_wchar_pointer(self):
ffi = FFI(backend=self.Backend())
self.check_wchar_t(ffi)
- assert unicode(ffi.new("wchar_t*", u"x")) == u"x"
- assert unicode(ffi.new("wchar_t*", u"\x00")) == u""
- x = ffi.new("wchar_t*", u"\x00")
- assert str(x) == repr(x)
+ x = ffi.new("wchar_t*", u"x")
+ assert unicode(x) == unicode(repr(x))
+ assert ffi.string(x) == u"x"
+ assert ffi.string(ffi.new("wchar_t*", u"\x00")) == u""
def test_string_from_char_array(self):
ffi = FFI(backend=self.Backend())
- assert str(ffi.cast("char", "x")) == "x"
p = ffi.new("char[]", "hello.")
p[5] = '!'
- assert str(p) == "hello!"
+ assert ffi.string(p) == "hello!"
p[6] = '?'
- assert str(p) == "hello!?"
+ assert ffi.string(p) == "hello!?"
p[3] = '\x00'
- assert str(p) == "hel"
+ assert ffi.string(p) == "hel"
+ assert ffi.string(p, 2) == "he"
py.test.raises(IndexError, "p[7] = 'X'")
#
a = ffi.new("char[]", "hello\x00world")
assert len(a) == 12
p = ffi.cast("char *", a)
- assert str(p) == 'hello'
+ assert ffi.string(p) == 'hello'
def test_string_from_wchar_array(self):
ffi = FFI(backend=self.Backend())
self.check_wchar_t(ffi)
- assert unicode(ffi.cast("wchar_t", "x")) == u"x"
- assert unicode(ffi.cast("wchar_t", u"x")) == u"x"
+ assert ffi.string(ffi.cast("wchar_t", "x")) == u"x"
+ assert ffi.string(ffi.cast("wchar_t", u"x")) == u"x"
x = ffi.cast("wchar_t", "x")
assert str(x) == repr(x)
+ assert ffi.string(x) == u"x"
#
p = ffi.new("wchar_t[]", u"hello.")
p[5] = u'!'
- assert unicode(p) == u"hello!"
+ assert ffi.string(p) == u"hello!"
p[6] = unichr(1234)
- assert unicode(p) == u"hello!\u04d2"
+ assert ffi.string(p) == u"hello!\u04d2"
p[3] = u'\x00'
- assert unicode(p) == u"hel"
+ assert ffi.string(p) == u"hel"
+ assert ffi.string(p, 123) == u"hel"
py.test.raises(IndexError, "p[7] = u'X'")
#
a = ffi.new("wchar_t[]", u"hello\x00world")
assert len(a) == 12
p = ffi.cast("wchar_t *", a)
- assert unicode(p) == u'hello'
+ assert ffi.string(p) == u'hello'
+ assert ffi.string(p, 123) == u'hello'
+ assert ffi.string(p, 5) == u'hello'
+ assert ffi.string(p, 2) == u'he'
def test_fetch_const_char_p_field(self):
# 'const' is ignored so far
@@ -609,7 +616,7 @@
t = ffi.new("const char[]", "testing")
s = ffi.new("struct foo*", [t])
assert type(s.name) is not str
- assert str(s.name) == "testing"
+ assert ffi.string(s.name) == "testing"
py.test.raises(TypeError, "s.name = None")
s.name = ffi.NULL
assert s.name == ffi.NULL
@@ -622,7 +629,7 @@
t = ffi.new("const wchar_t[]", u"testing")
s = ffi.new("struct foo*", [t])
assert type(s.name) not in (str, unicode)
- assert unicode(s.name) == u"testing"
+ assert ffi.string(s.name) == u"testing"
s.name = ffi.NULL
assert s.name == ffi.NULL
@@ -802,6 +809,28 @@
res = a(1) # and the error reported to stderr
assert res == 42
+ def test_structptr_argument(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int a, b; };")
+ def cb(p):
+ return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b
+ a = ffi.callback("int(*)(struct foo_s[])", cb)
+ res = a([[5, 6], {'a': 7, 'b': 8}])
+ assert res == 5678
+ res = a([[5], {'b': 8}])
+ assert res == 5008
+
+ def test_array_argument_as_list(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int a, b; };")
+ seen = []
+ def cb(argv):
+ seen.append(ffi.string(argv[0]))
+ seen.append(ffi.string(argv[1]))
+ a = ffi.callback("void(*)(char *[])", cb)
+ a([ffi.new("char[]", "foobar"), ffi.new("char[]", "baz")])
+ assert seen == ["foobar", "baz"]
+
def test_cast_float(self):
ffi = FFI(backend=self.Backend())
a = ffi.cast("float", 12)
@@ -813,7 +842,7 @@
a = ffi.cast("int", 12.9)
assert int(a) == 12
a = ffi.cast("char", 66.9 + 256)
- assert str(a) == "B"
+ assert ffi.string(a) == "B"
#
a = ffi.cast("float", ffi.cast("int", 12))
assert float(a) == 12.0
@@ -824,7 +853,7 @@
a = ffi.cast("int", ffi.cast("double", 12.9))
assert int(a) == 12
a = ffi.cast("char", ffi.cast("double", 66.9 + 256))
- assert str(a) == "B"
+ assert ffi.string(a) == "B"
def test_enum(self):
ffi = FFI(backend=self.Backend())
@@ -867,12 +896,12 @@
assert int(ffi.cast("enum foo", "A")) == 0
assert int(ffi.cast("enum foo", "B")) == 42
assert int(ffi.cast("enum foo", "C")) == 43
- assert str(ffi.cast("enum foo", 0)) == "A"
- assert str(ffi.cast("enum foo", 42)) == "B"
- assert str(ffi.cast("enum foo", 43)) == "C"
+ assert ffi.string(ffi.cast("enum foo", 0)) == "A"
+ assert ffi.string(ffi.cast("enum foo", 42)) == "B"
+ assert ffi.string(ffi.cast("enum foo", 43)) == "C"
invalid_value = ffi.cast("enum foo", 2)
assert int(invalid_value) == 2
- assert str(invalid_value) == "#2"
+ assert ffi.string(invalid_value) == "#2"
def test_array_of_struct(self):
ffi = FFI(backend=self.Backend())
@@ -1199,4 +1228,4 @@
ffi = FFI(backend=self.Backend())
ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
e = ffi.cast("enum e", 'CC')
- assert str(e) == "AA" # pick the first one arbitrarily
+ assert ffi.string(e) == "AA" # pick the first one arbitrarily
diff --git a/testing/test_ctypes.py b/testing/test_ctypes.py
--- a/testing/test_ctypes.py
+++ b/testing/test_ctypes.py
@@ -13,3 +13,11 @@
def test_array_of_func_ptr(self):
py.test.skip("ctypes backend: not supported: "
"initializers for function pointers")
+
+ def test_structptr_argument(self):
+ py.test.skip("ctypes backend: not supported: passing a list "
+ "for a pointer argument")
+
+ def test_array_argument_as_list(self):
+ py.test.skip("ctypes backend: not supported: passing a list "
+ "for a pointer argument")
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -254,7 +254,7 @@
ffi.C = ffi.dlopen(None)
p = ffi.new("char[]", "hello world!")
q = ffi.C.strchr(p, ord('w'))
- assert str(q) == "world!"
+ assert ffi.string(q) == "world!"
def test_function_with_struct_argument(self):
if sys.platform == 'win32':
@@ -267,4 +267,4 @@
ffi.C = ffi.dlopen(None)
ina = ffi.new("struct in_addr *", [0x04040404])
a = ffi.C.inet_ntoa(ina[0])
- assert str(a) == '4.4.4.4'
+ assert ffi.string(a) == '4.4.4.4'
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -337,7 +337,7 @@
ffi.cdef("static char *const PP;")
lib = ffi.verify('static char *const PP = "testing!";\n')
assert ffi.typeof(lib.PP) == ffi.typeof("char *")
- assert str(lib.PP) == "testing!"
+ assert ffi.string(lib.PP) == "testing!"
def test_nonfull_enum():
ffi = FFI()
@@ -633,7 +633,7 @@
""")
foochar = ffi.cast("char *(*)(void)", lib.fooptr)
s = foochar()
- assert str(s) == "foobar"
+ assert ffi.string(s) == "foobar"
def test_funcptr_as_argument():
ffi = FFI()
More information about the pypy-commit
mailing list