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

Reply via email to