[pypy-commit] cffi python3-port: hg merge default, and try to resolve all conflicts.
arigo
noreply at buildbot.pypy.org
Sun Aug 12 17:31:14 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: python3-port
Changeset: r812:db148db04570
Date: 2012-08-12 15:53 +0200
http://bitbucket.org/cffi/cffi/changeset/db148db04570/
Log: hg merge default, and try to resolve all conflicts. Not tested so
far.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -23,6 +23,7 @@
#endif
#if PY_MAJOR_VERSION >= 3
+# define STR_OR_BYTES "bytes"
# define PyText_Type PyUnicode_Type
# define PyText_Check PyUnicode_Check
# define PyText_FromFormat PyUnicode_FromFormat
@@ -32,6 +33,7 @@
# define PyText_FromStringAndSize PyUnicode_FromStringAndSize
# define PyText_InternInPlace PyUnicode_InternInPlace
#else
+# define STR_OR_BYTES "str"
# define PyText_Type PyString_Type
# define PyText_Check PyString_Check
# define PyText_FromFormat PyString_FromFormat
@@ -63,7 +65,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 */
@@ -78,6 +80,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 | \
@@ -99,7 +102,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 */
@@ -110,15 +114,19 @@
PyObject_HEAD
CTypeDescrObject *c_type;
char *c_data;
+ PyObject *c_weakreflist;
} CDataObject;
typedef struct cfieldobject_s {
PyObject_HEAD
CTypeDescrObject *cf_type;
Py_ssize_t cf_offset;
- short cf_bitshift, cf_bitsize;
+ short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
+ short cf_bitsize;
struct cfieldobject_s *cf_next;
} CFieldObject;
+#define BS_REGULAR (-1) /* a regular field, not with bitshift */
+#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */
static PyTypeObject CTypeDescr_Type;
static PyTypeObject CField_Type;
@@ -138,6 +146,7 @@
unsigned long long m_longlong;
float m_float;
double m_double;
+ long double m_longdouble;
} union_alignment;
typedef struct {
@@ -147,22 +156,17 @@
typedef struct {
CDataObject head;
- PyObject *weakreflist;
-} CDataObject_own_base;
-
-typedef struct {
- CDataObject_own_base head;
union_alignment alignment;
} CDataObject_own_nolength;
typedef struct {
- CDataObject_own_base head;
+ CDataObject head;
Py_ssize_t length;
union_alignment alignment;
} CDataObject_own_length;
typedef struct {
- CDataObject_own_base head;
+ CDataObject head;
PyObject *structobj;
} CDataObject_own_structptr;
@@ -553,6 +557,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)
{
@@ -564,6 +574,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)
{
@@ -573,15 +589,17 @@
Py_INCREF(ct);
cd->c_data = data;
cd->c_type = ct;
+ cd->c_weakreflist = NULL;
return (PyObject *)cd;
}
static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
{
PyObject *d_value;
-
- if (PyText_AsUTF8(ob)[0] == '#') {
- char *number = PyText_AsUTF8(ob) + 1; /* strip initial '#' */
+ char *p = PyText_AsUTF8(ob);
+
+ if (p[0] == '#') {
+ char *number = p + 1; /* strip initial '#' */
PyObject *ob2 = PyText_FromString(number);
if (ob2 == NULL)
return NULL;
@@ -602,6 +620,8 @@
return d_value;
}
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/
+
static PyObject *
convert_to_object(char *data, CTypeDescrObject *ct)
{
@@ -616,7 +636,16 @@
ct->ct_name);
return NULL;
}
- else if (ct->ct_flags & (CT_ARRAY|CT_STRUCT|CT_UNION)) {
+ else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+ return new_simple_cdata(data, ct);
+ }
+ else if (ct->ct_flags & CT_ARRAY) {
+ if (ct->ct_length < 0) {
+ /* we can't return a <cdata 'int[]'> here, because we don't
+ know the length to give it. As a compromize, returns
+ <cdata 'int *'> in this case. */
+ ct = (CTypeDescrObject *)ct->ct_stuff;
+ }
return new_simple_cdata(data, ct);
}
}
@@ -650,8 +679,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))
@@ -793,78 +831,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 (!PyBytes_Check(init)) {
+ expected = STR_OR_BYTES" 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 "STR_OR_BYTES" 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 (!PyBytes_Check(init)) {
- expected = "bytes or list or tuple";
- goto cannot_convert;
- }
- n = PyBytes_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 = PyBytes_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;
@@ -937,10 +988,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) {
@@ -1128,26 +1191,26 @@
static void cdata_dealloc(CDataObject *cd)
{
+ if (cd->c_weakreflist != NULL)
+ PyObject_ClearWeakRefs((PyObject *) cd);
+
Py_DECREF(cd->c_type);
PyObject_Del(cd);
}
-static void cdataowning_dealloc(CDataObject_own_base *cdb)
+static void cdataowning_dealloc(CDataObject *cd)
{
- if (cdb->weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *) cdb);
-
- if (cdb->head.c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
- Py_DECREF(((CDataObject_own_structptr *)cdb)->structobj);
+ if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+ Py_DECREF(((CDataObject_own_structptr *)cd)->structobj);
}
- else if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
+ else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {
/* a callback */
- ffi_closure *closure = (ffi_closure *)cdb->head.c_data;
+ ffi_closure *closure = (ffi_closure *)cd->c_data;
PyObject *args = (PyObject *)(closure->user_data);
Py_XDECREF(args);
cffi_closure_free(closure);
}
- cdata_dealloc(&cdb->head);
+ cdata_dealloc(cd);
}
static int cdata_traverse(CDataObject *cd, visitproc visit, void *arg)
@@ -1156,31 +1219,37 @@
return 0;
}
+static PyObject *cdata_float(CDataObject *cd); /*forward*/
+
static PyObject *cdata_repr(CDataObject *cd)
{
- char *p, *extra;
- PyObject *result, *s = NULL;
+ char *extra;
+ PyObject *result, *s;
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 = PyText_AsUTF8(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);
+ }
+ else {
+ long double lvalue = read_raw_longdouble_data(cd->c_data);
+ char buffer[128]; /* big enough */
+ sprintf(buffer, "%LE", lvalue);
+ s = PyText_FromString(buffer);
+ }
}
else {
if (cd->c_data != NULL) {
s = PyText_FromFormat("%p", cd->c_data);
- if (s == NULL)
- return NULL;
- p = PyText_AsUTF8(s);
}
else
- p = "NULL";
+ s = PyText_FromString("NULL");
}
+ if (s == NULL)
+ return NULL;
/* it's slightly confusing to get "<cdata 'struct foo' 0x...>" because the
struct foo is not owned. Trying to make it clearer, write in this
case "<cdata 'struct foo &' 0x...>". */
@@ -1188,95 +1257,12 @@
extra = " &";
else
extra = "";
- result = PyText_FromFormat("<cdata '%s%s' %s>",
- cd->c_type->ct_name, extra, p);
- Py_XDECREF(s);
+ result = PyText_FromFormat("<cdata '%s%s' %S>",
+ cd->c_type->ct_name, extra, s);
+ Py_DECREF(s);
return result;
}
-static PyObject *cdata_get_value(CDataObject *cd)
-{
- if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR &&
- cd->c_type->ct_size == sizeof(char)) {
- return PyBytes_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", s);
- Py_DECREF(s);
- }
- return NULL;
- }
- length = strlen(cd->c_data);
- }
-
- return PyBytes_FromStringAndSize(cd->c_data, length);
- }
-#ifdef HAVE_WCHAR_H
- else 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", s);
- Py_DECREF(s);
- }
- return NULL;
- }
- length = 0;
- while (start[length])
- length++;
- }
-
- return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length);
- }
-#endif
- else {
- Py_INCREF(cd);
- return (PyObject *)cd;
- }
-}
-
-static PyObject *cdata_str(CDataObject *cd)
-{
- 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);
-}
-
static PyObject *cdataowning_repr(CDataObject *cd)
{
Py_ssize_t size;
@@ -1336,7 +1322,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);
#if PY_MAJOR_VERSION < 3
PyObject *r = o ? PyNumber_Int(o) : NULL;
#else
@@ -1366,7 +1352,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);
@@ -1607,10 +1600,13 @@
if (cf != NULL) {
/* read the field 'cf' */
char *data = cd->c_data + cf->cf_offset;
- if (cf->cf_bitshift >= 0)
+ if (cf->cf_bitshift == BS_REGULAR)
+ return convert_to_object(data, cf->cf_type);
+ else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
+ return new_simple_cdata(data,
+ (CTypeDescrObject *)cf->cf_type->ct_stuff);
+ else
return convert_to_object_bitfield(data, cf);
- else
- return convert_to_object(data, cf->cf_type);
}
}
return PyObject_GenericGetAttr((PyObject *)cd, attr);
@@ -1665,14 +1661,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;
@@ -1702,7 +1756,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 {
@@ -1778,37 +1835,44 @@
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 (PyText_Check(obj)) {
- /* special case: Python string -> cdata 'char *' */
- *(char **)data = PyText_AsUTF8(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] = PyBytes_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;
- }
+ ((char **)data)[1] = NULL;
+ }
+ if (convert_from_object(data, argtype, obj) < 0) {
+ if (CData_Check(obj) && (argtype->ct_flags & CT_IS_PTR_TO_OWNED) &&
+ argtype->ct_itemdescr == ((CDataObject *)obj)->c_type) {
+ /* special case to make the life of verifier.py easier:
+ if the formal argument type is 'struct foo *' but
+ we pass a 'struct foo', then get a pointer to it */
+ PyErr_Clear();
+ ((char **)data)[0] = ((CDataObject *)obj)->c_data;
+ continue;
}
-#endif
+ goto error;
}
- if (convert_from_object(data, argtype, obj) < 0)
- goto error;
}
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)) {
@@ -1831,23 +1895,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 *);
@@ -1894,15 +1961,6 @@
(objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
};
-static PyMethodDef CData_methods[] = {
- {NULL, NULL} /* sentinel */
-};
-
-static PyGetSetDef CData_getset[] = {
- {"value", (getter)cdata_get_value, NULL, NULL},
- {0}
-};
-
static PyTypeObject CData_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_cffi_backend.CData",
@@ -1919,7 +1977,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 */
@@ -1928,18 +1986,15 @@
(traverseproc)cdata_traverse, /* tp_traverse */
0, /* tp_clear */
cdata_richcompare, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
+ offsetof(CDataObject, c_weakreflist), /* tp_weaklistoffset */
(getiterfunc)cdata_iter, /* tp_iter */
0, /* tp_iternext */
- CData_methods, /* tp_methods */
- 0, /* tp_members */
- CData_getset, /* tp_getset */
};
static PyTypeObject CDataOwning_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_cffi_backend.CDataOwn",
- sizeof(CDataObject_own_base),
+ sizeof(CDataObject),
0,
(destructor)cdataowning_dealloc, /* tp_dealloc */
0, /* tp_print */
@@ -1961,7 +2016,7 @@
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
- offsetof(CDataObject_own_base, weakreflist),/* tp_weaklistoffset */
+ 0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
@@ -2053,24 +2108,24 @@
/************************************************************/
-static CDataObject_own_base *allocate_owning_object(Py_ssize_t size,
- CTypeDescrObject *ct)
+static CDataObject *allocate_owning_object(Py_ssize_t size,
+ CTypeDescrObject *ct)
{
- CDataObject_own_base *cdb;
- cdb = (CDataObject_own_base *)PyObject_Malloc(size);
- if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+ CDataObject *cd;
+ cd = (CDataObject *)PyObject_Malloc(size);
+ if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL)
return NULL;
Py_INCREF(ct);
- cdb->head.c_type = ct;
- cdb->weakreflist = NULL;
- return cdb;
+ cd->c_type = ct;
+ cd->c_weakreflist = NULL;
+ return cd;
}
static PyObject *
convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
{
- CDataObject_own_base *cdb;
+ CDataObject *cd;
Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
Py_ssize_t datasize = ct->ct_size;
@@ -2079,20 +2134,19 @@
"return type is not a struct or is opaque");
return NULL;
}
- cdb = allocate_owning_object(dataoffset + datasize, ct);
- if (cdb == NULL)
+ cd = allocate_owning_object(dataoffset + datasize, ct);
+ if (cd == NULL)
return NULL;
- cdb->head.c_data = ((char *)cdb) + dataoffset;
-
- memcpy(cdb->head.c_data, data, datasize);
- return (PyObject *)cdb;
+ cd->c_data = ((char *)cd) + dataoffset;
+
+ memcpy(cd->c_data, data, datasize);
+ return (PyObject *)cd;
}
static PyObject *b_newp(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct, *ctitem;
CDataObject *cd;
- CDataObject_own_base *cdb;
PyObject *init = Py_None;
Py_ssize_t dataoffset, datasize, explicitlength;
if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init))
@@ -2160,33 +2214,31 @@
we build two objects instead of one, with the memory-owning
one being really the struct (or union) and the returned one
having a strong reference to it */
- CDataObject_own_base *cdp;
-
- cdb = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
- if (cdb == NULL)
+ CDataObject *cds;
+
+ cds = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
+ if (cds == NULL)
return NULL;
- cdp = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
- if (cdp == NULL) {
- Py_DECREF(cdb);
+ cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
+ if (cd == NULL) {
+ Py_DECREF(cds);
return NULL;
}
- /* store the only reference to cdb into cdp */
- ((CDataObject_own_structptr *)cdp)->structobj = (PyObject *)cdb;
+ /* store the only reference to cds into cd */
+ ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds;
assert(explicitlength < 0);
- cdb->head.c_data = cdp->head.c_data = ((char *)cdb) + dataoffset;
- cd = &cdp->head;
+ cds->c_data = cd->c_data = ((char *)cds) + dataoffset;
}
else {
- cdb = allocate_owning_object(dataoffset + datasize, ct);
- if (cdb == NULL)
+ cd = allocate_owning_object(dataoffset + datasize, ct);
+ if (cd == NULL)
return NULL;
- cdb->head.c_data = ((char *)cdb) + dataoffset;
+ cd->c_data = ((char *)cd) + dataoffset;
if (explicitlength >= 0)
- ((CDataObject_own_length*)cdb)->length = explicitlength;
- cd = &cdb->head;
+ ((CDataObject_own_length*)cd)->length = explicitlength;
}
memset(cd->c_data, 0, datasize);
@@ -2209,6 +2261,7 @@
Py_INCREF(ct);
cd->c_type = ct;
cd->c_data = ((char*)cd) + dataoffset;
+ cd->c_weakreflist = NULL;
return cd;
}
@@ -2337,6 +2390,16 @@
}
value = (unsigned char)PyBytes_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);
}
@@ -2345,8 +2408,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 {
@@ -2500,19 +2567,26 @@
static PyObject *b_load_library(PyObject *self, PyObject *args)
{
- char *filename;
+ char *filename_or_null, *printable_filename;
void *handle;
DynLibObject *dlobj;
int is_global = 0;
- if (!PyArg_ParseTuple(args, "et|i:load_library",
- Py_FileSystemDefaultEncoding, &filename,
+ if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
+ filename_or_null = NULL;
+ is_global = 1;
+ }
+ else if (!PyArg_ParseTuple(args, "et|i:load_library",
+ Py_FileSystemDefaultEncoding, &filename_or_null,
&is_global))
return NULL;
- handle = dlopen(filename, RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL));
+ printable_filename = filename_or_null ? filename_or_null : "<None>";
+ handle = dlopen(filename_or_null,
+ RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL));
if (handle == NULL) {
- PyErr_Format(PyExc_OSError, "cannot load library: %s", filename);
+ PyErr_Format(PyExc_OSError, "cannot load library: %s",
+ printable_filename);
return NULL;
}
@@ -2522,7 +2596,7 @@
return NULL;
}
dlobj->dl_handle = handle;
- dlobj->dl_name = strdup(filename);
+ dlobj->dl_name = strdup(printable_filename);
return (PyObject *)dlobj;
}
@@ -2588,7 +2662,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 )
@@ -2654,6 +2729,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;
}
@@ -2713,6 +2790,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;
@@ -2908,7 +2986,10 @@
if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size &&
!(ftype->ct_flags & CT_PRIMITIVE_CHAR))) {
fbitsize = -1;
- bitshift = -1;
+ if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0)
+ bitshift = BS_EMPTY_ARRAY;
+ else
+ bitshift = BS_REGULAR;
prev_bit_position = 0;
}
else {
@@ -3225,6 +3306,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 *);
+ }
}
}
@@ -3505,6 +3595,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;
@@ -3539,6 +3632,9 @@
Py_XDECREF(py_args);
Py_XDECREF(py_res);
Py_DECREF(cb_args);
+#ifdef WITH_THREAD
+ PyGILState_Release(state);
+#endif
restore_errno();
return;
@@ -3558,7 +3654,7 @@
static PyObject *b_callback(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct, *ctresult;
- CDataObject_own_base *cdb;
+ CDataObject *cd;
PyObject *ob, *error_ob = Py_None;
PyObject *py_rawerr, *infotuple = NULL;
cif_description_t *cif_descr;
@@ -3602,13 +3698,13 @@
closure = cffi_closure_alloc();
- cdb = PyObject_New(CDataObject_own_base, &CDataOwning_Type);
- if (cdb == NULL)
+ cd = PyObject_New(CDataObject, &CDataOwning_Type);
+ if (cd == NULL)
goto error;
Py_INCREF(ct);
- cdb->head.c_type = ct;
- cdb->head.c_data = (char *)closure;
- cdb->weakreflist = NULL;
+ cd->c_type = ct;
+ cd->c_data = (char *)closure;
+ cd->c_weakreflist = NULL;
cif_descr = (cif_description_t *)ct->ct_extra;
if (cif_descr == NULL) {
@@ -3623,14 +3719,14 @@
goto error;
}
assert(closure->user_data == infotuple);
- return (PyObject *)cdb;
+ return (PyObject *)cd;
error:
closure->user_data = NULL;
- if (cdb == NULL)
+ if (cd == NULL)
cffi_closure_free(closure);
else
- Py_DECREF(cdb);
+ Py_DECREF(cd);
Py_XDECREF(infotuple);
return NULL;
}
@@ -3812,6 +3908,84 @@
return PyText_FromStringAndSize(s, namelen + replacelen);
}
+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;
@@ -3996,6 +4170,24 @@
return result;
}
+static int _testfunc18(struct _testfunc17_s *ptr)
+{
+ return ptr->a1 + (int)ptr->a2;
+}
+
+static long double _testfunc19(long double x)
+{
+ int i;
+ for (i=0; i<28; i++)
+ x += x;
+ return x;
+}
+
+static short _testfunc20(struct _testfunc7_s *ptr)
+{
+ return ptr->a1 + ptr->a2;
+}
+
static PyObject *b__testfunc(PyObject *self, PyObject *args)
{
/* for testing only */
@@ -4022,6 +4214,9 @@
case 15: f = &_testfunc15; break;
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;
@@ -4050,6 +4245,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},
@@ -4209,12 +4405,13 @@
};
#define INITERROR return NULL
-PyObject *
+PyMODINIT_FUNC
PyInit__cffi_backend(void)
#else
#define INITERROR return
-void init_cffi_backend(void)
+PyMODINIT_FUNC
+init_cffi_backend(void)
#endif
{
PyObject *m, *v;
@@ -4253,7 +4450,7 @@
if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
INITERROR;
- v = PyText_FromString("0.2.1");
+ v = PyText_FromString("0.3");
if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
INITERROR;
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
@@ -26,7 +26,10 @@
def find_and_load_library(name, is_global=0):
import ctypes.util
- path = ctypes.util.find_library(name)
+ if name is None:
+ path = None
+ else:
+ path = ctypes.util.find_library(name)
return load_library(path, is_global)
def test_load_library():
@@ -92,6 +95,7 @@
def test_no_float_on_int_types():
p = new_primitive_type('long')
py.test.raises(TypeError, float, cast(p, 42))
+ py.test.raises(TypeError, complex, cast(p, 42))
def test_float_types():
INF = 1E200 * 1E200
@@ -122,6 +126,39 @@
assert float(cast(p, True)) == 1.0
py.test.raises(TypeError, cast, p, None)
+def test_complex_types():
+ py.test.skip("later")
+ INF = 1E200 * 1E200
+ for name in ["float", "double"]:
+ p = new_primitive_type("_Complex " + name)
+ assert bool(cast(p, 0))
+ assert bool(cast(p, INF))
+ assert bool(cast(p, -INF))
+ assert bool(cast(p, 0j))
+ assert bool(cast(p, INF*1j))
+ assert bool(cast(p, -INF*1j))
+ py.test.raises(TypeError, int, cast(p, -150))
+ py.test.raises(TypeError, long, cast(p, -150))
+ py.test.raises(TypeError, float, cast(p, -150))
+ assert complex(cast(p, 1.25)) == 1.25
+ assert complex(cast(p, 1.25j)) == 1.25j
+ assert float(cast(p, INF*1j)) == INF*1j
+ assert float(cast(p, -INF)) == -INF
+ if name == "float":
+ assert complex(cast(p, 1.1j)) != 1.1j # rounding error
+ assert complex(cast(p, 1E200+3j)) == INF+3j # limited range
+ assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
+
+ assert cast(p, -1.1j) != cast(p, -1.1j)
+ assert repr(complex(cast(p, -0.0)).real) == '-0.0'
+ assert repr(complex(cast(p, -0j))) == '-0j'
+ assert complex(cast(p, '\x09')) == 9.0
+ assert complex(cast(p, True)) == 1.0
+ py.test.raises(TypeError, cast, p, None)
+ #
+ py.test.raises(cast, new_primitive_type(name), 1+2j)
+ py.test.raises(cast, new_primitive_type("int"), 1+2j)
+
def test_character_type():
p = new_primitive_type("char")
assert bool(cast(p, '\x00'))
@@ -130,7 +167,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 +272,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():
@@ -261,6 +300,16 @@
p = newp(BIntPtrPtr, q)
assert p[0][0] == 43
+def test_load_standard_library():
+ if sys.platform == "win32":
+ py.test.raises(OSError, find_and_load_library, None)
+ return
+ x = find_and_load_library(None)
+ BVoidP = new_pointer_type(new_void_type())
+ assert x.load_function(BVoidP, 'strcpy')
+ py.test.raises(KeyError, x.load_function,
+ BVoidP, 'xxx_this_function_does_not_exist')
+
def test_hash_differences():
BChar = new_primitive_type("char")
BInt = new_primitive_type("int")
@@ -295,6 +344,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 +372,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 +513,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 +815,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")
@@ -786,6 +848,22 @@
res = f(x[0])
assert res == -4042 + ord('A')
+def test_call_function_20():
+ BChar = new_primitive_type("char")
+ BShort = new_primitive_type("short")
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BChar, -1),
+ ('a2', BShort, -1)])
+ BFunc18 = new_function_type((BStructPtr,), BShort, False)
+ f = cast(BFunc18, _testfunc(20))
+ x = newp(BStructPtr, {'a1': 'A', 'a2': -4042})
+ # test the exception that allows us to pass a 'struct foo' where the
+ # function really expects a 'struct foo *'.
+ res = f(x[0])
+ assert res == -4042 + ord('A')
+ assert res == f(x)
+
def test_call_function_9():
BInt = new_primitive_type("int")
BFunc9 = new_function_type((BInt,), BInt, True) # vararg
@@ -949,14 +1027,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'>"
@@ -1082,8 +1160,8 @@
BPtr = new_pointer_type(BInt)
weakref.ref(BInt)
weakref.ref(newp(BPtr, 42))
- py.test.raises(TypeError, weakref.ref, cast(BPtr, 42))
- py.test.raises(TypeError, weakref.ref, cast(BInt, 42))
+ weakref.ref(cast(BPtr, 42))
+ weakref.ref(cast(BInt, 42))
def test_no_inheritance():
BInt = new_primitive_type("int")
@@ -1106,11 +1184,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)
@@ -1203,16 +1282,56 @@
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'
+ if 'PY_DOT_PY' not in globals():
+ assert string(a, 8).startswith('ABC') # may contain additional garbage
+
+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'
+ if 'PY_DOT_PY' not in globals():
+ assert string(a, 8).startswith(u'ABC') # may contain additional garbage
+
+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")
@@ -1229,12 +1348,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())
@@ -1332,6 +1451,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())
@@ -1356,7 +1483,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
#
@@ -1387,20 +1514,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
@@ -1411,17 +1538,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>"
@@ -1429,7 +1556,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>"
@@ -1439,34 +1566,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
@@ -1578,12 +1704,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")
@@ -1689,3 +1815,89 @@
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")
+
+def test_get_array_of_length_zero():
+ for length in [0, 5, 10]:
+ BLong = new_primitive_type("long")
+ BLongP = new_pointer_type(BLong)
+ BArray0 = new_array_type(BLongP, length)
+ BStruct = new_struct_type("foo")
+ BStructPtr = new_pointer_type(BStruct)
+ complete_struct_or_union(BStruct, [('a1', BArray0, -1)])
+ p = newp(BStructPtr, None)
+ if length == 0:
+ assert repr(p.a1).startswith("<cdata 'long *' 0x")
+ else:
+ assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length)
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
@@ -216,6 +234,18 @@
replace_with = ' ' + replace_with
return self._backend.getcname(cdecl, replace_with)
+ def gc(self, 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.
+ """
+ try:
+ gc_weakrefs = self.gc_weakrefs
+ except AttributeError:
+ from .gc_weakref import GcWeakrefs
+ gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self)
+ return gc_weakrefs.build(cdata, destructor)
+
def _get_cached_btype(self, type):
try:
BType = self._cached_btypes[type]
@@ -259,41 +289,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,8 +1,8 @@
-import ctypes, ctypes.util, operator
+import ctypes, ctypes.util, operator, sys
from . import model
import sys
-if sys.version < '3':
+if sys.version_info < (3,):
integer_types = (int, long)
bytes = str
else:
@@ -10,7 +10,7 @@
xrange = range
class CTypesData(object):
- __slots__ = []
+ __slots__ = ['__weakref__']
def __init__(self, *args):
raise TypeError("cannot instantiate %r" % (self.__class__,))
@@ -125,6 +125,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__ = []
@@ -326,7 +329,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):
@@ -357,7 +363,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)
@@ -374,9 +380,6 @@
return cls(source)
def __int__(self):
return ord(self._value)
- @property
- def value(self):
- return self._value
if kind == 'float':
@classmethod
@@ -399,7 +402,7 @@
_cast_to_integer = __int__
- if kind == 'int':
+ if kind == 'int' or kind == 'byte':
@staticmethod
def _to_ctypes(x):
if not isinstance(x, integer_types):
@@ -423,7 +426,7 @@
return x
if isinstance(x, CTypesPrimitive): # <CData <char>>
return x._value
- if sys.version >= '3' and isinstance(x, int):
+ if sys.version_info >= (3,) and isinstance(x, int):
return x
raise TypeError("character expected, got %s" %
type(x).__name__)
@@ -443,13 +446,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'
#
@@ -498,13 +512,6 @@
self._as_ctype_ptr[index] = BItem._to_ctypes(value)
if kind == 'charp':
- @property
- def value(self):
- n = 0
- while self._as_ctype_ptr[n] != b'\x00':
- n += 1
- chars = [self._as_ctype_ptr[i] for i in range(n)]
- return b''.join(chars)
@classmethod
def _arg_to_ctypes(cls, value):
if isinstance(value, bytes):
@@ -512,6 +519,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' % (
@@ -531,8 +549,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'
#
@@ -543,6 +565,8 @@
else:
__slots__.append('_ctype')
_reftypename = BItem._get_c_name(brackets)
+ _declared_length = length
+ _CTPtr = CTypesPtr
def __init__(self, init):
if length is None:
@@ -584,20 +608,21 @@
raise IndexError
self._blob[index] = BItem._to_ctypes(value)
- if kind == 'char':
- @property
- def value(self):
- s = b''.join(self._blob)
- try:
- s = s[:s.index(b'\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):
return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
- return super(CTypesPtr, self)._get_own_repr()
+ return super(CTypesArray, self)._get_own_repr()
def _convert_to_address(self, BClass):
if BClass in (CTypesPtr, None) or BClass._automatic_casts:
@@ -622,6 +647,11 @@
other * ctypes.sizeof(BItem._ctype))
else:
return NotImplemented
+
+ @classmethod
+ def _cast_from(cls, source):
+ raise NotImplementedError("casting to %r" % (
+ cls._get_c_name(),))
#
CTypesArray._fix_class()
return CTypesArray
@@ -647,7 +677,12 @@
def new_union_type(self, name):
return self._new_struct_or_union('union', name, ctypes.Union)
- def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp):
+ def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
+ totalsize=-1, totalalignment=-1):
+ if totalsize >= 0 or totalalignment >= 0:
+ raise NotImplementedError("the ctypes backend of CFFI does not support "
+ "structures completed by verify(); please "
+ "compile and install the _cffi_backend module.")
struct_or_union = CTypesStructOrUnion._ctype
fnames = [fname for (fname, BField, bitsize) in fields]
btypes = [BField for (fname, BField, bitsize) in fields]
@@ -708,6 +743,17 @@
return BField._from_ctypes(p.contents)
def setter(self, value, fname=fname, BField=BField):
setattr(self._blob, fname, BField._to_ctypes(value))
+ #
+ if issubclass(BField, CTypesGenericArray):
+ setter = None
+ if BField._declared_length == 0:
+ def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
+ offset=CTypesStructOrUnion._offsetof(fname),
+ PTR=ctypes.POINTER(BField._ctype)):
+ addr = ctypes.addressof(self._blob)
+ p = ctypes.cast(addr + offset, PTR)
+ return BFieldPtr._from_ctypes(p)
+ #
else:
def getter(self, fname=fname, BField=BField):
return BField._from_ctypes(getattr(self._blob, fname))
@@ -859,7 +905,7 @@
__slots__ = []
_reftypename = 'enum %s &' % name
- def __str__(self):
+ def _to_string(self, maxlen):
return str(CTypesEnum._from_ctypes(self._value))
@classmethod
@@ -889,8 +935,11 @@
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):
- if sys.version >= '3':
+ if sys.version_info >= (3,):
# buf = bptr._as_ctype_ptr
# return memoryview(buf.contents)
if isinstance(bptr, CTypesGenericPtr):
@@ -972,7 +1021,10 @@
return funcobj
def read_variable(self, BType, name):
- ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+ try:
+ ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+ except AttributeError, e:
+ raise NotImplementedError(e)
return BType._from_ctypes(ctypes_obj)
def write_variable(self, BType, name, value):
diff --git a/cffi/gc_weakref.py b/cffi/gc_weakref.py
new file mode 100644
--- /dev/null
+++ b/cffi/gc_weakref.py
@@ -0,0 +1,19 @@
+from weakref import ref
+
+
+class GcWeakrefs(object):
+ # code copied and adapted from WeakKeyDictionary.
+
+ def __init__(self, ffi):
+ self.ffi = ffi
+ self.data = data = {}
+ def remove(k):
+ destructor, cdata = data.pop(k)
+ destructor(cdata)
+ self.remove = remove
+
+ def build(self, cdata, destructor):
+ # make a new cdata of the same type as the original one
+ new_cdata = self.ffi.cast(self.ffi.typeof(cdata), cdata)
+ self.data[ref(new_cdata, self.remove)] = destructor, cdata
+ return new_cdata
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
new file mode 100644
--- /dev/null
+++ b/cffi/vengine_cpy.py
@@ -0,0 +1,776 @@
+import imp
+from . import model, ffiplatform
+
+
+class VCPythonEngine(object):
+ _class_key = 'x'
+ _gen_python_module = True
+
+ def __init__(self, verifier):
+ self.verifier = verifier
+ self.ffi = verifier.ffi
+
+ def patch_extension_kwds(self, kwds):
+ pass
+
+ def collect_types(self):
+ self._typesdict = {}
+ self._generate("collecttype")
+
+ def _prnt(self, what=''):
+ self._f.write(what + '\n')
+
+ def _gettypenum(self, type):
+ # a KeyError here is a bug. please report it! :-)
+ return self._typesdict[type]
+
+ def _do_collect_type(self, tp):
+ if (not isinstance(tp, model.PrimitiveType) and
+ tp not in self._typesdict):
+ num = len(self._typesdict)
+ self._typesdict[tp] = num
+
+ def write_source_to_f(self):
+ self.collect_types()
+ #
+ # The new module will have a _cffi_setup() function that receives
+ # objects from the ffi world, and that calls some setup code in
+ # the module. This setup code is split in several independent
+ # functions, e.g. one per constant. The functions are "chained"
+ # by ending in a tail call to each other.
+ #
+ # This is further split in two chained lists, depending on if we
+ # can do it at import-time or if we must wait for _cffi_setup() to
+ # provide us with the <ctype> objects. This is needed because we
+ # need the values of the enum constants in order to build the
+ # <ctype 'enum'> that we may have to pass to _cffi_setup().
+ #
+ # 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']
+ #
+ prnt = self._prnt
+ # first paste some standard set of lines that are mostly '#define'
+ prnt(cffimod_header)
+ prnt()
+ # then paste the C source given by the user, verbatim.
+ prnt(self.verifier.preamble)
+ prnt()
+ #
+ # call generate_cpy_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
+ self._generate("decl")
+ #
+ # implement the function _cffi_setup_custom() as calling the
+ # head of the chained list.
+ self._generate_setup_custom()
+ prnt()
+ #
+ # produce the method table, including the entries for the
+ # generated Python->C function wrappers, which are done
+ # by generate_cpy_function_method().
+ prnt('static PyMethodDef _cffi_methods[] = {')
+ self._generate("method")
+ prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
+ prnt(' {NULL, NULL} /* Sentinel */')
+ prnt('};')
+ prnt()
+ #
+ # standard init.
+ modname = self.verifier.get_module_name()
+ if sys.version_info >= (3,):
+ prnt('static struct PyModuleDef _cffi_module_def = {')
+ prnt(' PyModuleDef_HEAD_INIT,')
+ prnt(' "%s",' % modname)
+ prnt(' NULL,')
+ prnt(' -1,')
+ prnt(' _cffi_methods,')
+ prnt(' NULL, NULL, NULL, NULL')
+ prnt('};')
+ prnt()
+ initname = 'PyInit_%s' % modname
+ createmod = 'PyModule_Create(&_cffi_module_def)'
+ errorcase = 'return NULL'
+ finalreturn = 'return lib'
+ else:
+ initname = 'init%s' % modname
+ createmod = 'Py_InitModule("%s", _cffi_methods)' % modname
+ errorcase = 'return'
+ finalreturn = 'return'
+ prnt('PyMODINIT_FUNC')
+ prnt('%s(void)' % initname)
+ prnt('{')
+ prnt(' PyObject *lib;')
+ prnt(' lib = %s;' % createmod)
+ prnt(' if (lib == NULL || %s < 0)' % (
+ self._chained_list_constants[False],))
+ prnt(' %s;' % errorcase)
+ prnt(' _cffi_init();')
+ prnt(' %s;' % finalreturn)
+ prnt('}')
+
+ def load_library(self):
+ # XXX review all usages of 'self' here!
+ # import it as a new extension module
+ try:
+ 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)
+ #
+ # call loading_cpy_struct() to get the struct layout inferred by
+ # the C compiler
+ self._load(module, 'loading')
+ #
+ # the C code will need the <ctype> objects. Collect them in
+ # order in a list.
+ revmapping = dict([(value, key)
+ for (key, value) in self._typesdict.items()])
+ lst = [revmapping[i] for i in range(len(revmapping))]
+ lst = list(map(self.ffi._get_cached_btype, lst))
+ #
+ # build the FFILibrary class and instance and call _cffi_setup().
+ # this will set up some fields like '_cffi_types', and only then
+ # it will invoke the chained list of functions that will really
+ # build (notably) the constant objects, as <cdata> if they are
+ # pointers, and store them as attributes on the 'library' object.
+ class FFILibrary(object):
+ _cffi_python_module = module
+ library = FFILibrary()
+ module._cffi_setup(lst, ffiplatform.VerificationError, library)
+ #
+ # finally, call the loaded_cpy_xxx() functions. This will perform
+ # the final adjustments, like copying the Python->C wrapper
+ # functions from the module to the 'library' object, and setting
+ # up the FFILibrary class with properties for the global C variables.
+ self._load(module, 'loaded', library=library)
+ return library
+
+ def _generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.items():
+ kind, realname = name.split(' ', 1)
+ try:
+ method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+ step_name))
+ except AttributeError:
+ raise ffiplatform.VerificationError(
+ "not implemented in verify(): %r" % name)
+ method(tp, realname)
+
+ def _load(self, module, step_name, **kwds):
+ for name, tp in self.ffi._parser._declarations.items():
+ kind, realname = name.split(' ', 1)
+ method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
+ method(tp, realname, module, **kwds)
+
+ def _generate_nothing(self, tp, name):
+ pass
+
+ def _loaded_noop(self, tp, name, module, **kwds):
+ pass
+
+ # ----------
+
+ def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+ extraarg = ''
+ if isinstance(tp, model.PrimitiveType):
+ converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),)
+ errvalue = '-1'
+ #
+ elif isinstance(tp, model.PointerType):
+ if (isinstance(tp.totype, model.PrimitiveType) and
+ tp.totype.name == 'char'):
+ converter = '_cffi_to_c_char_p'
+ else:
+ converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+ extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+ errvalue = 'NULL'
+ #
+ elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
+ # a struct (not a struct pointer) as a function argument
+ self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
+ % (tovar, self._gettypenum(tp), fromvar))
+ self._prnt(' %s;' % errcode)
+ return
+ #
+ elif isinstance(tp, model.FunctionPtrType):
+ converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+ extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+ errvalue = 'NULL'
+ #
+ else:
+ raise NotImplementedError(tp)
+ #
+ self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
+ self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % (
+ tovar, tp.get_c_name(''), errvalue))
+ self._prnt(' %s;' % errcode)
+
+ def _convert_expr_from_c(self, tp, var):
+ if isinstance(tp, model.PrimitiveType):
+ return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
+ elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
+ return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+ var, self._gettypenum(tp))
+ elif isinstance(tp, model.ArrayType):
+ return '_cffi_from_c_deref((char *)%s, _cffi_type(%d))' % (
+ var, self._gettypenum(tp))
+ elif isinstance(tp, model.StructType):
+ return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+ var, self._gettypenum(tp))
+ elif isinstance(tp, model.EnumType):
+ return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+ var, self._gettypenum(tp))
+ else:
+ raise NotImplementedError(tp)
+
+ # ----------
+ # typedefs: generates no code so far
+
+ _generate_cpy_typedef_collecttype = _generate_nothing
+ _generate_cpy_typedef_decl = _generate_nothing
+ _generate_cpy_typedef_method = _generate_nothing
+ _loading_cpy_typedef = _loaded_noop
+ _loaded_cpy_typedef = _loaded_noop
+
+ # ----------
+ # function declarations
+
+ def _generate_cpy_function_collecttype(self, tp, name):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ self._do_collect_type(tp)
+ else:
+ for type in tp.args:
+ self._do_collect_type(type)
+ self._do_collect_type(tp.result)
+
+ def _generate_cpy_function_decl(self, tp, name):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ # cannot support vararg functions better than this: check for its
+ # exact type (including the fixed arguments), and build it as a
+ # constant function pointer (no CPython wrapper)
+ self._generate_cpy_const(False, name, tp)
+ return
+ prnt = self._prnt
+ numargs = len(tp.args)
+ if numargs == 0:
+ argname = 'no_arg'
+ elif numargs == 1:
+ argname = 'arg0'
+ else:
+ argname = 'args'
+ prnt('static PyObject *')
+ prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+ prnt('{')
+ #
+ for i, type in enumerate(tp.args):
+ prnt(' %s;' % type.get_c_name(' x%d' % i))
+ if not isinstance(tp.result, model.VoidType):
+ result_code = 'result = '
+ prnt(' %s;' % tp.result.get_c_name(' result'))
+ else:
+ result_code = ''
+ #
+ if len(tp.args) > 1:
+ rng = range(len(tp.args))
+ for i in rng:
+ prnt(' PyObject *arg%d;' % i)
+ prnt()
+ prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
+ 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+ prnt(' return NULL;')
+ prnt()
+ #
+ for i, type in enumerate(tp.args):
+ self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+ 'return NULL')
+ prnt()
+ #
+ prnt(' Py_BEGIN_ALLOW_THREADS')
+ prnt(' _cffi_restore_errno();')
+ prnt(' { %s%s(%s); }' % (
+ result_code, name,
+ ', '.join(['x%d' % i for i in range(len(tp.args))])))
+ prnt(' _cffi_save_errno();')
+ prnt(' Py_END_ALLOW_THREADS')
+ prnt()
+ #
+ if result_code:
+ prnt(' return %s;' %
+ self._convert_expr_from_c(tp.result, 'result'))
+ else:
+ prnt(' Py_INCREF(Py_None);')
+ prnt(' return Py_None;')
+ prnt('}')
+ prnt()
+
+ def _generate_cpy_function_method(self, tp, name):
+ if tp.ellipsis:
+ return
+ numargs = len(tp.args)
+ if numargs == 0:
+ meth = 'METH_NOARGS'
+ elif numargs == 1:
+ meth = 'METH_O'
+ else:
+ meth = 'METH_VARARGS'
+ self._prnt(' {"%s", _cffi_f_%s, %s},' % (name, name, meth))
+
+ _loading_cpy_function = _loaded_noop
+
+ def _loaded_cpy_function(self, tp, name, module, library):
+ if tp.ellipsis:
+ return
+ setattr(library, name, getattr(module, name))
+
+ # ----------
+ # named structs
+
+ _generate_cpy_struct_collecttype = _generate_nothing
+
+ def _generate_cpy_struct_decl(self, tp, name):
+ assert name == tp.name
+ self._generate_struct_or_union_decl(tp, 'struct', name)
+
+ def _generate_cpy_struct_method(self, tp, name):
+ self._generate_struct_or_union_method(tp, 'struct', name)
+
+ def _loading_cpy_struct(self, tp, name, module):
+ self._loading_struct_or_union(tp, 'struct', name, module)
+
+ def _loaded_cpy_struct(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ def _generate_struct_or_union_decl(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ prnt = self._prnt
+ prnt('static void %s(%s *p)' % (checkfuncname, cname))
+ prnt('{')
+ prnt(' /* only to generate compile-time warnings or errors */')
+ for i in range(len(tp.fldnames)):
+ fname = tp.fldnames[i]
+ ftype = tp.fldtypes[i]
+ if (isinstance(ftype, model.PrimitiveType)
+ and ftype.is_integer_type()):
+ # accept all integers, but complain on float or double
+ prnt(' (void)((p->%s) << 1);' % fname)
+ else:
+ # only accept exactly the type declared. Note the parentheses
+ # around the '*tmp' below. In most cases they are not needed
+ # but don't hurt --- except test_struct_array_field.
+ prnt(' { %s = &p->%s; (void)tmp; }' % (
+ ftype.get_c_name('(*tmp)'), fname))
+ prnt('}')
+ prnt('static PyObject *')
+ prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
+ prnt('{')
+ prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
+ if tp.partial:
+ prnt(' static Py_ssize_t nums[] = {')
+ prnt(' sizeof(%s),' % cname)
+ prnt(' offsetof(struct _cffi_aligncheck, y),')
+ for fname in tp.fldnames:
+ prnt(' offsetof(%s, %s),' % (cname, fname))
+ prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
+ prnt(' -1')
+ prnt(' };')
+ prnt(' return _cffi_get_struct_layout(nums);')
+ else:
+ ffi = self.ffi
+ BStruct = ffi._get_cached_btype(tp)
+ conditions = [
+ 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
+ 'offsetof(struct _cffi_aligncheck, y) != %d' % (
+ ffi.alignof(BStruct),)]
+ for fname, ftype in zip(tp.fldnames, tp.fldtypes):
+ BField = ffi._get_cached_btype(ftype)
+ conditions += [
+ 'offsetof(%s, %s) != %d' % (
+ cname, fname, ffi.offsetof(BStruct, fname)),
+ 'sizeof(((%s *)0)->%s) != %d' % (
+ cname, fname, ffi.sizeof(BField))]
+ prnt(' if (%s ||' % conditions[0])
+ for i in range(1, len(conditions)-1):
+ prnt(' %s ||' % conditions[i])
+ prnt(' %s) {' % conditions[-1])
+ prnt(' Py_INCREF(Py_False);')
+ prnt(' return Py_False;')
+ prnt(' }')
+ prnt(' else {')
+ prnt(' Py_INCREF(Py_True);')
+ prnt(' return Py_True;')
+ prnt(' }')
+ prnt(' /* the next line is not executed, but compiled */')
+ prnt(' %s(0);' % (checkfuncname,))
+ prnt('}')
+ prnt()
+
+ def _generate_struct_or_union_method(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ self._prnt(' {"%s", %s, METH_NOARGS},' % (layoutfuncname,
+ layoutfuncname))
+
+ def _loading_struct_or_union(self, tp, prefix, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ function = getattr(module, layoutfuncname)
+ layout = function()
+ if layout is False:
+ raise ffiplatform.VerificationError(
+ "incompatible layout for %s" % cname)
+ elif layout is True:
+ assert not tp.partial
+ else:
+ totalsize = layout[0]
+ totalalignment = layout[1]
+ fieldofs = layout[2::2]
+ fieldsize = layout[3::2]
+ assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+ tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+
+ def _loaded_struct_or_union(self, tp):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
+
+ # ----------
+ # 'anonymous' declarations. These are produced for anonymous structs
+ # or unions; the 'name' is obtained by a typedef.
+
+ _generate_cpy_anonymous_collecttype = _generate_nothing
+
+ def _generate_cpy_anonymous_decl(self, tp, name):
+ self._generate_struct_or_union_decl(tp, '', name)
+
+ def _generate_cpy_anonymous_method(self, tp, name):
+ self._generate_struct_or_union_method(tp, '', name)
+
+ def _loading_cpy_anonymous(self, tp, name, module):
+ self._loading_struct_or_union(tp, '', name, module)
+
+ def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ # ----------
+ # constants, likely declared with '#define'
+
+ def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+ vartp=None, delayed=True):
+ prnt = self._prnt
+ funcname = '_cffi_%s_%s' % (category, name)
+ prnt('static int %s(PyObject *lib)' % funcname)
+ prnt('{')
+ prnt(' PyObject *o;')
+ prnt(' int res;')
+ if not is_int:
+ prnt(' %s;' % (vartp or tp).get_c_name(' i'))
+ else:
+ assert category == 'const'
+ #
+ if not is_int:
+ if category == 'var':
+ realexpr = '&' + name
+ else:
+ realexpr = name
+ prnt(' i = (%s);' % (realexpr,))
+ prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i'),))
+ assert delayed
+ else:
+ prnt(' if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name))
+ prnt(' o = PyInt_FromLong((long)(%s));' % (name,))
+ prnt(' else if ((%s) <= 0)' % (name,))
+ prnt(' o = PyLong_FromLongLong((long long)(%s));' % (name,))
+ prnt(' else')
+ prnt(' o = PyLong_FromUnsignedLongLong('
+ '(unsigned long long)(%s));' % (name,))
+ prnt(' if (o == NULL)')
+ prnt(' return -1;')
+ prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name)
+ prnt(' Py_DECREF(o);')
+ prnt(' if (res < 0)')
+ prnt(' return -1;')
+ prnt(' return %s;' % self._chained_list_constants[delayed])
+ self._chained_list_constants[delayed] = funcname + '(lib)'
+ prnt('}')
+ prnt()
+
+ def _generate_cpy_constant_collecttype(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ if not is_int:
+ self._do_collect_type(tp)
+
+ def _generate_cpy_constant_decl(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ self._generate_cpy_const(is_int, name, tp)
+
+ _generate_cpy_constant_method = _generate_nothing
+ _loading_cpy_constant = _loaded_noop
+ _loaded_cpy_constant = _loaded_noop
+
+ # ----------
+ # enums
+
+ def _generate_cpy_enum_decl(self, tp, name):
+ if tp.partial:
+ for enumerator in tp.enumerators:
+ self._generate_cpy_const(True, enumerator, delayed=False)
+ return
+ #
+ funcname = '_cffi_enum_%s' % name
+ prnt = self._prnt
+ prnt('static int %s(PyObject *lib)' % funcname)
+ prnt('{')
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ prnt(' if (%s != %d) {' % (enumerator, enumvalue))
+ prnt(' PyErr_Format(_cffi_VerificationError,')
+ prnt(' "in enum %s: %s has the real value %d, '
+ 'not %d",')
+ prnt(' "%s", "%s", (int)%s, %d);' % (
+ name, enumerator, enumerator, enumvalue))
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' return %s;' % self._chained_list_constants[True])
+ self._chained_list_constants[True] = funcname + '(lib)'
+ prnt('}')
+ prnt()
+
+ _generate_cpy_enum_collecttype = _generate_nothing
+ _generate_cpy_enum_method = _generate_nothing
+ _loading_cpy_enum = _loaded_noop
+
+ def _loading_cpy_enum(self, tp, name, module):
+ if tp.partial:
+ enumvalues = [getattr(module, enumerator)
+ for enumerator in tp.enumerators]
+ tp.enumvalues = tuple(enumvalues)
+ tp.partial = False
+
+ def _loaded_cpy_enum(self, tp, name, module, library):
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ setattr(library, enumerator, enumvalue)
+
+ # ----------
+ # macros: for now only for integers
+
+ def _generate_cpy_macro_decl(self, tp, name):
+ assert tp == '...'
+ self._generate_cpy_const(True, name)
+
+ _generate_cpy_macro_collecttype = _generate_nothing
+ _generate_cpy_macro_method = _generate_nothing
+ _loading_cpy_macro = _loaded_noop
+ _loaded_cpy_macro = _loaded_noop
+
+ # ----------
+ # global variables
+
+ def _generate_cpy_variable_collecttype(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ self._do_collect_type(tp)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._do_collect_type(tp_ptr)
+
+ def _generate_cpy_variable_decl(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ tp_ptr = model.PointerType(tp.item)
+ self._generate_cpy_const(False, name, tp, vartp=tp_ptr)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._generate_cpy_const(False, name, tp_ptr, category='var')
+
+ _generate_cpy_variable_method = _generate_nothing
+ _loading_cpy_variable = _loaded_noop
+
+ def _loaded_cpy_variable(self, tp, name, module, library):
+ if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
+ return # sense that "a=..." is forbidden
+ # remove ptr=<cdata 'int *'> from the library instance, and replace
+ # it by a property on the class, which reads/writes into ptr[0].
+ ptr = getattr(library, name)
+ delattr(library, name)
+ def getter(library):
+ return ptr[0]
+ def setter(library, value):
+ ptr[0] = value
+ setattr(library.__class__, name, property(getter, setter))
+
+ # ----------
+
+ def _generate_setup_custom(self):
+ prnt = self._prnt
+ prnt('static PyObject *_cffi_setup_custom(PyObject *lib)')
+ prnt('{')
+ prnt(' if (%s < 0)' % self._chained_list_constants[True])
+ prnt(' return NULL;')
+ prnt(' Py_INCREF(Py_None);')
+ prnt(' return Py_None;')
+ prnt('}')
+
+cffimod_header = r'''
+#include <Python.h>
+#include <stddef.h>
+
+#ifdef MS_WIN32
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+
+#if PY_MAJOR_VERSION < 3
+# undef PyCapsule_CheckExact
+# undef PyCapsule_GetPointer
+# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
+# define PyCapsule_GetPointer(capsule, name) \
+ (PyCObject_AsVoidPtr(capsule))
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+# define PyInt_AsLong PyLong_AsLong
+#endif
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_signed_char PyInt_FromLong
+#define _cffi_from_c_short PyInt_FromLong
+#define _cffi_from_c_int PyInt_FromLong
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_unsigned_char PyInt_FromLong
+#define _cffi_from_c_unsigned_short PyInt_FromLong
+#define _cffi_from_c_unsigned_long PyLong_FromUnsignedLong
+#define _cffi_from_c_unsigned_long_long PyLong_FromUnsignedLongLong
+
+#if SIZEOF_INT < SIZEOF_LONG
+# define _cffi_from_c_unsigned_int PyInt_FromLong
+#else
+# define _cffi_from_c_unsigned_int PyLong_FromUnsignedLong
+#endif
+
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+# define _cffi_from_c_long_long PyLong_FromLongLong
+#else
+# define _cffi_from_c_long_long PyInt_FromLong
+#endif
+
+#define _cffi_to_c_long PyInt_AsLong
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_to_c_char_p \
+ ((char *(*)(PyObject *))_cffi_exports[0])
+#define _cffi_to_c_signed_char \
+ ((signed char(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_unsigned_char \
+ ((unsigned char(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_short \
+ ((short(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_unsigned_short \
+ ((unsigned short(*)(PyObject *))_cffi_exports[4])
+
+#if SIZEOF_INT < SIZEOF_LONG
+# define _cffi_to_c_int \
+ ((int(*)(PyObject *))_cffi_exports[5])
+# define _cffi_to_c_unsigned_int \
+ ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#else
+# define _cffi_to_c_int _cffi_to_c_long
+# define _cffi_to_c_unsigned_int _cffi_to_c_unsigned_long
+#endif
+
+#define _cffi_to_c_unsigned_long \
+ ((unsigned long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_unsigned_long_long \
+ ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char \
+ ((char(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
+#define _cffi_to_c_pointer \
+ ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
+#define _cffi_get_struct_layout \
+ ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
+#define _cffi_restore_errno \
+ ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno \
+ ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char \
+ ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
+#define _cffi_to_c \
+ ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t \
+ ((wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t \
+ ((PyObject *(*)(wchar_t))_cffi_exports[20])
+#define _CFFI_NUM_EXPORTS 21
+
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+# define _cffi_to_c_long_long PyLong_AsLongLong
+#else
+# define _cffi_to_c_long_long _cffi_to_c_long
+#endif
+
+typedef struct _ctypedescr CTypeDescrObject;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+static PyObject *_cffi_types, *_cffi_VerificationError;
+
+static PyObject *_cffi_setup_custom(PyObject *lib); /* forward */
+
+static PyObject *_cffi_setup(PyObject *self, PyObject *args)
+{
+ PyObject *library;
+ if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
+ &library))
+ return NULL;
+ Py_INCREF(_cffi_types);
+ Py_INCREF(_cffi_VerificationError);
+ return _cffi_setup_custom(library);
+}
+
+static void _cffi_init(void)
+{
+ PyObject *module = PyImport_ImportModule("_cffi_backend");
+ PyObject *c_api_object;
+
+ if (module == NULL)
+ return;
+
+ c_api_object = PyObject_GetAttrString(module, "_C_API");
+ if (c_api_object == NULL)
+ return;
+ if (!PyCapsule_CheckExact(c_api_object)) {
+ PyErr_SetNone(PyExc_ImportError);
+ return;
+ }
+ memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
+ _CFFI_NUM_EXPORTS * sizeof(void *));
+}
+
+#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
+
+/**********/
+'''
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
new file mode 100644
--- /dev/null
+++ b/cffi/vengine_gen.py
@@ -0,0 +1,458 @@
+import sys, os, binascii, imp, shutil
+from . import model, ffiplatform
+
+
+class VGenericEngine(object):
+ _class_key = 'g'
+ _gen_python_module = False
+
+ def __init__(self, verifier):
+ self.verifier = verifier
+ self.ffi = verifier.ffi
+ self.export_symbols = []
+
+ def patch_extension_kwds(self, kwds):
+ # add 'export_symbols' to the dictionary. Note that we add the
+ # list before filling it. When we fill it, it will thus also show
+ # up in kwds['export_symbols'].
+ kwds.setdefault('export_symbols', self.export_symbols)
+
+ def collect_types(self):
+ pass # not needed in the generic engine
+
+ def _prnt(self, what=''):
+ print >> self._f, what
+
+ def write_source_to_f(self):
+ prnt = self._prnt
+ # first paste some standard set of lines that are mostly '#include'
+ prnt(cffimod_header)
+ # then paste the C source given by the user, verbatim.
+ prnt(self.verifier.preamble)
+ #
+ # call generate_gen_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
+ self._generate('decl')
+ #
+ # on Windows, distutils insists on putting init_cffi_xyz in
+ # 'export_symbols', so instead of fighting it, just give up and
+ # give it one
+ if sys.platform == 'win32':
+ prnt("void init%s(void) { }\n" % self.verifier.get_module_name())
+
+ def load_library(self):
+ # import it with the CFFI backend
+ backend = self.ffi._backend
+ module = backend.load_library(self.verifier.modulefilename)
+ #
+ # call loading_gen_struct() to get the struct layout inferred by
+ # the C compiler
+ self._load(module, 'loading')
+ #
+ # build the FFILibrary class and instance
+ class FFILibrary(object):
+ _cffi_generic_module = module
+ library = FFILibrary()
+ #
+ # finally, call the loaded_gen_xxx() functions. This will set
+ # up the 'library' object.
+ self._load(module, 'loaded', library=library)
+ return library
+
+ def _generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ try:
+ method = getattr(self, '_generate_gen_%s_%s' % (kind,
+ step_name))
+ except AttributeError:
+ raise ffiplatform.VerificationError(
+ "not implemented in verify(): %r" % name)
+ method(tp, realname)
+
+ def _load(self, module, step_name, **kwds):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ method = getattr(self, '_%s_gen_%s' % (step_name, kind))
+ method(tp, realname, module, **kwds)
+
+ def _generate_nothing(self, tp, name):
+ pass
+
+ def _loaded_noop(self, tp, name, module, **kwds):
+ pass
+
+ # ----------
+ # typedefs: generates no code so far
+
+ _generate_gen_typedef_decl = _generate_nothing
+ _loading_gen_typedef = _loaded_noop
+ _loaded_gen_typedef = _loaded_noop
+
+ # ----------
+ # function declarations
+
+ def _generate_gen_function_decl(self, tp, name):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ # cannot support vararg functions better than this: check for its
+ # exact type (including the fixed arguments), and build it as a
+ # constant function pointer (no _cffi_f_%s wrapper)
+ self._generate_gen_const(False, name, tp)
+ return
+ prnt = self._prnt
+ numargs = len(tp.args)
+ argnames = []
+ for i, type in enumerate(tp.args):
+ indirection = ''
+ if isinstance(type, model.StructOrUnion):
+ indirection = '*'
+ argnames.append('%sx%d' % (indirection, i))
+ arglist = [type.get_c_name(' %s' % arg)
+ for type, arg in zip(tp.args, argnames)]
+ arglist = ', '.join(arglist) or 'void'
+ wrappername = '_cffi_f_%s' % name
+ self.export_symbols.append(wrappername)
+ funcdecl = ' %s(%s)' % (wrappername, arglist)
+ prnt(tp.result.get_c_name(funcdecl))
+ prnt('{')
+ #
+ if not isinstance(tp.result, model.VoidType):
+ result_code = 'return '
+ else:
+ result_code = ''
+ prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames)))
+ prnt('}')
+ prnt()
+
+ _loading_gen_function = _loaded_noop
+
+ def _loaded_gen_function(self, tp, name, module, library):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ newfunction = self._load_constant(False, tp, name, module)
+ else:
+ indirections = []
+ if any(isinstance(type, model.StructOrUnion) for type in tp.args):
+ indirect_args = []
+ for i, type in enumerate(tp.args):
+ if isinstance(type, model.StructOrUnion):
+ type = model.PointerType(type)
+ indirections.append((i, type))
+ indirect_args.append(type)
+ tp = model.FunctionPtrType(tuple(indirect_args),
+ tp.result, tp.ellipsis)
+ BFunc = self.ffi._get_cached_btype(tp)
+ wrappername = '_cffi_f_%s' % name
+ newfunction = module.load_function(BFunc, wrappername)
+ for i, type in indirections:
+ newfunction = self._make_struct_wrapper(newfunction, i, type)
+ setattr(library, name, newfunction)
+
+ def _make_struct_wrapper(self, oldfunc, i, tp):
+ backend = self.ffi._backend
+ BType = self.ffi._get_cached_btype(tp)
+ def newfunc(*args):
+ args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+ return oldfunc(*args)
+ return newfunc
+
+ # ----------
+ # named structs
+
+ def _generate_gen_struct_decl(self, tp, name):
+ assert name == tp.name
+ self._generate_struct_or_union_decl(tp, 'struct', name)
+
+ def _loading_gen_struct(self, tp, name, module):
+ self._loading_struct_or_union(tp, 'struct', name, module)
+
+ def _loaded_gen_struct(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ def _generate_struct_or_union_decl(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ prnt = self._prnt
+ prnt('static void %s(%s *p)' % (checkfuncname, cname))
+ prnt('{')
+ prnt(' /* only to generate compile-time warnings or errors */')
+ for i in range(len(tp.fldnames)):
+ fname = tp.fldnames[i]
+ ftype = tp.fldtypes[i]
+ if (isinstance(ftype, model.PrimitiveType)
+ and ftype.is_integer_type()):
+ # accept all integers, but complain on float or double
+ prnt(' (void)((p->%s) << 1);' % fname)
+ else:
+ # only accept exactly the type declared. Note the parentheses
+ # around the '*tmp' below. In most cases they are not needed
+ # but don't hurt --- except test_struct_array_field.
+ prnt(' { %s = &p->%s; (void)tmp; }' % (
+ ftype.get_c_name('(*tmp)'), fname))
+ prnt('}')
+ self.export_symbols.append(layoutfuncname)
+ prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,))
+ prnt('{')
+ prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
+ if tp.partial:
+ prnt(' static ssize_t nums[] = {')
+ prnt(' 1, sizeof(%s),' % cname)
+ prnt(' offsetof(struct _cffi_aligncheck, y),')
+ for fname in tp.fldnames:
+ prnt(' offsetof(%s, %s),' % (cname, fname))
+ prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
+ prnt(' -1')
+ prnt(' };')
+ prnt(' return nums[i];')
+ else:
+ ffi = self.ffi
+ BStruct = ffi._get_cached_btype(tp)
+ conditions = [
+ 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
+ 'offsetof(struct _cffi_aligncheck, y) != %d' % (
+ ffi.alignof(BStruct),)]
+ for fname, ftype in zip(tp.fldnames, tp.fldtypes):
+ BField = ffi._get_cached_btype(ftype)
+ conditions += [
+ 'offsetof(%s, %s) != %d' % (
+ cname, fname, ffi.offsetof(BStruct, fname)),
+ 'sizeof(((%s *)0)->%s) != %d' % (
+ cname, fname, ffi.sizeof(BField))]
+ prnt(' if (%s ||' % conditions[0])
+ for i in range(1, len(conditions)-1):
+ prnt(' %s ||' % conditions[i])
+ prnt(' %s) {' % conditions[-1])
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' else {')
+ prnt(' return 0;')
+ prnt(' }')
+ prnt(' /* the next line is not executed, but compiled */')
+ prnt(' %s(0);' % (checkfuncname,))
+ prnt('}')
+ prnt()
+
+ def _loading_struct_or_union(self, tp, prefix, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ BFunc = self.ffi.typeof("ssize_t(*)(ssize_t)")
+ function = module.load_function(BFunc, layoutfuncname)
+ layout = function(0)
+ if layout < 0:
+ raise ffiplatform.VerificationError(
+ "incompatible layout for %s" % cname)
+ elif layout == 0:
+ assert not tp.partial
+ else:
+ totalsize = function(1)
+ totalalignment = function(2)
+ fieldofs = []
+ fieldsize = []
+ num = 3
+ while True:
+ x = function(num)
+ if x < 0: break
+ fieldofs.append(x)
+ fieldsize.append(function(num+1))
+ num += 2
+ assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+ tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+
+ def _loaded_struct_or_union(self, tp):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
+
+ # ----------
+ # 'anonymous' declarations. These are produced for anonymous structs
+ # or unions; the 'name' is obtained by a typedef.
+
+ def _generate_gen_anonymous_decl(self, tp, name):
+ self._generate_struct_or_union_decl(tp, '', name)
+
+ def _loading_gen_anonymous(self, tp, name, module):
+ self._loading_struct_or_union(tp, '', name, module)
+
+ def _loaded_gen_anonymous(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ # ----------
+ # constants, likely declared with '#define'
+
+ def _generate_gen_const(self, is_int, name, tp=None, category='const'):
+ prnt = self._prnt
+ funcname = '_cffi_%s_%s' % (category, name)
+ self.export_symbols.append(funcname)
+ if is_int:
+ assert category == 'const'
+ prnt('int %s(long long *out_value)' % funcname)
+ prnt('{')
+ prnt(' *out_value = (long long)(%s);' % (name,))
+ prnt(' return (%s) <= 0;' % (name,))
+ prnt('}')
+ else:
+ assert tp is not None
+ prnt(tp.get_c_name(' %s(void)' % funcname),)
+ prnt('{')
+ if category == 'var':
+ ampersand = '&'
+ else:
+ ampersand = ''
+ prnt(' return (%s%s);' % (ampersand, name))
+ prnt('}')
+ prnt()
+
+ def _generate_gen_constant_decl(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ self._generate_gen_const(is_int, name, tp)
+
+ _loading_gen_constant = _loaded_noop
+
+ def _load_constant(self, is_int, tp, name, module):
+ funcname = '_cffi_const_%s' % name
+ if is_int:
+ BFunc = self.ffi.typeof("int(*)(long long*)")
+ function = module.load_function(BFunc, funcname)
+ p = self.ffi.new("long long*")
+ negative = function(p)
+ value = int(p[0])
+ if value < 0 and not negative:
+ value += (1 << (8*self.ffi.sizeof("long long")))
+ else:
+ BFunc = self.ffi.typeof(tp.get_c_name('(*)(void)'))
+ function = module.load_function(BFunc, funcname)
+ value = function()
+ return value
+
+ def _loaded_gen_constant(self, tp, name, module, library):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ value = self._load_constant(is_int, tp, name, module)
+ setattr(library, name, value)
+
+ # ----------
+ # enums
+
+ def _generate_gen_enum_decl(self, tp, name):
+ if tp.partial:
+ for enumerator in tp.enumerators:
+ self._generate_gen_const(True, enumerator)
+ return
+ #
+ funcname = '_cffi_enum_%s' % name
+ self.export_symbols.append(funcname)
+ prnt = self._prnt
+ prnt('int %s(char *out_error)' % funcname)
+ prnt('{')
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ prnt(' if (%s != %d) {' % (enumerator, enumvalue))
+ prnt(' snprintf(out_error, 255, "in enum %s: '
+ '%s has the real value %d, not %d",')
+ prnt(' "%s", "%s", (int)%s, %d);' % (
+ name, enumerator, enumerator, enumvalue))
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' return 0;')
+ prnt('}')
+ prnt()
+
+ _loading_gen_enum = _loaded_noop
+
+ def _loading_gen_enum(self, tp, name, module):
+ if tp.partial:
+ enumvalues = [self._load_constant(True, tp, enumerator, module)
+ for enumerator in tp.enumerators]
+ tp.enumvalues = tuple(enumvalues)
+ tp.partial = False
+ else:
+ BFunc = self.ffi.typeof("int(*)(char*)")
+ funcname = '_cffi_enum_%s' % name
+ function = module.load_function(BFunc, funcname)
+ p = self.ffi.new("char[]", 256)
+ if function(p) < 0:
+ raise ffiplatform.VerificationError(self.ffi.string(p))
+
+ def _loaded_gen_enum(self, tp, name, module, library):
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ setattr(library, enumerator, enumvalue)
+
+ # ----------
+ # macros: for now only for integers
+
+ def _generate_gen_macro_decl(self, tp, name):
+ assert tp == '...'
+ self._generate_gen_const(True, name)
+
+ _loading_gen_macro = _loaded_noop
+
+ def _loaded_gen_macro(self, tp, name, module, library):
+ value = self._load_constant(True, tp, name, module)
+ setattr(library, name, value)
+
+ # ----------
+ # global variables
+
+ def _generate_gen_variable_decl(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ tp_ptr = model.PointerType(tp.item)
+ self._generate_gen_const(False, name, tp_ptr)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._generate_gen_const(False, name, tp_ptr, category='var')
+
+ _loading_gen_variable = _loaded_noop
+
+ def _loaded_gen_variable(self, tp, name, module, library):
+ if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
+ # sense that "a=..." is forbidden
+ tp_ptr = model.PointerType(tp.item)
+ value = self._load_constant(False, tp_ptr, name, module)
+ # 'value' is a <cdata 'type *'> which we have to replace with
+ # a <cdata 'type[N]'> if the N is actually known
+ if tp.length is not None:
+ BArray = self.ffi._get_cached_btype(tp)
+ value = self.ffi.cast(BArray, value)
+ setattr(library, name, value)
+ return
+ # remove ptr=<cdata 'int *'> from the library instance, and replace
+ # it by a property on the class, which reads/writes into ptr[0].
+ funcname = '_cffi_var_%s' % name
+ BFunc = self.ffi.typeof(tp.get_c_name('*(*)(void)'))
+ function = module.load_function(BFunc, funcname)
+ ptr = function()
+ def getter(library):
+ return ptr[0]
+ def setter(library, value):
+ ptr[0] = value
+ setattr(library.__class__, name, property(getter, setter))
+
+cffimod_header = r'''
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h> /* XXX for ssize_t on some platforms */
+
+#ifdef _WIN32
+# include <Windows.h>
+# define snprintf _snprintf
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef SSIZE_T ssize_t;
+#else
+# include <stdint.h>
+#endif
+'''
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -1,25 +1,26 @@
-from __future__ import print_function
-import sys, os, hashlib, imp, shutil
-from . import model, ffiplatform
+import sys, os, binascii, imp, shutil
from . import __version__
+from . import ffiplatform
class Verifier(object):
_status = '?'
- def __init__(self, ffi, preamble, **kwds):
- import _cffi_backend
- if ffi._backend is not _cffi_backend:
- raise NotImplementedError(
- "verify() is only available for the _cffi_backend")
- #
+ def __init__(self, ffi, preamble, force_generic_engine=False, **kwds):
self.ffi = ffi
self.preamble = preamble
+ vengine_class = _locate_engine_class(ffi, force_generic_engine)
+ self._vengine = vengine_class(self)
+ self._vengine.patch_extension_kwds(kwds)
self.kwds = kwds
#
- m = hashlib.md5('\x00'.join([sys.version[:3], __version__, preamble] +
- ffi._cdefsources).encode())
- modulename = '_cffi_%s' % m.hexdigest()
+ key = '\x00'.join(['1', sys.version[:3], __version__, preamble] +
+ ffi._cdefsources).encode('utf-8')
+ k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+ k1 = k1.lstrip('0x').rstrip('L')
+ k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+ k2 = k2.lstrip('0').rstrip('L')
+ modulename = '_cffi_%s%s%s' % (self._vengine._class_key, k1, k2)
suffix = _get_so_suffix()
self.sourcefilename = os.path.join(_TMPDIR, modulename + '.c')
self.modulefilename = os.path.join(_TMPDIR, modulename + suffix)
@@ -60,7 +61,8 @@
return self._load_library()
def get_module_name(self):
- return os.path.basename(self.modulefilename).split('.', 1)[0]
+ basename = os.path.basename(self.modulefilename))
+ return basename.rsplit('.', 1)[0]
def get_extension(self):
if self._status == 'init':
@@ -69,6 +71,9 @@
modname = self.get_module_name()
return ffiplatform.get_extension(sourcename, modname, **self.kwds)
+ def generates_python_module(self):
+ return self._vengine._gen_python_module
+
# ----------
def _locate_module(self):
@@ -80,122 +85,23 @@
if f is not None:
f.close()
self.modulefilename = filename
- self._collect_types()
+ self._vengine.collect_types()
self._status = 'module'
- def print(self, what=''):
- print(what, file=self._f)
-
- def _gettypenum(self, type):
- # a KeyError here is a bug. please report it! :-)
- return self._typesdict[type]
-
- def _collect_types(self):
- self._typesdict = {}
- self._generate("collecttype")
-
- def _do_collect_type(self, tp):
- if (not isinstance(tp, model.PrimitiveType) and
- tp not in self._typesdict):
- num = len(self._typesdict)
- self._typesdict[tp] = num
-
def _write_source(self, file=None):
must_close = (file is None)
if must_close:
_ensure_dir(self.sourcefilename)
file = open(self.sourcefilename, 'w')
- self._f = file
+ self._vengine._f = file
try:
- self._write_source_to_f()
+ self._vengine.write_source_to_f()
finally:
- del self._f
+ del self._vengine._f
if must_close:
file.close()
self._status = 'source'
- def _write_source_to_f(self):
- self._collect_types()
- #
- # The new module will have a _cffi_setup() function that receives
- # objects from the ffi world, and that calls some setup code in
- # the module. This setup code is split in several independent
- # functions, e.g. one per constant. The functions are "chained"
- # by ending in a tail call to each other.
- #
- # This is further split in two chained lists, depending on if we
- # can do it at import-time or if we must wait for _cffi_setup() to
- # provide us with the <ctype> objects. This is needed because we
- # need the values of the enum constants in order to build the
- # <ctype 'enum'> that we may have to pass to _cffi_setup().
- #
- # 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']
- #
- print = self.print
- # first paste some standard set of lines that are mostly '#define'
- print(cffimod_header)
- print()
- # then paste the C source given by the user, verbatim.
- print(self.preamble)
- print()
- #
- # call generate_cpy_xxx_decl(), for every xxx found from
- # ffi._parser._declarations. This generates all the functions.
- self._generate("decl")
- #
- # implement the function _cffi_setup_custom() as calling the
- # head of the chained list.
- self._generate_setup_custom()
- print()
- #
- # produce the method table, including the entries for the
- # generated Python->C function wrappers, which are done
- # by generate_cpy_function_method().
- print('static PyMethodDef _cffi_methods[] = {')
- self._generate("method")
- print(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
- print(' {NULL, NULL} /* Sentinel */')
- print('};')
- print()
- #
- # standard init.
- modname = self.get_module_name()
- if sys.version < '3':
- print('PyMODINIT_FUNC')
- print('init%s(void)' % modname)
- print('{')
- print(' PyObject *lib;')
- print(' lib = Py_InitModule("%s", _cffi_methods);' % modname)
- print(' if (lib == NULL || %s < 0)' % (
- self._chained_list_constants[False],))
- print(' return;')
- print(' _cffi_init();')
- print('}')
- else:
- print('static struct PyModuleDef _cffi_module_def = {')
- print(' PyModuleDef_HEAD_INIT,')
- print(' "%s",' % modname)
- print(' NULL,')
- print(' -1,')
- print(' _cffi_methods,')
- print(' NULL, NULL, NULL, NULL')
- print('};')
- print('')
- print('PyMODINIT_FUNC')
- print('PyInit_%s(void)' % modname)
- print('{')
- print(' PyObject *lib;')
- print(' lib = PyModule_Create(&_cffi_module_def);')
- print(' if (lib == NULL || %s < 0)' % (
- self._chained_list_constants[False],))
- print(' return NULL;')
- print(' _cffi_init();')
- print(' return lib;')
- print('}')
-
def _compile_module(self):
# compile this C source
tmpdir = os.path.dirname(self.sourcefilename)
@@ -210,654 +116,31 @@
self._status = 'module'
def _load_library(self):
- # XXX review all usages of 'self' here!
- # import it as a new extension module
- try:
- module = imp.load_dynamic(self.get_module_name(),
- self.modulefilename)
- except ImportError as e:
- error = "importing %r: %s" % (self.modulefilename, e)
- raise ffiplatform.VerificationError(error)
- #
- # call loading_cpy_struct() to get the struct layout inferred by
- # the C compiler
- self._load(module, 'loading')
- #
- # the C code will need the <ctype> objects. Collect them in
- # order in a list.
- revmapping = dict([(value, key)
- for (key, value) in self._typesdict.items()])
- lst = [revmapping[i] for i in range(len(revmapping))]
- lst = list(map(self.ffi._get_cached_btype, lst))
- #
- # build the FFILibrary class and instance and call _cffi_setup().
- # this will set up some fields like '_cffi_types', and only then
- # it will invoke the chained list of functions that will really
- # build (notably) the constant objects, as <cdata> if they are
- # pointers, and store them as attributes on the 'library' object.
- class FFILibrary(object):
- pass
- library = FFILibrary()
- module._cffi_setup(lst, ffiplatform.VerificationError, library)
- #
- # finally, call the loaded_cpy_xxx() functions. This will perform
- # the final adjustments, like copying the Python->C wrapper
- # functions from the module to the 'library' object, and setting
- # up the FFILibrary class with properties for the global C variables.
- self._load(module, 'loaded', library=library)
- return library
+ return self._vengine.load_library()
- def _generate(self, step_name):
- for name, tp in self.ffi._parser._declarations.items():
- kind, realname = name.split(' ', 1)
+# ____________________________________________________________
+
+_FORCE_GENERIC_ENGINE = False # for tests
+
+def _locate_engine_class(ffi, force_generic_engine):
+ if _FORCE_GENERIC_ENGINE:
+ force_generic_engine = True
+ if not force_generic_engine:
+ if '__pypy__' in sys.builtin_module_names:
+ force_generic_engine = True
+ else:
try:
- method = getattr(self, '_generate_cpy_%s_%s' % (kind,
- step_name))
- except AttributeError:
- raise ffiplatform.VerificationError(
- "not implemented in verify(): %r" % name)
- method(tp, realname)
-
- def _load(self, module, step_name, **kwds):
- for name, tp in self.ffi._parser._declarations.items():
- kind, realname = name.split(' ', 1)
- method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
- method(tp, realname, module, **kwds)
-
- def _generate_nothing(self, tp, name):
- pass
-
- def _loaded_noop(self, tp, name, module, **kwds):
- pass
-
- # ----------
-
- def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
- extraarg = ''
- if isinstance(tp, model.PrimitiveType):
- converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),)
- errvalue = '-1'
- #
- elif isinstance(tp, model.PointerType):
- if (isinstance(tp.totype, model.PrimitiveType) and
- tp.totype.name == 'char'):
- converter = '_cffi_to_c_char_p'
- else:
- converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
- extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
- errvalue = 'NULL'
- #
- elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
- # a struct (not a struct pointer) as a function argument
- self.print(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
- % (tovar, self._gettypenum(tp), fromvar))
- self.print(' %s;' % errcode)
- return
- #
- elif isinstance(tp, model.FunctionPtrType):
- converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
- extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
- errvalue = 'NULL'
- #
- else:
- raise NotImplementedError(tp)
- #
- self.print(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
- self.print(' if (%s == (%s)%s && PyErr_Occurred())' % (
- tovar, tp.get_c_name(''), errvalue))
- self.print(' %s;' % errcode)
-
- def _convert_expr_from_c(self, tp, var):
- if isinstance(tp, model.PrimitiveType):
- return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
- elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
- return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, model.ArrayType):
- return '_cffi_from_c_deref((char *)%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, model.StructType):
- return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, model.EnumType):
- return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- else:
- raise NotImplementedError(tp)
-
- # ----------
- # typedefs: generates no code so far
-
- _generate_cpy_typedef_collecttype = _generate_nothing
- _generate_cpy_typedef_decl = _generate_nothing
- _generate_cpy_typedef_method = _generate_nothing
- _loading_cpy_typedef = _loaded_noop
- _loaded_cpy_typedef = _loaded_noop
-
- # ----------
- # function declarations
-
- def _generate_cpy_function_collecttype(self, tp, name):
- assert isinstance(tp, model.FunctionPtrType)
- if tp.ellipsis:
- self._do_collect_type(tp)
- else:
- for type in tp.args:
- self._do_collect_type(type)
- self._do_collect_type(tp.result)
-
- def _generate_cpy_function_decl(self, tp, name):
- assert isinstance(tp, model.FunctionPtrType)
- if tp.ellipsis:
- # cannot support vararg functions better than this: check for its
- # exact type (including the fixed arguments), and build it as a
- # constant function pointer (no CPython wrapper)
- self._generate_cpy_const(False, name, tp)
- return
- print = self.print
- numargs = len(tp.args)
- if numargs == 0:
- argname = 'no_arg'
- elif numargs == 1:
- argname = 'arg0'
- else:
- argname = 'args'
- print('static PyObject *')
- print('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
- print('{')
- #
- for i, type in enumerate(tp.args):
- print(' %s;' % type.get_c_name(' x%d' % i))
- if not isinstance(tp.result, model.VoidType):
- result_code = 'result = '
- print(' %s;' % tp.result.get_c_name(' result'))
- else:
- result_code = ''
- #
- if len(tp.args) > 1:
- rng = range(len(tp.args))
- for i in rng:
- print(' PyObject *arg%d;' % i)
- print()
- print(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
- 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
- print(' return NULL;')
- print()
- #
- for i, type in enumerate(tp.args):
- self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
- 'return NULL')
- print()
- #
- print(' _cffi_restore_errno();')
- print(' { %s%s(%s); }' % (
- result_code, name,
- ', '.join(['x%d' % i for i in range(len(tp.args))])))
- print(' _cffi_save_errno();')
- print()
- #
- if result_code:
- print(' return %s;' %
- self._convert_expr_from_c(tp.result, 'result'))
- else:
- print(' Py_INCREF(Py_None);')
- print(' return Py_None;')
- print('}')
- print()
-
- def _generate_cpy_function_method(self, tp, name):
- if tp.ellipsis:
- return
- numargs = len(tp.args)
- if numargs == 0:
- meth = 'METH_NOARGS'
- elif numargs == 1:
- meth = 'METH_O'
- else:
- meth = 'METH_VARARGS'
- self.print(' {"%s", _cffi_f_%s, %s},' % (name, name, meth))
-
- _loading_cpy_function = _loaded_noop
-
- def _loaded_cpy_function(self, tp, name, module, library):
- if tp.ellipsis:
- return
- setattr(library, name, getattr(module, name))
-
- # ----------
- # named structs
-
- _generate_cpy_struct_collecttype = _generate_nothing
-
- def _generate_cpy_struct_decl(self, tp, name):
- assert name == tp.name
- self._generate_struct_or_union_decl(tp, 'struct', name)
-
- def _generate_cpy_struct_method(self, tp, name):
- self._generate_struct_or_union_method(tp, 'struct', name)
-
- def _loading_cpy_struct(self, tp, name, module):
- self._loading_struct_or_union(tp, 'struct', name, module)
-
- def _loaded_cpy_struct(self, tp, name, module, **kwds):
- self._loaded_struct_or_union(tp)
-
- def _generate_struct_or_union_decl(self, tp, prefix, name):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
- layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- cname = ('%s %s' % (prefix, name)).strip()
- #
- print = self.print
- print('static void %s(%s *p)' % (checkfuncname, cname))
- print('{')
- print(' /* only to generate compile-time warnings or errors */')
- for i in range(len(tp.fldnames)):
- fname = tp.fldnames[i]
- ftype = tp.fldtypes[i]
- if (isinstance(ftype, model.PrimitiveType)
- and ftype.is_integer_type()):
- # accept all integers, but complain on float or double
- print(' (void)((p->%s) << 1);' % fname)
- else:
- # only accept exactly the type declared. Note the parentheses
- # around the '*tmp' below. In most cases they are not needed
- # but don't hurt --- except test_struct_array_field.
- print(' { %s = &p->%s; (void)tmp; }' % (
- ftype.get_c_name('(*tmp)'), fname))
- print('}')
- print('static PyObject *')
- print('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
- print('{')
- print(' struct _cffi_aligncheck { char x; %s y; };' % cname)
- if tp.partial:
- print(' static Py_ssize_t nums[] = {')
- print(' sizeof(%s),' % cname)
- print(' offsetof(struct _cffi_aligncheck, y),')
- for fname in tp.fldnames:
- print(' offsetof(%s, %s),' % (cname, fname))
- print(' sizeof(((%s *)0)->%s),' % (cname, fname))
- print(' -1')
- print(' };')
- print(' return _cffi_get_struct_layout(nums);')
- else:
- ffi = self.ffi
- BStruct = ffi._get_cached_btype(tp)
- conditions = [
- 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
- 'offsetof(struct _cffi_aligncheck, y) != %d' % (
- ffi.alignof(BStruct),)]
- for fname, ftype in zip(tp.fldnames, tp.fldtypes):
- BField = ffi._get_cached_btype(ftype)
- conditions += [
- 'offsetof(%s, %s) != %d' % (
- cname, fname, ffi.offsetof(BStruct, fname)),
- 'sizeof(((%s *)0)->%s) != %d' % (
- cname, fname, ffi.sizeof(BField))]
- print(' if (%s ||' % conditions[0])
- for i in range(1, len(conditions)-1):
- print(' %s ||' % conditions[i])
- print(' %s) {' % conditions[-1])
- print(' Py_INCREF(Py_False);')
- print(' return Py_False;')
- print(' }')
- print(' else {')
- print(' Py_INCREF(Py_True);')
- print(' return Py_True;')
- print(' }')
- print(' /* the next line is not executed, but compiled */')
- print(' %s(0);' % (checkfuncname,))
- print('}')
- print()
-
- def _generate_struct_or_union_method(self, tp, prefix, name):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- self.print(' {"%s", %s, METH_NOARGS},' % (layoutfuncname,
- layoutfuncname))
-
- def _loading_struct_or_union(self, tp, prefix, name, module):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- cname = ('%s %s' % (prefix, name)).strip()
- #
- function = getattr(module, layoutfuncname)
- layout = function()
- if layout is False:
- raise ffiplatform.VerificationError(
- "incompatible layout for %s" % cname)
- elif layout is True:
- assert not tp.partial
- else:
- totalsize = layout[0]
- totalalignment = layout[1]
- fieldofs = layout[2::2]
- fieldsize = layout[3::2]
- assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
- tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
-
- def _loaded_struct_or_union(self, tp):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
-
- # ----------
- # 'anonymous' declarations. These are produced for anonymous structs
- # or unions; the 'name' is obtained by a typedef.
-
- _generate_cpy_anonymous_collecttype = _generate_nothing
-
- def _generate_cpy_anonymous_decl(self, tp, name):
- self._generate_struct_or_union_decl(tp, '', name)
-
- def _generate_cpy_anonymous_method(self, tp, name):
- self._generate_struct_or_union_method(tp, '', name)
-
- def _loading_cpy_anonymous(self, tp, name, module):
- self._loading_struct_or_union(tp, '', name, module)
-
- def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
- self._loaded_struct_or_union(tp)
-
- # ----------
- # constants, likely declared with '#define'
-
- def _generate_cpy_const(self, is_int, name, tp=None, category='const',
- vartp=None, delayed=True):
- print = self.print
- funcname = '_cffi_%s_%s' % (category, name)
- print('static int %s(PyObject *lib)' % funcname)
- print('{')
- print(' PyObject *o;')
- print(' int res;')
- if not is_int:
- print(' %s;' % (vartp or tp).get_c_name(' i'))
- else:
- assert category == 'const'
- #
- if not is_int:
- if category == 'var':
- realexpr = '&' + name
- else:
- realexpr = name
- print(' i = (%s);' % (realexpr,))
- print(' o = %s;' % (self._convert_expr_from_c(tp, 'i'),))
- assert delayed
- else:
- print(' if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name))
- print(' o = PyInt_FromLong((long)(%s));' % (name,))
- print(' else if ((%s) <= 0)' % (name,))
- print(' o = PyLong_FromLongLong((long long)(%s));' % (name,))
- print(' else')
- print(' o = PyLong_FromUnsignedLongLong('
- '(unsigned long long)(%s));' % (name,))
- print(' if (o == NULL)')
- print(' return -1;')
- print(' res = PyObject_SetAttrString(lib, "%s", o);' % name)
- print(' Py_DECREF(o);')
- print(' if (res < 0)')
- print(' return -1;')
- print(' return %s;' % self._chained_list_constants[delayed])
- self._chained_list_constants[delayed] = funcname + '(lib)'
- print('}')
- print()
-
- def _generate_cpy_constant_collecttype(self, tp, name):
- is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
- if not is_int:
- self._do_collect_type(tp)
-
- def _generate_cpy_constant_decl(self, tp, name):
- is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
- self._generate_cpy_const(is_int, name, tp)
-
- _generate_cpy_constant_method = _generate_nothing
- _loading_cpy_constant = _loaded_noop
- _loaded_cpy_constant = _loaded_noop
-
- # ----------
- # enums
-
- def _generate_cpy_enum_decl(self, tp, name):
- if tp.partial:
- for enumerator in tp.enumerators:
- self._generate_cpy_const(True, enumerator, delayed=False)
- return
- #
- funcname = '_cffi_enum_%s' % name
- print = self.print
- print('static int %s(PyObject *lib)' % funcname)
- print('{')
- for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- print(' if (%s != %d) {' % (enumerator, enumvalue))
- print(' PyErr_Format(_cffi_VerificationError,')
- print(' "in enum %s: %s has the real value %d, '
- 'not %d",')
- print(' "%s", "%s", (int)%s, %d);' % (
- name, enumerator, enumerator, enumvalue))
- print(' return -1;')
- print(' }')
- print(' return %s;' % self._chained_list_constants[True])
- self._chained_list_constants[True] = funcname + '(lib)'
- print('}')
- print()
-
- _generate_cpy_enum_collecttype = _generate_nothing
- _generate_cpy_enum_method = _generate_nothing
- _loading_cpy_enum = _loaded_noop
-
- def _loading_cpy_enum(self, tp, name, module):
- if tp.partial:
- enumvalues = [getattr(module, enumerator)
- for enumerator in tp.enumerators]
- tp.enumvalues = tuple(enumvalues)
- tp.partial = False
-
- def _loaded_cpy_enum(self, tp, name, module, library):
- for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- setattr(library, enumerator, enumvalue)
-
- # ----------
- # macros: for now only for integers
-
- def _generate_cpy_macro_decl(self, tp, name):
- assert tp == '...'
- self._generate_cpy_const(True, name)
-
- _generate_cpy_macro_collecttype = _generate_nothing
- _generate_cpy_macro_method = _generate_nothing
- _loading_cpy_macro = _loaded_noop
- _loaded_cpy_macro = _loaded_noop
-
- # ----------
- # global variables
-
- def _generate_cpy_variable_collecttype(self, tp, name):
- if isinstance(tp, model.ArrayType):
- self._do_collect_type(tp)
- else:
- tp_ptr = model.PointerType(tp)
- self._do_collect_type(tp_ptr)
-
- def _generate_cpy_variable_decl(self, tp, name):
- if isinstance(tp, model.ArrayType):
- tp_ptr = model.PointerType(tp.item)
- self._generate_cpy_const(False, name, tp, vartp=tp_ptr)
- else:
- tp_ptr = model.PointerType(tp)
- self._generate_cpy_const(False, name, tp_ptr, category='var')
-
- _generate_cpy_variable_method = _generate_nothing
- _loading_cpy_variable = _loaded_noop
-
- def _loaded_cpy_variable(self, tp, name, module, library):
- if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
- return # sense that "a=..." is forbidden
- # remove ptr=<cdata 'int *'> from the library instance, and replace
- # it by a property on the class, which reads/writes into ptr[0].
- ptr = getattr(library, name)
- delattr(library, name)
- def getter(library):
- return ptr[0]
- def setter(library, value):
- ptr[0] = value
- setattr(library.__class__, name, property(getter, setter))
-
- # ----------
-
- def _generate_setup_custom(self):
- print = self.print
- print('static PyObject *_cffi_setup_custom(PyObject *lib)')
- print('{')
- print(' if (%s < 0)' % self._chained_list_constants[True])
- print(' return NULL;')
- print(' Py_INCREF(Py_None);')
- print(' return Py_None;')
- print('}')
-
-cffimod_header = r'''
-#include <Python.h>
-#include <stddef.h>
-
-#if PY_MAJOR_VERSION < 3
-# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
-# define PyCapsule_GetPointer(capsule, name) \
- (PyCObject_AsVoidPtr(capsule))
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-# define PyInt_FromLong PyLong_FromLong
-# define PyInt_AsLong PyLong_AsLong
-#endif
-
-#define _cffi_from_c_double PyFloat_FromDouble
-#define _cffi_from_c_float PyFloat_FromDouble
-#define _cffi_from_c_signed_char PyInt_FromLong
-#define _cffi_from_c_short PyInt_FromLong
-#define _cffi_from_c_int PyInt_FromLong
-#define _cffi_from_c_long PyInt_FromLong
-#define _cffi_from_c_unsigned_char PyInt_FromLong
-#define _cffi_from_c_unsigned_short PyInt_FromLong
-#define _cffi_from_c_unsigned_long PyLong_FromUnsignedLong
-#define _cffi_from_c_unsigned_long_long PyLong_FromUnsignedLongLong
-
-#if SIZEOF_INT < SIZEOF_LONG
-# define _cffi_from_c_unsigned_int PyInt_FromLong
-#else
-# define _cffi_from_c_unsigned_int PyLong_FromUnsignedLong
-#endif
-
-#if SIZEOF_LONG < SIZEOF_LONG_LONG
-# define _cffi_from_c_long_long PyLong_FromLongLong
-#else
-# define _cffi_from_c_long_long PyInt_FromLong
-#endif
-
-#define _cffi_to_c_long PyInt_AsLong
-#define _cffi_to_c_double PyFloat_AsDouble
-#define _cffi_to_c_float PyFloat_AsDouble
-
-#define _cffi_to_c_char_p \
- ((char *(*)(PyObject *))_cffi_exports[0])
-#define _cffi_to_c_signed_char \
- ((signed char(*)(PyObject *))_cffi_exports[1])
-#define _cffi_to_c_unsigned_char \
- ((unsigned char(*)(PyObject *))_cffi_exports[2])
-#define _cffi_to_c_short \
- ((short(*)(PyObject *))_cffi_exports[3])
-#define _cffi_to_c_unsigned_short \
- ((unsigned short(*)(PyObject *))_cffi_exports[4])
-
-#if SIZEOF_INT < SIZEOF_LONG
-# define _cffi_to_c_int \
- ((int(*)(PyObject *))_cffi_exports[5])
-# define _cffi_to_c_unsigned_int \
- ((unsigned int(*)(PyObject *))_cffi_exports[6])
-#else
-# define _cffi_to_c_int _cffi_to_c_long
-# define _cffi_to_c_unsigned_int _cffi_to_c_unsigned_long
-#endif
-
-#define _cffi_to_c_unsigned_long \
- ((unsigned long(*)(PyObject *))_cffi_exports[7])
-#define _cffi_to_c_unsigned_long_long \
- ((unsigned long long(*)(PyObject *))_cffi_exports[8])
-#define _cffi_to_c_char \
- ((char(*)(PyObject *))_cffi_exports[9])
-#define _cffi_from_c_pointer \
- ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
-#define _cffi_to_c_pointer \
- ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
-#define _cffi_get_struct_layout \
- ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
-#define _cffi_restore_errno \
- ((void(*)(void))_cffi_exports[13])
-#define _cffi_save_errno \
- ((void(*)(void))_cffi_exports[14])
-#define _cffi_from_c_char \
- ((PyObject *(*)(char))_cffi_exports[15])
-#define _cffi_from_c_deref \
- ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
-#define _cffi_to_c \
- ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
-#define _cffi_from_c_struct \
- ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
-#define _cffi_to_c_wchar_t \
- ((wchar_t(*)(PyObject *))_cffi_exports[19])
-#define _cffi_from_c_wchar_t \
- ((PyObject *(*)(wchar_t))_cffi_exports[20])
-#define _CFFI_NUM_EXPORTS 21
-
-#if SIZEOF_LONG < SIZEOF_LONG_LONG
-# define _cffi_to_c_long_long PyLong_AsLongLong
-#else
-# define _cffi_to_c_long_long _cffi_to_c_long
-#endif
-
-typedef struct _ctypedescr CTypeDescrObject;
-
-static void *_cffi_exports[_CFFI_NUM_EXPORTS];
-static PyObject *_cffi_types, *_cffi_VerificationError;
-
-static PyObject *_cffi_setup_custom(PyObject *lib); /* forward */
-
-static PyObject *_cffi_setup(PyObject *self, PyObject *args)
-{
- PyObject *library;
- if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
- &library))
- return NULL;
- Py_INCREF(_cffi_types);
- Py_INCREF(_cffi_VerificationError);
- return _cffi_setup_custom(library);
-}
-
-static void _cffi_init(void)
-{
- PyObject *module = PyImport_ImportModule("_cffi_backend");
- PyObject *c_api_object;
-
- if (module == NULL)
- return;
-
- c_api_object = PyObject_GetAttrString(module, "_C_API");
- if (c_api_object == NULL)
- return;
- if (!PyCapsule_CheckExact(c_api_object)) {
- PyErr_SetNone(PyExc_ImportError);
- return;
- }
- memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
- _CFFI_NUM_EXPORTS * sizeof(void *));
-}
-
-#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
-
-/**********/
-'''
+ import _cffi_backend
+ except ImportError:
+ _cffi_backend = '?'
+ if ffi._backend is not _cffi_backend:
+ force_generic_engine = True
+ if force_generic_engine:
+ from . import vengine_gen
+ return vengine_gen.VGenericEngine
+ else:
+ from . import vengine_cpy
+ return vengine_cpy.VCPythonEngine
# ____________________________________________________________
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``
@@ -99,8 +99,7 @@
* or you can directly import and use ``cffi``, but if you don't
compile the ``_cffi_backend`` extension module, it will fall back
- to using internally ``ctypes`` (much slower and does not support
- ``verify()``; we recommend not to use it).
+ to using internally ``ctypes`` (much slower; we recommend not to use it).
* running the tests: ``py.test c/ testing/ -x`` (if you didn't
install cffi yet, you may need ``python setup_base.py build``
@@ -194,7 +193,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,
@@ -307,7 +306,8 @@
During development, every time you change the C sources that you pass to
``cdef()`` or ``verify()``, then the latter will create a new module
-file name, based on the MD5 hash of these strings. This creates more
+file name, based on two CRC32 hashes computed from these strings.
+This creates more
and more files in the ``__pycache__`` directory. It is recommended that
you clean it up from time to time. A nice way to do that is to add, in
your test suite, a call to ``cffi.verifier.cleanup_tmpdir()``.
@@ -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
@@ -539,7 +539,20 @@
long as needed. (This also applies if you immediately cast the returned
pointer to a pointer of a different type: only the original object has
ownership, so you must keep it alive. As soon as you forget it, then
-the casted pointer will point to garbage.)
+the casted pointer will point to garbage.) Example::
+
+ global_weakkeydict = weakref.WeakKeyDictionary()
+
+ s1 = ffi.new("struct foo *")
+ fld1 = ffi.new("struct bar *")
+ fld2 = ffi.new("struct bar *")
+ s1.thefield1 = fld1
+ s1.thefield2 = fld2
+ # here the 'fld1' and 'fld2' object must not go away,
+ # otherwise 's1.thefield1/2' will point to garbage!
+ global_weakkeydict[s1] = (fld1, fld2)
+ # now 's1' keeps alive 'fld1' and 'fld2'. When 's1' goes
+ # away, then the weak dictionary entry will be removed.
The cdata objects support mostly the same operations as in C: you can
read or write from pointers, arrays and structures. Dereferencing a
@@ -615,11 +628,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 +671,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 +679,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 +714,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 +747,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 +851,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;
@@ -856,6 +915,37 @@
``ffi.getcname(ffi.typeof(x), "*")`` returns the string representation
of the C type "pointer to the same type than x".
+``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)``. *New in version 0.3* (together
+with the fact that any cdata object can be weakly referenced).
+
+.. "versionadded:: 0.3" --- inlined in the previous paragraph
+
+
+Unimplemented features
+----------------------
+
+All of the ANSI C declarations should be supported, and some of C99.
+Here are the major known missing features that are GCC or MSVC
+extensions:
+
+* Any ``__attribute__`` or ``#pragma pack(n)``
+
+* Additional types: complex numbers, special-size floating and
+ fixed point types, vector types, etc. (must be declared with
+ ``typedef struct { ...; } typename;`` and cannot be accessed directly)
+
+* Unnamed struct/union fields within struct/union
+
+* Thread-local variables (access them via getter/setter functions)
+
+* Variable-length structures, i.e. whose last field is a variable-length
+ array (work around like in C, e.g. by declaring it as an array of
+ length 0, allocating a ``char[]`` of the correct size, and casting
+ it to a struct pointer)
+
Reference: conversions
----------------------
@@ -874,10 +964,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 +975,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 +1004,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 +1019,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
-------------------
@@ -959,13 +1070,13 @@
``Verifier`` objects have the following public attributes and methods:
- ``sourcefilename``: name of a C file. Defaults to
- ``__pycache__/_cffi_MD5HASH.c``, with the ``MD5HASH`` part computed
+ ``__pycache__/_cffi_CRCHASH.c``, with the ``CRCHASH`` part computed
from the strings you passed to cdef() and verify() as well as the
version numbers of Python and CFFI. Can be changed before calling
``write_source()`` if you want to write the source somewhere else.
- ``modulefilename``: name of the ``.so`` file (or ``.pyd`` on Windows).
- Defaults to ``__pycache__/_cffi_MD5HASH.so``. Can be changed before
+ Defaults to ``__pycache__/_cffi_CRCHASH.so``. Can be changed before
calling ``compile_module()``.
- ``get_module_name()``: extract the module name from ``modulefilename``.
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
@@ -8,6 +8,9 @@
SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p)
SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+if sys.version_info >= (3,):
+ unicode = str
+
class BackendTests:
@@ -59,7 +62,7 @@
assert int(p) == max
q = ffi.cast(c_decl, min - 1)
assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
- if sys.version < '3':
+ if sys.version_info < (3,):
p = ffi.cast(c_decl, long(max))
assert int(p) == max
q = ffi.cast(c_decl, long(min - 1))
@@ -72,7 +75,7 @@
py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1)
assert ffi.new(c_decl_ptr, min)[0] == min
assert ffi.new(c_decl_ptr, max)[0] == max
- if sys.version < '3':
+ if sys.version_info < (3,):
py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1))
py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1))
assert ffi.new(c_decl_ptr, long(min))[0] == min
@@ -282,7 +285,7 @@
assert ffi.new("char*")[0] == b'\x00'
assert int(ffi.cast("char", 300)) == 300 - 256
assert bool(ffi.cast("char", 0))
- if sys.version < '3':
+ if sys.version_info < (3,):
py.test.raises(TypeError, ffi.new, "char*", 32)
else:
assert ffi.new("char*", 32)[0] == b' '
@@ -555,57 +558,65 @@
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 ffi.new("char*", b"x").value == b"x"
- assert ffi.new("char*", b"\x00").value == b""
+ x = ffi.new("char*", b"x")
+ assert str(x) == repr(x)
+ assert ffi.string(x) == b"x"
+ assert ffi.string(ffi.new("char*", b"\x00")) == ""
+ py.test.raises(TypeError, ffi.new, "char*", unicode("foo"))
def test_unicode_from_wchar_pointer(self):
ffi = FFI(backend=self.Backend())
self.check_wchar_t(ffi)
- assert ffi.new("wchar_t*", u"x").value == u"x"
- assert ffi.new("wchar_t*", u"\x00").value == 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 ffi.cast("char", b"x").value == b"x"
- p = ffi.new("char[]", b"hello.")
- p[5] = b'!'
- assert p.value == b"hello!"
- p[6] = b'?'
- assert p.value == b"hello!?"
- p[3] = b'\x00'
- assert p.value == b"hel"
- py.test.raises(IndexError, "p[7] = b'X'")
+ p = ffi.new("char[]", "hello.")
+ p[5] = '!'
+ assert ffi.string(p) == "hello!"
+ p[6] = '?'
+ assert ffi.string(p) == "hello!?"
+ p[3] = '\x00'
+ assert ffi.string(p) == "hel"
+ assert ffi.string(p, 2) == "he"
+ py.test.raises(IndexError, "p[7] = 'X'")
#
a = ffi.new("char[]", b"hello\x00world")
assert len(a) == 12
p = ffi.cast("char *", a)
- assert p.value == b'hello'
+ assert ffi.string(p) == b'hello'
def test_string_from_wchar_array(self):
ffi = FFI(backend=self.Backend())
self.check_wchar_t(ffi)
- assert ffi.cast("wchar_t", b"x").value == u"x"
- assert ffi.cast("wchar_t", u"x").value == 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 p.value == u"hello!"
- p[6] = u'\u1234'
- assert p.value == u"hello!\u1234"
+ assert ffi.string(p) == u"hello!"
+ p[6] = unichr(1234)
+ assert ffi.string(p) == u"hello!\u04d2"
p[3] = u'\x00'
- assert p.value == 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 p.value == 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
@@ -614,7 +625,7 @@
t = ffi.new("const char[]", b"testing")
s = ffi.new("struct foo*", [t])
assert type(s.name) is not str
- assert s.name.value == b"testing"
+ assert ffi.string(s.name) == "testing"
py.test.raises(TypeError, "s.name = None")
s.name = ffi.NULL
assert s.name == ffi.NULL
@@ -626,11 +637,8 @@
ffi.cdef("struct foo { const wchar_t *name; };")
t = ffi.new("const wchar_t[]", u"testing")
s = ffi.new("struct foo*", [t])
- if sys.version < '3':
- assert type(s.name) not in (str, unicode)
- else:
- assert type(s.name) not in (bytes, str)
- assert s.name.value == u"testing"
+ assert type(s.name) not in (bytes, str, unicode)
+ assert ffi.string(s.name) == u"testing"
s.name = ffi.NULL
assert s.name == ffi.NULL
@@ -811,6 +819,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)
@@ -822,7 +852,7 @@
a = ffi.cast("int", 12.9)
assert int(a) == 12
a = ffi.cast("char", 66.9 + 256)
- assert a.value == b"B"
+ assert ffi.string(a) == b"B"
#
a = ffi.cast("float", ffi.cast("int", 12))
assert float(a) == 12.0
@@ -833,7 +863,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 a.value == b"B"
+ assert ffi.string(a) == b"B"
def test_enum(self):
ffi = FFI(backend=self.Backend())
@@ -876,12 +906,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())
@@ -1119,6 +1149,26 @@
f.close()
os.unlink(filename)
+ def test_array_in_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int len; short data[5]; };")
+ p = ffi.new("struct foo_s *")
+ p.data[3] = 5
+ assert p.data[3] == 5
+ assert repr(p.data).startswith("<cdata 'short[5]' 0x")
+
+ def test_struct_containing_array_varsize_workaround(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int len; short data[0]; };")
+ p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT)
+ q = ffi.cast("struct foo_s *", p)
+ assert q.len == 0
+ # 'q.data' gets not a 'short[0]', but just a 'short *' instead
+ assert repr(q.data).startswith("<cdata 'short *' 0x")
+ assert q.data[6] == 0
+ q.data[6] = 15
+ assert q.data[6] == 15
+
def test_new_struct_containing_array_varsize(self):
py.test.skip("later?")
ffi = FFI(backend=self.Backend())
@@ -1226,4 +1276,49 @@
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
+
+ def test_nested_anonymous_struct(self):
+ py.test.skip("later")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ struct { int a, b; };
+ union { int c, d; };
+ };
+ """)
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+ p = ffi.new("struct foo_s *", [[1], [3]])
+ assert p.a == 1
+ assert p.b == 0
+ assert p.c == 3
+ assert p.d == 3
+ p.d = 17
+ assert p.c == 17
+ p.b = 19
+ assert p.a == 1
+ assert p.b == 19
+ assert p.c == 17
+ assert p.d == 17
+
+ def test_cast_to_array_type(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int[4]", [-5])
+ q = ffi.cast("int[3]", p)
+ assert q[0] == -5
+ assert repr(q).startswith("<cdata 'int[3]' 0x")
+
+ def test_gc(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ seen = []
+ def destructor(p1):
+ assert p1 is p
+ assert p1[0] == 123
+ seen.append(1)
+ q = ffi.gc(p, destructor)
+ import gc; gc.collect()
+ assert seen == []
+ del q
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [1]
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,14 @@
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")
+
+ def test_cast_to_array_type(self):
+ py.test.skip("ctypes backend: not supported: casting to array")
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -112,8 +112,8 @@
assert res == b'hello\n world\n'
def test_fputs(self):
- if sys.platform == 'win32':
- py.test.skip("no 'stderr'")
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stdout' in the lib")
ffi = FFI(backend=self.Backend())
ffi.cdef("""
int fputs(const char *, void *);
@@ -145,14 +145,16 @@
ffi.C.fflush(ffi.NULL)
res = fd.getvalue()
if sys.platform == 'win32':
- NIL = b"00000000"
+ NIL = "00000000"
+ elif sys.platform.startswith('linux'):
+ NIL = "(nil)"
else:
- NIL = b"(nil)"
- assert res == (b"hello with no arguments\n"
- b"hello, world!\n"
- b"hello, world2!\n"
- b"hello int 42 long 84 long long 168\n"
- b"hello " + NIL + b"\n")
+ NIL = "0x0" # OS/X at least
+ assert res == bytes("hello with no arguments\n"
+ "hello, world!\n"
+ "hello, world2!\n"
+ "hello int 42 long 84 long long 168\n"
+ "hello " + NIL + "\n")
def test_must_specify_type_of_vararg(self):
ffi = FFI(backend=self.Backend())
@@ -230,8 +232,8 @@
assert res == 5
def test_write_variable(self):
- if sys.platform == 'win32':
- py.test.skip("no 'stdout'")
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stdout' in the lib")
ffi = FFI(backend=self.Backend())
ffi.cdef("""
int puts(const char *);
@@ -259,7 +261,7 @@
ffi.C = ffi.dlopen(None)
p = ffi.new("char[]", b"hello world!")
q = ffi.C.strchr(p, ord('w'))
- assert q.value == b"world!"
+ assert ffi.string(q) == b"world!"
def test_function_with_struct_argument(self):
if sys.platform == 'win32':
@@ -272,4 +274,15 @@
ffi.C = ffi.dlopen(None)
ina = ffi.new("struct in_addr *", [0x04040404])
a = ffi.C.inet_ntoa(ina[0])
- assert a.value == b'4.4.4.4'
+ assert ffi.string(a) == b'4.4.4.4'
+
+ def test_function_typedef(self):
+ py.test.skip("using really obscure C syntax")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef double func_t(double);
+ func_t sin;
+ """)
+ m = ffi.dlopen("m")
+ x = m.sin(1.23)
+ assert x == math.sin(1.23)
diff --git a/testing/test_ownlib.py b/testing/test_ownlib.py
--- a/testing/test_ownlib.py
+++ b/testing/test_ownlib.py
@@ -15,6 +15,8 @@
int test_setting_errno(void) {
return errno;
}
+
+int my_array[7] = {0, 1, 2, 3, 4, 5, 6};
"""
class TestOwnLib(object):
@@ -56,3 +58,43 @@
res = ownlib.test_setting_errno()
assert res == 42
assert ffi.errno == 42
+
+ def test_my_array_7(self):
+ if sys.platform == 'win32':
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int my_array[7];
+ """)
+ ownlib = ffi.dlopen(self.module)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+ assert len(ownlib.my_array) == 7
+ if self.Backend is CTypesBackend:
+ py.test.skip("not supported by the ctypes backend")
+ ownlib.my_array = range(10, 17)
+ for i in range(7):
+ assert ownlib.my_array[i] == 10 + i
+ ownlib.my_array = range(7)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+
+ def test_my_array_no_length(self):
+ if sys.platform == 'win32':
+ 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")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int my_array[];
+ """)
+ ownlib = ffi.dlopen(self.module)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+ py.test.raises(TypeError, len, ownlib.my_array)
+ ownlib.my_array = range(10, 17)
+ for i in range(7):
+ assert ownlib.my_array[i] == 10 + i
+ ownlib.my_array = range(7)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -15,10 +15,39 @@
cffi.verifier.cleanup_tmpdir()
-def test_missing_function():
+def test_module_type():
+ import cffi.verifier
ffi = FFI()
+ lib = ffi.verify()
+ if hasattr(lib, '_cffi_python_module'):
+ print 'verify got a PYTHON module'
+ if hasattr(lib, '_cffi_generic_module'):
+ print 'verify got a GENERIC module'
+ expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or
+ '__pypy__' in sys.builtin_module_names)
+ assert hasattr(lib, '_cffi_python_module') == (not expected_generic)
+ assert hasattr(lib, '_cffi_generic_module') == expected_generic
+
+def test_missing_function(ffi=None):
+ # uses the FFI hacked above with '-Werror'
+ if ffi is None:
+ ffi = FFI()
ffi.cdef("void some_completely_unknown_function();")
- py.test.raises(VerificationError, ffi.verify)
+ try:
+ lib = ffi.verify()
+ except VerificationError:
+ pass # expected case: we get a VerificationError
+ else:
+ # but depending on compiler and loader details, maybe
+ # 'lib' could actually be imported but will fail if we
+ # actually try to call the unknown function... Hard
+ # to test anything more.
+ pass
+
+def test_missing_function_import_error():
+ # uses the original FFI that just gives a warning during compilation
+ import cffi
+ test_missing_function(ffi=cffi.FFI())
def test_simple_case():
ffi = FFI()
@@ -102,6 +131,20 @@
else:
assert lib.foo(value) == value + 1
+def test_nonstandard_integer_types():
+ ffi = FFI()
+ lst = ffi._backend.nonstandard_integer_types().items()
+ lst.sort()
+ verify_lines = []
+ for key, value in lst:
+ ffi.cdef("static const int expected_%s;" % key)
+ verify_lines.append("static const int expected_%s =" % key)
+ verify_lines.append(" sizeof(%s) | (((%s)-1) <= 0 ? 0 : 0x1000);"
+ % (key, key))
+ lib = ffi.verify('\n'.join(verify_lines))
+ for key, value in lst:
+ assert getattr(lib, 'expected_%s' % key) == value
+
def test_char_type():
ffi = FFI()
ffi.cdef("char foo(char);")
@@ -339,7 +382,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 lib.PP.value == b"testing!"
+ assert ffi.string(lib.PP) == b"testing!"
def test_nonfull_enum():
ffi = FFI()
@@ -415,16 +458,26 @@
assert lib.somenumber == 42
lib.somenumber = 2 # reset for the next run, if any
-def test_access_array_variable():
+def test_access_array_variable(length=5):
ffi = FFI()
ffi.cdef("int foo(int);\n"
- "int somenumber[5];")
+ "int somenumber[%s];" % (length,))
lib = ffi.verify("""
static int somenumber[] = {2, 2, 3, 4, 5};
static int foo(int i) {
return somenumber[i] * 7;
}
""")
+ if length == '':
+ # a global variable of an unknown array length is implicitly
+ # transformed into a global pointer variable, because we can only
+ # work with array instances whose length we know. using a pointer
+ # instead of an array gives the correct effects.
+ assert repr(lib.somenumber).startswith("<cdata 'int *' 0x")
+ py.test.raises(TypeError, len, lib.somenumber)
+ else:
+ assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length)
+ assert len(lib.somenumber) == 5
assert lib.somenumber[3] == 4
assert lib.foo(3) == 28
lib.somenumber[3] = -6
@@ -433,6 +486,9 @@
assert lib.somenumber[4] == 5
lib.somenumber[3] = 4 # reset for the next run, if any
+def test_access_array_variable_length_hidden():
+ test_access_array_variable(length='')
+
def test_access_struct_variable():
ffi = FFI()
ffi.cdef("struct foo { int x; ...; };\n"
@@ -476,11 +532,32 @@
lib.cb = my_callback
assert lib.foo(4) == 887
-def test_cannot_verify_with_ctypes():
+def test_access_callback_function_typedef():
+ ffi = FFI()
+ ffi.cdef("typedef int mycallback_t(int);\n"
+ "mycallback_t *cb;\n"
+ "int foo(int);\n"
+ "void reset_cb(void);")
+ lib = ffi.verify("""
+ static int g(int x) { return x * 7; }
+ static int (*cb)(int);
+ static int foo(int i) { return cb(i) - 1; }
+ static void reset_cb(void) { cb = g; }
+ """)
+ lib.reset_cb()
+ assert lib.foo(6) == 41
+ my_callback = ffi.callback("int(*)(int)", lambda n: n * 222)
+ lib.cb = my_callback
+ assert lib.foo(4) == 887
+
+def test_ctypes_backend_forces_generic_engine():
from cffi.backend_ctypes import CTypesBackend
ffi = FFI(backend=CTypesBackend())
- ffi.cdef("int a;")
- py.test.raises(NotImplementedError, ffi.verify, "int a;")
+ ffi.cdef("int func(int a);")
+ lib = ffi.verify("int func(int a) { return a * 42; }")
+ assert not hasattr(lib, '_cffi_python_module')
+ assert hasattr(lib, '_cffi_generic_module')
+ assert lib.func(100) == 4200
def test_call_with_struct_ptr():
ffi = FFI()
@@ -635,7 +712,7 @@
""")
foochar = ffi.cast("char *(*)(void)", lib.fooptr)
s = foochar()
- assert s.value == b"foobar"
+ assert ffi.string(s) == b"foobar"
def test_funcptr_as_argument():
ffi = FFI()
@@ -709,12 +786,12 @@
# anyway. XXX think about something better :-(
ffi = FFI()
ffi.cdef("""
- typedef struct { ...; } handle_t;
- handle_t foo(void);
+ typedef struct { ...; } myhandle_t;
+ myhandle_t foo(void);
""")
lib = ffi.verify("""
- typedef short handle_t;
- handle_t foo(void) { return 42; }
+ typedef short myhandle_t;
+ myhandle_t foo(void) { return 42; }
""")
h = lib.foo()
assert ffi.sizeof(h) == ffi.sizeof("short")
diff --git a/testing/test_vgen.py b/testing/test_vgen.py
new file mode 100644
--- /dev/null
+++ b/testing/test_vgen.py
@@ -0,0 +1,12 @@
+import cffi.verifier
+from .test_verify import *
+
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir()
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+ # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we
+ # also test vengine_gen.py.
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/test_vgen2.py b/testing/test_vgen2.py
new file mode 100644
--- /dev/null
+++ b/testing/test_vgen2.py
@@ -0,0 +1,13 @@
+import cffi.verifier
+from .test_vgen import *
+
+# This test file runs normally after test_vgen. We only clean up the .c
+# sources, to check that it also works when we have only the .so. The
+# tests should run much faster than test_vgen.
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir(keep_so=True)
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/test_zdistutils.py b/testing/test_zdistutils.py
--- a/testing/test_zdistutils.py
+++ b/testing/test_zdistutils.py
@@ -1,137 +1,164 @@
-import os, imp, math, random
+import sys, os, imp, math, random
import py
from cffi import FFI, FFIError
-from cffi.verifier import Verifier
+from cffi.verifier import Verifier, _locate_engine_class
from testing.udir import udir
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
+class DistUtilsTest(object):
-def test_write_source():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.write_source()
- with open(v.sourcefilename, 'r') as f:
- data = f.read()
- assert csrc in data
+ def test_locate_engine_class(self):
+ cls = _locate_engine_class(FFI(), self.generic)
+ if self.generic:
+ # asked for the generic engine, which must not generate a
+ # CPython extension module
+ assert not cls._gen_python_module
+ else:
+ # asked for the CPython engine: check that we got it, unless
+ # we are running on top of PyPy, where the generic engine is
+ # always better
+ if '__pypy__' not in sys.builtin_module_names:
+ assert cls._gen_python_module
-def test_write_source_explicit_filename():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.sourcefilename = filename = str(udir.join('write_source.c'))
- v.write_source()
- assert filename == v.sourcefilename
- with open(filename, 'r') as f:
- data = f.read()
- assert csrc in data
-
-def test_write_source_to_file_obj():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- f = StringIO()
- v.write_source(file=f)
- assert csrc in f.getvalue()
-
-def test_compile_module():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.compile_module()
- assert v.get_module_name().startswith('_cffi_')
- mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
- assert hasattr(mod, '_cffi_setup')
-
-def test_compile_module_explicit_filename():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!2*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.modulefilename = filename = str(udir.join('test_compile_module.so'))
- v.compile_module()
- assert filename == v.modulefilename
- assert v.get_module_name() == 'test_compile_module'
- mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
- assert hasattr(mod, '_cffi_setup')
-
-def test_name_from_md5_of_cdef():
- names = []
- for csrc in ['double', 'double', 'float']:
- ffi = FFI()
- ffi.cdef("%s sin(double x);" % csrc)
- v = Verifier(ffi, "#include <math.h>")
- names.append(v.get_module_name())
- assert names[0] == names[1] != names[2]
-
-def test_name_from_md5_of_csrc():
- names = []
- for csrc in ['123', '123', '1234']:
+ def test_write_source(self):
ffi = FFI()
ffi.cdef("double sin(double x);")
- v = Verifier(ffi, csrc)
- names.append(v.get_module_name())
- assert names[0] == names[1] != names[2]
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.write_source()
+ with open(v.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
-def test_load_library():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!3*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- library = v.load_library()
- assert library.sin(12.3) == math.sin(12.3)
+ def test_write_source_explicit_filename(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.sourcefilename = filename = str(udir.join('write_source.c'))
+ v.write_source()
+ assert filename == v.sourcefilename
+ with open(filename, 'r') as f:
+ data = f.read()
+ assert csrc in data
-def test_verifier_args():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!4*/#include "test_verifier_args.h"\n'
- udir.join('test_verifier_args.h').write('#include <math.h>\n')
- v = Verifier(ffi, csrc, include_dirs=[str(udir)])
- library = v.load_library()
- assert library.sin(12.3) == math.sin(12.3)
+ def test_write_source_to_file_obj(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
+ f = StringIO()
+ v.write_source(file=f)
+ assert csrc in f.getvalue()
-def test_verifier_object_from_ffi():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = "/*6*/\n#include <math.h>"
- lib = ffi.verify(csrc)
- assert lib.sin(12.3) == math.sin(12.3)
- assert isinstance(ffi.verifier, Verifier)
- with open(ffi.verifier.sourcefilename, 'r') as f:
- data = f.read()
- assert csrc in data
+ def test_compile_module(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.compile_module()
+ assert v.get_module_name().startswith('_cffi_')
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
-def test_extension_object():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '''/*7*/
-#include <math.h>
-#ifndef TEST_EXTENSION_OBJECT
-# error "define_macros missing"
-#endif
-'''
- lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')])
- assert lib.sin(12.3) == math.sin(12.3)
- v = ffi.verifier
- ext = v.get_extension()
- assert str(ext.__class__) == 'distutils.extension.Extension'
- assert ext.sources == [v.sourcefilename]
- assert ext.name == v.get_module_name()
- assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
+ def test_compile_module_explicit_filename(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!2*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ basename = self.__class__.__name__ + 'test_compile_module'
+ v.modulefilename = filename = str(udir.join(basename + '.so'))
+ v.compile_module()
+ assert filename == v.modulefilename
+ assert v.get_module_name() == basename
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
-def test_extension_forces_write_source():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!%r*/\n#include <math.h>\n' % random.random()
- v = Verifier(ffi, csrc)
- assert not os.path.exists(v.sourcefilename)
- v.get_extension()
- assert os.path.exists(v.sourcefilename)
+ def test_name_from_checksum_of_cdef(self):
+ names = []
+ for csrc in ['double', 'double', 'float']:
+ ffi = FFI()
+ ffi.cdef("%s sin(double x);" % csrc)
+ v = Verifier(ffi, "#include <math.h>",
+ force_generic_engine=self.generic)
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_name_from_checksum_of_csrc(self):
+ names = []
+ for csrc in ['123', '123', '1234']:
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_load_library(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!3*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_args(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!4*/#include "test_verifier_args.h"\n'
+ udir.join('test_verifier_args.h').write('#include <math.h>\n')
+ v = Verifier(ffi, csrc, include_dirs=[str(udir)],
+ force_generic_engine=self.generic)
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_object_from_ffi(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = "/*6*/\n#include <math.h>"
+ lib = ffi.verify(csrc, force_generic_engine=self.generic)
+ assert lib.sin(12.3) == math.sin(12.3)
+ assert isinstance(ffi.verifier, Verifier)
+ with open(ffi.verifier.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_extension_object(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '''/*7*/
+ #include <math.h>
+ #ifndef TEST_EXTENSION_OBJECT
+ # error "define_macros missing"
+ #endif
+ '''
+ lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')],
+ force_generic_engine=self.generic)
+ assert lib.sin(12.3) == math.sin(12.3)
+ v = ffi.verifier
+ ext = v.get_extension()
+ assert str(ext.__class__) == 'distutils.extension.Extension'
+ assert ext.sources == [v.sourcefilename]
+ assert ext.name == v.get_module_name()
+ assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
+
+ def test_extension_forces_write_source(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!%r*/\n#include <math.h>\n' % random.random()
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ assert not os.path.exists(v.sourcefilename)
+ v.get_extension()
+ assert os.path.exists(v.sourcefilename)
+
+
+class TestDistUtilsCPython(DistUtilsTest):
+ generic = False
+
+class TestDistUtilsGeneric(DistUtilsTest):
+ generic = True
More information about the pypy-commit
mailing list