Author: Armin Rigo <[email protected]>
Branch: release-1.1
Changeset: r2165:8636109593c6
Date: 2015-06-07 14:33 +0200
http://bitbucket.org/cffi/cffi/changeset/8636109593c6/
Log: hg merge default
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5158,12 +5158,14 @@
return PyText_FromStringAndSize(s, namelen + replacelen);
}
-static PyObject *b_string(PyObject *self, PyObject *args)
+static PyObject *b_string(PyObject *self, PyObject *args, PyObject *kwds)
{
CDataObject *cd;
Py_ssize_t maxlen = -1;
- if (!PyArg_ParseTuple(args, "O!|n:string",
- &CData_Type, &cd, &maxlen))
+ static char *keywords[] = {"cdata", "maxlen", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:string", keywords,
+ &CData_Type, &cd, &maxlen))
return NULL;
if (cd->c_type->ct_itemdescr != NULL &&
@@ -5246,12 +5248,14 @@
return NULL;
}
-static PyObject *b_buffer(PyObject *self, PyObject *args)
+static PyObject *b_buffer(PyObject *self, PyObject *args, PyObject *kwds)
{
CDataObject *cd;
Py_ssize_t size = -1;
- if (!PyArg_ParseTuple(args, "O!|n:buffer",
- &CData_Type, &cd, &size))
+ static char *keywords[] = {"cdata", "size", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords,
+ &CData_Type, &cd, &size))
return NULL;
if (cd->c_type->ct_flags & CT_POINTER) {
@@ -5351,6 +5355,12 @@
return NULL;
}
x = (PyObject *)(raw + 42);
+ if (Py_REFCNT(x) <= 0) {
+ Py_FatalError("ffi.from_handle() detected that the address passed "
+ "points to garbage. If it is really the result of "
+ "ffi.new_handle(), then the Python object has already "
+ "been garbage collected");
+ }
Py_INCREF(x);
return x;
}
@@ -5790,15 +5800,15 @@
{"typeoffsetof", b_typeoffsetof, METH_VARARGS},
{"rawaddressof", b_rawaddressof, METH_VARARGS},
{"getcname", b_getcname, METH_VARARGS},
- {"string", b_string, METH_VARARGS},
- {"buffer", b_buffer, METH_VARARGS},
+ {"string", (PyCFunction)b_string, METH_VARARGS | METH_KEYWORDS},
+ {"buffer", (PyCFunction)b_buffer, METH_VARARGS | METH_KEYWORDS},
{"get_errno", b_get_errno, METH_NOARGS},
{"set_errno", b_set_errno, METH_O},
{"newp_handle", b_newp_handle, METH_VARARGS},
{"from_handle", b_from_handle, METH_O},
{"from_buffer", b_from_buffer, METH_VARARGS},
#ifdef MS_WIN32
- {"getwinerror", b_getwinerror, METH_VARARGS},
+ {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS},
#endif
{"_get_types", b__get_types, METH_NOARGS},
{"_testfunc", b__testfunc, METH_VARARGS},
diff --git a/c/cgc.c b/c/cgc.c
--- a/c/cgc.c
+++ b/c/cgc.c
@@ -2,79 +2,121 @@
/* translated to C from cffi/gc_weakref.py */
-static PyObject *const_name_pop;
+static PyObject *gc_wref_remove(PyObject *ffi_wref_tup, PyObject *key)
+{
+ FFIObject *ffi;
+ PyObject *indexobj, *destructor, *cdata, *freelist, *result;
+ Py_ssize_t index;
-static PyObject *gc_wref_remove(PyObject *ffi_wref_data, PyObject *arg)
-{
- PyObject *destructor, *cdata, *x;
- PyObject *res = PyObject_CallMethodObjArgs(ffi_wref_data,
- const_name_pop, arg, NULL);
- if (res == NULL)
- return NULL;
+ /* here, tup is a 4-tuple (ffi, destructor, cdata, index) */
+ if (!PyTuple_Check(ffi_wref_tup))
+ goto oops; /* should never occur */
- assert(PyTuple_Check(res));
- destructor = PyTuple_GET_ITEM(res, 0);
- cdata = PyTuple_GET_ITEM(res, 1);
- x = PyObject_CallFunctionObjArgs(destructor, cdata, NULL);
- Py_DECREF(res);
- if (x == NULL)
- return NULL;
- Py_DECREF(x);
+ ffi = (FFIObject *)PyTuple_GET_ITEM(ffi_wref_tup, 0);
+ destructor = PyTuple_GET_ITEM(ffi_wref_tup, 1);
+ cdata = PyTuple_GET_ITEM(ffi_wref_tup, 2);
+ indexobj = PyTuple_GET_ITEM(ffi_wref_tup, 3);
- Py_INCREF(Py_None);
- return Py_None;
+ index = PyInt_AsSsize_t(indexobj);
+ if (index < 0)
+ goto oops; /* should never occur */
+
+ /* assert gc_wrefs[index] is key */
+ if (PyList_GET_ITEM(ffi->gc_wrefs, index) != key)
+ goto oops; /* should never occur */
+
+ /* gc_wrefs[index] = freelist */
+ /* transfer ownership of 'freelist' to 'gc_wrefs[index]' */
+ freelist = ffi->gc_wrefs_freelist;
+ PyList_SET_ITEM(ffi->gc_wrefs, index, freelist);
+
+ /* freelist = index */
+ ffi->gc_wrefs_freelist = indexobj;
+ Py_INCREF(indexobj);
+
+ /* destructor(cdata) */
+ result = PyObject_CallFunctionObjArgs(destructor, cdata, NULL);
+
+ Py_DECREF(key); /* free the reference that was in 'gc_wrefs[index]' */
+ return result;
+
+ oops:
+ PyErr_SetString(PyExc_SystemError, "cgc: internal inconsistency");
+ /* random leaks may follow */
+ return NULL;
}
static PyMethodDef remove_callback = {
"gc_wref_remove", (PyCFunction)gc_wref_remove, METH_O
};
-static PyObject *gc_weakrefs_build(FFIObject *ffi, CDataObject *cd,
+static PyObject *gc_weakrefs_build(FFIObject *ffi, CDataObject *cdata,
PyObject *destructor)
{
- PyObject *new_cdata, *ref = NULL, *tup = NULL;
+ PyObject *new_cdata, *ref = NULL, *tup = NULL, *remove_fn = NULL;
+ Py_ssize_t index;
+ PyObject *datalist;
if (ffi->gc_wrefs == NULL) {
/* initialize */
- PyObject *data;
-
- if (const_name_pop == NULL) {
- const_name_pop = PyText_InternFromString("pop");
- if (const_name_pop == NULL)
- return NULL;
- }
- data = PyDict_New();
- if (data == NULL)
+ datalist = PyList_New(0);
+ if (datalist == NULL)
return NULL;
- ffi->gc_wrefs = PyCFunction_New(&remove_callback, data);
- Py_DECREF(data);
- if (ffi->gc_wrefs == NULL)
- return NULL;
+ ffi->gc_wrefs = datalist;
+ assert(ffi->gc_wrefs_freelist == NULL);
+ ffi->gc_wrefs_freelist = Py_None;
+ Py_INCREF(Py_None);
}
- new_cdata = do_cast(cd->c_type, (PyObject *)cd);
+ /* new_cdata = self.ffi.cast(typeof(cdata), cdata) */
+ new_cdata = do_cast(cdata->c_type, (PyObject *)cdata);
if (new_cdata == NULL)
goto error;
- ref = PyWeakref_NewRef(new_cdata, ffi->gc_wrefs);
+ /* if freelist is None: */
+ datalist = ffi->gc_wrefs;
+ if (ffi->gc_wrefs_freelist == Py_None) {
+ /* index = len(gc_wrefs) */
+ index = PyList_GET_SIZE(datalist);
+ /* gc_wrefs.append(None) */
+ if (PyList_Append(datalist, Py_None) < 0)
+ goto error;
+ tup = Py_BuildValue("OOOn", ffi, destructor, cdata, index);
+ }
+ else {
+ /* index = freelist */
+ index = PyInt_AsSsize_t(ffi->gc_wrefs_freelist);
+ if (index < 0)
+ goto error; /* should not occur */
+ tup = PyTuple_Pack(4, ffi, destructor, cdata, ffi->gc_wrefs_freelist);
+ }
+ if (tup == NULL)
+ goto error;
+
+ remove_fn = PyCFunction_New(&remove_callback, tup);
+ if (remove_fn == NULL)
+ goto error;
+
+ ref = PyWeakref_NewRef(new_cdata, remove_fn);
if (ref == NULL)
goto error;
- tup = PyTuple_Pack(2, destructor, cd);
- if (tup == NULL)
- goto error;
+ /* freelist = gc_wrefs[index] (which is None if we just did append(None))
*/
+ /* transfer ownership of 'datalist[index]' into gc_wrefs_freelist */
+ Py_DECREF(ffi->gc_wrefs_freelist);
+ ffi->gc_wrefs_freelist = PyList_GET_ITEM(datalist, index);
+ /* gc_wrefs[index] = ref */
+ /* transfer ownership of 'ref' into 'datalist[index]' */
+ PyList_SET_ITEM(datalist, index, ref);
+ Py_DECREF(remove_fn);
+ Py_DECREF(tup);
- /* the 'self' of the function 'gc_wrefs' is actually the data dict */
- if (PyDict_SetItem(PyCFunction_GET_SELF(ffi->gc_wrefs), ref, tup) < 0)
- goto error;
-
- Py_DECREF(tup);
- Py_DECREF(ref);
return new_cdata;
error:
Py_XDECREF(new_cdata);
Py_XDECREF(ref);
Py_XDECREF(tup);
+ Py_XDECREF(remove_fn);
return NULL;
}
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -23,7 +23,7 @@
struct FFIObject_s {
PyObject_HEAD
- PyObject *gc_wrefs;
+ PyObject *gc_wrefs, *gc_wrefs_freelist;
struct _cffi_parse_info_s info;
char ctx_is_static, ctx_is_nonempty;
builder_c_t types_builder;
@@ -51,6 +51,7 @@
return NULL;
}
ffi->gc_wrefs = NULL;
+ ffi->gc_wrefs_freelist = NULL;
ffi->info.ctx = &ffi->types_builder.ctx;
ffi->info.output = internal_output;
ffi->info.output_size = FFI_COMPLEXITY_OUTPUT;
@@ -63,6 +64,7 @@
{
PyObject_GC_UnTrack(ffi);
Py_XDECREF(ffi->gc_wrefs);
+ Py_XDECREF(ffi->gc_wrefs_freelist);
free_builder_c(&ffi->types_builder, ffi->ctx_is_static);
@@ -140,6 +142,38 @@
#define ACCEPT_ALL (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA)
#define CONSIDER_FN_AS_FNPTR 8
+static CTypeDescrObject *_ffi_bad_type(FFIObject *ffi, char *input_text)
+{
+ size_t length = strlen(input_text);
+ char *extra;
+
+ if (length > 500) {
+ extra = "";
+ }
+ else {
+ char *p;
+ size_t i, num_spaces = ffi->info.error_location;
+ extra = alloca(length + num_spaces + 4);
+ p = extra;
+ *p++ = '\n';
+ for (i = 0; i < length; i++) {
+ if (' ' <= input_text[i] && input_text[i] < 0x7f)
+ *p++ = input_text[i];
+ else if (input_text[i] == '\t' || input_text[i] == '\n')
+ *p++ = ' ';
+ else
+ *p++ = '?';
+ }
+ *p++ = '\n';
+ memset(p, ' ', num_spaces);
+ p += num_spaces;
+ *p++ = '^';
+ *p++ = 0;
+ }
+ PyErr_Format(FFIError, "%s%s", ffi->info.error_message, extra);
+ return NULL;
+}
+
static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg,
int accept)
{
@@ -153,15 +187,9 @@
if (x == NULL) {
char *input_text = PyText_AS_UTF8(arg);
int err, index = parse_c_type(&ffi->info, input_text);
- if (index < 0) {
- size_t num_spaces = ffi->info.error_location;
- char *spaces = alloca(num_spaces + 1);
- memset(spaces, ' ', num_spaces);
- spaces[num_spaces] = '\0';
- PyErr_Format(FFIError, "%s\n%s\n%s^", ffi->info.error_message,
- input_text, spaces);
- return NULL;
- }
+ if (index < 0)
+ return _ffi_bad_type(ffi, input_text);
+
x = realize_c_type_or_func(&ffi->types_builder,
ffi->info.output, index);
if (x == NULL)
@@ -774,7 +802,7 @@
static PyMethodDef ffi_methods[] = {
{"addressof", (PyCFunction)ffi_addressof, METH_VARARGS, ffi_addressof_doc},
{"alignof", (PyCFunction)ffi_alignof, METH_O, ffi_alignof_doc},
- {"buffer", (PyCFunction)ffi_buffer, METH_VARARGS, ffi_buffer_doc},
+ {"buffer", (PyCFunction)ffi_buffer, METH_VKW, ffi_buffer_doc},
{"callback", (PyCFunction)ffi_callback, METH_VKW, ffi_callback_doc},
{"cast", (PyCFunction)ffi_cast, METH_VARARGS, ffi_cast_doc},
{"dlclose", (PyCFunction)ffi_dlclose, METH_VARARGS, ffi_dlclose_doc},
@@ -784,14 +812,14 @@
{"gc", (PyCFunction)ffi_gc, METH_VKW, ffi_gc_doc},
{"getctype", (PyCFunction)ffi_getctype, METH_VKW, ffi_getctype_doc},
#ifdef MS_WIN32
- {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VARARGS,
ffi_getwinerror_doc},
+ {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW,
ffi_getwinerror_doc},
#endif
{"integer_const",(PyCFunction)ffi_int_const,METH_VKW, ffi_int_const_doc},
{"new", (PyCFunction)ffi_new, METH_VKW, ffi_new_doc},
{"new_handle", (PyCFunction)ffi_new_handle, METH_O, ffi_new_handle_doc},
{"offsetof", (PyCFunction)ffi_offsetof, METH_VARARGS, ffi_offsetof_doc},
{"sizeof", (PyCFunction)ffi_sizeof, METH_O, ffi_sizeof_doc},
- {"string", (PyCFunction)ffi_string, METH_VARARGS, ffi_string_doc},
+ {"string", (PyCFunction)ffi_string, METH_VKW, ffi_string_doc},
{"typeof", (PyCFunction)ffi_typeof, METH_O, ffi_typeof_doc},
{NULL}
};
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -82,14 +82,15 @@
}
#if PY_MAJOR_VERSION >= 3
-static PyObject *b_getwinerror(PyObject *self, PyObject *args)
+static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
{
int err = -1;
int len;
WCHAR *s_buf = NULL; /* Free via LocalFree */
PyObject *v, *message;
+ static char *keywords[] = {"code", NULL};
- if (!PyArg_ParseTuple(args, "|i", &err))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err))
return NULL;
if (err == -1) {
@@ -129,7 +130,7 @@
return v;
}
#else
-static PyObject *b_getwinerror(PyObject *self, PyObject *args)
+static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
{
int err = -1;
int len;
@@ -137,8 +138,9 @@
char *s_buf = NULL; /* Free via LocalFree */
char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
PyObject *v;
+ static char *keywords[] = {"code", NULL};
- if (!PyArg_ParseTuple(args, "|i", &err))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err))
return NULL;
if (err == -1) {
diff --git a/cffi/gc_weakref.py b/cffi/gc_weakref.py
--- a/cffi/gc_weakref.py
+++ b/cffi/gc_weakref.py
@@ -2,18 +2,27 @@
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
+ self.data = []
+ self.freelist = None
def build(self, cdata, destructor):
# make a new cdata of the same type as the original one
new_cdata = self.ffi.cast(self.ffi._backend.typeof(cdata), cdata)
- self.data[ref(new_cdata, self.remove)] = destructor, cdata
+ #
+ def remove(key):
+ assert self.data[index] is key
+ self.data[index] = self.freelist
+ self.freelist = index
+ destructor(cdata)
+ #
+ key = ref(new_cdata, remove)
+ index = self.freelist
+ if index is None:
+ index = len(self.data)
+ self.data.append(key)
+ else:
+ self.freelist = self.data[index]
+ self.data[index] = key
return new_cdata
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -775,7 +775,8 @@
try:
if ftype.is_integer_type() or fbitsize >= 0:
# accept all integers, but complain on float or double
- prnt(' (void)((p->%s) << 1);' % fname)
+ prnt(" (void)((p->%s) << 1); /* check that '%s.%s' is "
+ "an integer */" % (fname, cname, fname))
continue
# only accept exactly the type declared, except that '[]'
# is interpreted as a '*' and so will match any array length.
@@ -949,7 +950,7 @@
prnt('{')
prnt(' int n = (%s) <= 0;' % (name,))
prnt(' *o = (unsigned long long)((%s) << 0);'
- ' /* check that we get an integer */' % (name,))
+ ' /* check that %s is an integer */' % (name, name))
if check_value is not None:
if check_value > 0:
check_value = '%dU' % (check_value,)
@@ -1088,8 +1089,9 @@
self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
def _emit_bytecode_UnknownIntegerType(self, tp, index):
- s = '_cffi_prim_int(sizeof(%s), (((%s)-1) << 0) <= 0)' % (
- tp.name, tp.name)
+ s = ('_cffi_prim_int(sizeof(%s), (\n'
+ ' ((%s)-1) << 0 /* check that %s is an integer type
*/\n'
+ ' ) <= 0)' % (tp.name, tp.name, tp.name))
self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
def _emit_bytecode_RawFunctionType(self, tp, index):
diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py
--- a/cffi/setuptools_ext.py
+++ b/cffi/setuptools_ext.py
@@ -18,7 +18,9 @@
# __init__.py files may already try to import the file that
# we are generating.
with open(filename) as f:
- code = compile(f.read(), filename, 'exec')
+ src = f.read()
+ src += '\n' # Python 2.6 compatibility
+ code = compile(src, filename, 'exec')
exec(code, glob, glob)
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -402,12 +402,16 @@
else:
assert tp is not None
assert check_value is None
- prnt(tp.get_c_name(' %s(void)' % funcname, name),)
- prnt('{')
if category == 'var':
ampersand = '&'
else:
ampersand = ''
+ extra = ''
+ if category == 'const' and isinstance(tp, model.StructOrUnion):
+ extra = 'const *'
+ ampersand = '&'
+ prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name))
+ prnt('{')
prnt(' return (%s%s);' % (ampersand, name))
prnt('}')
prnt()
@@ -436,9 +440,14 @@
value += (1 << (8*self.ffi.sizeof(BLongLong)))
else:
assert check_value is None
- BFunc = self.ffi._typeof_locked(tp.get_c_name('(*)(void)',
name))[0]
+ fntypeextra = '(*)(void)'
+ if isinstance(tp, model.StructOrUnion):
+ fntypeextra = '*' + fntypeextra
+ BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra,
name))[0]
function = module.load_function(BFunc, funcname)
value = function()
+ if isinstance(tp, model.StructOrUnion):
+ value = value[0]
return value
def _loaded_gen_constant(self, tp, name, module, library):
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -278,6 +278,18 @@
needed. (Alternatively, the out-of-line FFIs have a method
``ffi.dlclose(lib)``.)
+.. _dlopen-note:
+
+Note: the old version of ``ffi.dlopen()`` from the in-line ABI mode
+tries to use ``ctypes.util.find_library()`` if it cannot directly find
+the library. The newer out-of-line ``ffi.dlopen()`` no longer does it
+automatically; it simply passes the argument it receives to the
+underlying ``dlopen()`` or ``LoadLibrary()`` function. If needed, it
+is up to you to use ``ctypes.util.find_library()`` or any other way to
+look for the library's filename. This also means that
+``ffi.dlopen(None)`` no longer work on Windows; try instead
+``ffi.dlopen(ctypes.util.find_library('c'))``.
+
ffi.set_source(): preparing out-of-line modules
-----------------------------------------------
@@ -375,7 +387,7 @@
* *New in version 1.1:* integer types: the syntax "``typedef
int... foo_t;``" declares the type ``foo_t`` as an integer type
- whose exact size and signness is not specified. The compiler will
+ whose exact size and signedness is not specified. The compiler will
figure it out. (Note that this requires ``set_source()``; it does
not work with ``verify()``.) The ``int...`` can be replaced with
``long...`` or ``unsigned long long...`` or any other primitive
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -82,9 +82,21 @@
from _simple_example import ffi
- lib = ffi.dlopen(None) # or path to a library
+ lib = ffi.dlopen(None) # Unix: open the standard C library
+ #import ctypes.util # or, try this on Windows:
+ #lib = ffi.dlopen(ctypes.util.find_library("c"))
+
lib.printf(b"hi there, number %d\n", ffi.cast("int", 2))
+Note that this ``ffi.dlopen()``, unlike the one from in-line mode,
+does not invoke any additional magic to locate the library: it must be
+a path name (with or without a directory), as required by the C
+``dlopen()`` or ``LoadLibrary()`` functions. This means that
+``ffi.dlopen("libfoo.so")`` is ok, but ``ffi.dlopen("foo")`` is not.
+In the latter case, you could replace it with
+``ffi.dlopen(ctypes.util.find_library("foo"))``. Also, None is only
+recognized on Unix to open the standard C library.
+
For distribution purposes, remember that there is a new
``_simple_example.py`` file generated. You can either include it
statically within your project's source files, or, with Setuptools,
@@ -202,6 +214,13 @@
.. _struct: http://docs.python.org/library/struct.html
.. _array: http://docs.python.org/library/array.html
+This example also admits an out-of-line equivalent. It is similar to
+`Out-of-line example (ABI level, out-of-line)`_ above, but without any
+call to ``ffi.dlopen()``. In the main program, you write ``from
+_simple_example import ffi`` and then the same content as the in-line
+example above starting from the line ``image = ffi.new("pixel_t[]",
+800*600)``.
+
.. _performance:
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,11 +3,29 @@
======================
+1.1.1
+=====
+
+* Out-of-line mode: ``ffi.string()``, ``ffi.buffer()`` and
+ ``ffi.getwinerror()`` didn't accept their arguments as keyword
+ arguments, unlike their in-line mode equivalent. (It worked in PyPy.)
+
+* Out-of-line ABI mode: documented a restriction__ of ``ffi.dlopen()``
+ when compared to the in-line mode.
+
+* ``ffi.gc()``: when called several times with equal pointers, it was
+ accidentally registering only the last destructor, or even none at
+ all depending on details. (It was correctly registering all of them
+ only in PyPy, and only with the out-of-line FFIs.)
+
+.. __: cdef.html#dlopen-note
+
+
1.1.0
=====
* Out-of-line API mode: we can now declare integer types with
- ``typedef int... foo_t;``. The exact size and signness of ``foo_t``
+ ``typedef int... foo_t;``. The exact size and signedness of ``foo_t``
is figured out by the compiler.
* Out-of-line API mode: we can now declare multidimensional arrays
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -1457,6 +1457,63 @@
import gc; gc.collect(); gc.collect(); gc.collect()
assert seen == [1]
+ def test_gc_2(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ seen = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ import gc; gc.collect()
+ assert seen == []
+ del q1, q2
+ import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+ assert seen == [2, 1]
+
+ def test_gc_3(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ r = ffi.new("int *", 123)
+ seen = []
+ seen_r = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ s1 = ffi.gc(r, lambda r: seen_r.append(4))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ s2 = ffi.gc(s1, lambda r: seen_r.append(5))
+ q3 = ffi.gc(q2, lambda p: seen.append(3))
+ import gc; gc.collect()
+ assert seen == []
+ assert seen_r == []
+ del q1, q2, q3, s2, s1
+ import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+ assert seen == [3, 2, 1]
+ assert seen_r == [5, 4]
+
+ def test_gc_4(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ seen = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ q3 = ffi.gc(q2, lambda p: seen.append(3))
+ import gc; gc.collect()
+ assert seen == []
+ del q1, q3 # q2 remains, and has a hard ref to q1
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [3]
+
+ def test_gc_finite_list(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ keepalive = []
+ for i in range(10):
+ keepalive.append(ffi.gc(p, lambda p: None))
+ assert len(ffi.gc_weakrefs.data) == i + 1 #should be a private
attr
+ del keepalive[:]
+ import gc; gc.collect(); gc.collect()
+ for i in range(10):
+ keepalive.append(ffi.gc(p, lambda p: None))
+ assert len(ffi.gc_weakrefs.data) == 10
+
def test_CData_CType(self):
ffi = FFI(backend=self.Backend())
assert isinstance(ffi.cast("int", 0), ffi.CData)
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
--- a/testing/cffi0/test_verify.py
+++ b/testing/cffi0/test_verify.py
@@ -2227,3 +2227,11 @@
ffi.cdef("static const int FOO = 123;")
e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
assert str(e.value).endswith("FOO has the real value 124, not 123")
+
+def test_const_struct_global():
+ ffi = FFI()
+ ffi.cdef("typedef struct { int x; ...; } T; const T myglob;")
+ lib = ffi.verify("typedef struct { double y; int x; } T;"
+ "const T myglob = { 0.1, 42 };")
+ assert ffi.typeof(lib.myglob) == ffi.typeof("T")
+ assert lib.myglob.x == 42
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
--- a/testing/cffi1/test_ffi_obj.py
+++ b/testing/cffi1/test_ffi_obj.py
@@ -1,4 +1,4 @@
-import py
+import py, sys
import _cffi_backend as _cffi1_backend
@@ -65,6 +65,7 @@
ffi = _cffi1_backend.FFI()
p = ffi.new("char[]", init=b"foobar\x00baz")
assert ffi.string(p) == b"foobar"
+ assert ffi.string(cdata=p, maxlen=3) == b"foo"
def test_ffi_errno():
# xxx not really checking errno, just checking that we can read/write it
@@ -157,11 +158,18 @@
assert str(e.value) == ("undefined struct/union name\n"
"struct never_heard_of_s\n"
" ^")
+ e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0)
+ assert str(e.value) == ("identifier expected\n"
+ " ??~???\n"
+ " ^")
+ e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0)
+ assert str(e.value) == ("undefined type name")
def test_ffi_buffer():
ffi = _cffi1_backend.FFI()
a = ffi.new("signed char[]", [5, 6, 7])
assert ffi.buffer(a)[:] == b'\x05\x06\x07'
+ assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06'
def test_ffi_from_buffer():
import array
@@ -178,3 +186,11 @@
ffi = _cffi1_backend.FFI()
assert isinstance(ffi.cast("int", 42), CData)
assert isinstance(ffi.typeof("int"), CType)
+
+def test_ffi_getwinerror():
+ if sys.platform != "win32":
+ py.test.skip("for windows")
+ ffi = _cffi1_backend.FFI()
+ n = (1 << 29) + 42
+ code, message = ffi.getwinerror(code=n)
+ assert code == n
diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py
--- a/testing/cffi1/test_new_ffi_1.py
+++ b/testing/cffi1/test_new_ffi_1.py
@@ -32,7 +32,9 @@
struct ab { int a, b; };
struct abc { int a, b, c; };
- enum foq { A0, B0, CC0, D0 };
+ /* don't use A0, B0, CC0, D0 because termios.h might be included
+ and it has its own #defines for these names */
+ enum foq { cffiA0, cffiB0, cffiCC0, cffiD0 };
enum bar { A1, B1=-2, CC1, D1, E1 };
enum baz { A2=0x1000, B2=0x2000 };
enum foo2 { A3, B3, C3, D3 };
@@ -878,9 +880,9 @@
def test_enum(self):
# enum foq { A0, B0, CC0, D0 };
- assert ffi.string(ffi.cast("enum foq", 0)) == "A0"
- assert ffi.string(ffi.cast("enum foq", 2)) == "CC0"
- assert ffi.string(ffi.cast("enum foq", 3)) == "D0"
+ assert ffi.string(ffi.cast("enum foq", 0)) == "cffiA0"
+ assert ffi.string(ffi.cast("enum foq", 2)) == "cffiCC0"
+ assert ffi.string(ffi.cast("enum foq", 3)) == "cffiD0"
assert ffi.string(ffi.cast("enum foq", 4)) == "4"
# enum bar { A1, B1=-2, CC1, D1, E1 };
assert ffi.string(ffi.cast("enum bar", 0)) == "A1"
@@ -1407,6 +1409,47 @@
import gc; gc.collect(); gc.collect(); gc.collect()
assert seen == [1]
+ def test_gc_2(self):
+ p = ffi.new("int *", 123)
+ seen = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ import gc; gc.collect()
+ assert seen == []
+ del q1, q2
+ import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+ assert seen == [2, 1]
+
+ def test_gc_3(self):
+ p = ffi.new("int *", 123)
+ r = ffi.new("int *", 123)
+ seen = []
+ seen_r = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ s1 = ffi.gc(r, lambda r: seen_r.append(4))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ s2 = ffi.gc(s1, lambda r: seen_r.append(5))
+ q3 = ffi.gc(q2, lambda p: seen.append(3))
+ import gc; gc.collect()
+ assert seen == []
+ assert seen_r == []
+ del q1, q2, q3, s2, s1
+ import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+ assert seen == [3, 2, 1]
+ assert seen_r == [5, 4]
+
+ def test_gc_4(self):
+ p = ffi.new("int *", 123)
+ seen = []
+ q1 = ffi.gc(p, lambda p: seen.append(1))
+ q2 = ffi.gc(q1, lambda p: seen.append(2))
+ q3 = ffi.gc(q2, lambda p: seen.append(3))
+ import gc; gc.collect()
+ assert seen == []
+ del q1, q3 # q2 remains, and has a hard ref to q1
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [3]
+
def test_CData_CType(self):
assert isinstance(ffi.cast("int", 0), ffi.CData)
assert isinstance(ffi.new("int *"), ffi.CData)
@@ -1533,8 +1576,8 @@
assert p.a == -52525
#
p = ffi.cast("enum foq", 2)
- assert ffi.string(p) == "CC0"
- assert ffi2.sizeof("char[CC0]") == 2
+ assert ffi.string(p) == "cffiCC0"
+ assert ffi2.sizeof("char[cffiCC0]") == 2
#
p = ffi.new("anon_foo_t *", [-52526])
assert p.a == -52526
diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py
--- a/testing/cffi1/test_re_python.py
+++ b/testing/cffi1/test_re_python.py
@@ -7,6 +7,7 @@
def setup_module(mod):
SRC = """
+ #include <string.h>
#define FOOBAR (-42)
static const int FOOBAZ = -43;
#define BIGPOS 420000000000L
@@ -53,6 +54,7 @@
struct foo_s;
typedef struct bar_s { int x; signed char a[]; } bar_t;
enum foo_e { AA, BB, CC };
+ int strlen(const char *);
""")
ffi.set_source('re_python_pysrc', None)
ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py')))
@@ -81,10 +83,20 @@
def test_function_with_varargs():
import _cffi_backend
from re_python_pysrc import ffi
- lib = ffi.dlopen(extmod)
+ lib = ffi.dlopen(extmod, 0)
assert lib.add43(45, ffi.cast("int", -5)) == 45
assert type(lib.add43) is _cffi_backend.FFI.CData
+def test_dlopen_none():
+ import _cffi_backend
+ from re_python_pysrc import ffi
+ name = None
+ if sys.platform == 'win32':
+ import ctypes.util
+ name = ctypes.util.find_msvcrt()
+ lib = ffi.dlopen(name)
+ assert lib.strlen(b"hello") == 5
+
def test_dlclose():
import _cffi_backend
from re_python_pysrc import ffi
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -992,3 +992,13 @@
ffi.typeof('function_t*')
lib.function(ffi.NULL)
# assert did not crash
+
+def test_alignment_of_longlong():
+ ffi = FFI()
+ x1 = ffi.alignof('unsigned long long')
+ assert x1 in [4, 8]
+ ffi.cdef("struct foo_s { unsigned long long x; };")
+ lib = verify(ffi, 'test_alignment_of_longlong',
+ "struct foo_s { unsigned long long x; };")
+ assert ffi.alignof('unsigned long long') == x1
+ assert ffi.alignof('struct foo_s') == x1
diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py
--- a/testing/cffi1/test_verify1.py
+++ b/testing/cffi1/test_verify1.py
@@ -2117,25 +2117,19 @@
try:
ffi1 = FFI()
ffi1.cdef("int foo_verify_dlopen_flags;")
-
- sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY)
+ sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW)
lib1 = ffi1.verify("int foo_verify_dlopen_flags;")
- lib2 = get_second_lib()
-
- lib1.foo_verify_dlopen_flags = 42
- assert lib2.foo_verify_dlopen_flags == 42
- lib2.foo_verify_dlopen_flags += 1
- assert lib1.foo_verify_dlopen_flags == 43
finally:
sys.setdlopenflags(old)
-def get_second_lib():
- # Hack, using modulename makes the test fail
ffi2 = FFI()
- ffi2.cdef("int foo_verify_dlopen_flags;")
- lib2 = ffi2.verify("int foo_verify_dlopen_flags;",
- flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY)
- return lib2
+ ffi2.cdef("int *getptr(void);")
+ lib2 = ffi2.verify("""
+ extern int foo_verify_dlopen_flags;
+ static int *getptr(void) { return &foo_verify_dlopen_flags; }
+ """)
+ p = lib2.getptr()
+ assert ffi1.addressof(lib1, 'foo_verify_dlopen_flags') == p
def test_consider_not_implemented_function_type():
ffi = FFI()
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
--- a/testing/cffi1/test_zdist.py
+++ b/testing/cffi1/test_zdist.py
@@ -29,13 +29,17 @@
if hasattr(self, 'saved_cwd'):
os.chdir(self.saved_cwd)
- def run(self, args):
+ def run(self, args, cwd=None):
env = os.environ.copy()
- newpath = self.rootdir
- if 'PYTHONPATH' in env:
- newpath += os.pathsep + env['PYTHONPATH']
- env['PYTHONPATH'] = newpath
- subprocess.check_call([self.executable] + args, env=env)
+ # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg
+ # (there is the --no-user-cfg option, but not in Python 2.6...)
+ env['HOME'] = '/this/path/does/not/exist'
+ if cwd is None:
+ newpath = self.rootdir
+ if 'PYTHONPATH' in env:
+ newpath += os.pathsep + env['PYTHONPATH']
+ env['PYTHONPATH'] = newpath
+ subprocess.check_call([self.executable] + args, cwd=cwd, env=env)
def _prepare_setuptools(self):
if hasattr(TestDist, '_setuptools_ready'):
@@ -44,8 +48,7 @@
import setuptools
except ImportError:
py.test.skip("setuptools not found")
- subprocess.check_call([self.executable, 'setup.py', 'egg_info'],
- cwd=self.rootdir)
+ self.run(['setup.py', 'egg_info'], cwd=self.rootdir)
TestDist._setuptools_ready = True
def check_produced_files(self, content, curdir=None):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit