Author: Armin Rigo <ar...@tunes.org>
Branch: release-1.11
Changeset: r3108:5ff833c65f91
Date: 2018-02-27 19:12 +0100
http://bitbucket.org/cffi/cffi/changeset/5ff833c65f91/

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
@@ -3794,27 +3794,29 @@
 static int check_bytes_for_float_compatible(PyObject *io, double *out_value)
 {
     if (PyBytes_Check(io)) {
-        if (PyBytes_GET_SIZE(io) != 1) {
-            Py_DECREF(io);
-            return -1;
-        }
+        if (PyBytes_GET_SIZE(io) != 1)
+            goto error;
         *out_value = (unsigned char)PyBytes_AS_STRING(io)[0];
         return 1;
     }
     else if (PyUnicode_Check(io)) {
         char ignored[80];
         cffi_char32_t ordinal;
-        if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0) {
-            Py_DECREF(io);
-            return -1;
-        }
+        if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0)
+            goto error;
         /* the signness of the 32-bit version of wide chars should not
          * matter here, because 'ordinal' comes from a normal Python
          * unicode string */
         *out_value = ordinal;
         return 1;
     }
+    *out_value = 0;   /* silence a gcc warning if this function is inlined */
     return 0;
+
+ error:
+    Py_DECREF(io);
+    *out_value = 0;   /* silence a gcc warning if this function is inlined */
+    return -1;
 }
 
 static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob)
@@ -3982,7 +3984,8 @@
 
 static void dl_dealloc(DynLibObject *dlobj)
 {
-    dlclose(dlobj->dl_handle);
+    if (dlobj->dl_handle != NULL)
+        dlclose(dlobj->dl_handle);
     free(dlobj->dl_name);
     PyObject_Del(dlobj);
 }
@@ -3992,6 +3995,17 @@
     return PyText_FromFormat("<clibrary '%s'>", dlobj->dl_name);
 }
 
+static int dl_check_closed(DynLibObject *dlobj)
+{
+    if (dlobj->dl_handle == NULL)
+    {
+        PyErr_Format(PyExc_ValueError, "library '%s' has already been closed",
+                     dlobj->dl_name);
+        return -1;
+    }
+    return 0;
+}
+
 static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args)
 {
     CTypeDescrObject *ct;
@@ -4002,6 +4016,9 @@
                           &CTypeDescr_Type, &ct, &funcname))
         return NULL;
 
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+
     if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) {
         PyErr_Format(PyExc_TypeError,
                      "function or pointer or array cdata expected, got '%s'",
@@ -4034,6 +4051,9 @@
                           &CTypeDescr_Type, &ct, &varname))
         return NULL;
 
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+
     dlerror();   /* clear error condition */
     data = dlsym(dlobj->dl_handle, varname);
     if (data == NULL) {
@@ -4059,6 +4079,9 @@
                           &CTypeDescr_Type, &ct, &varname, &value))
         return NULL;
 
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+
     dlerror();   /* clear error condition */
     data = dlsym(dlobj->dl_handle, varname);
     if (data == NULL) {
@@ -4074,10 +4097,21 @@
     return Py_None;
 }
 
+static PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args)
+{
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+    dlclose(dlobj->dl_handle);
+    dlobj->dl_handle = NULL;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 static PyMethodDef dl_methods[] = {
     {"load_function",   (PyCFunction)dl_load_function,  METH_VARARGS},
     {"read_variable",   (PyCFunction)dl_read_variable,  METH_VARARGS},
     {"write_variable",  (PyCFunction)dl_write_variable, METH_VARARGS},
+    {"close_lib",       (PyCFunction)dl_close_lib,      METH_NOARGS},
     {NULL,              NULL}           /* sentinel */
 };
 
@@ -4113,44 +4147,95 @@
     dl_methods,                         /* tp_methods */
 };
 
-static PyObject *b_load_library(PyObject *self, PyObject *args)
-{
-    char *filename_or_null, *printable_filename;
+static void *b_do_dlopen(PyObject *args, char **p_printable_filename,
+                         PyObject **p_temp)
+{
+    /* Logic to call the correct version of dlopen().  Returns NULL in case of 
error.
+       Otherwise, '*p_printable_filename' will point to a printable char 
version of
+       the filename (maybe utf-8-encoded).  '*p_temp' will be set either to 
NULL or
+       to a temporary object that must be freed after looking at 
printable_filename.
+    */
     void *handle;
-    DynLibObject *dlobj;
+    char *filename_or_null;
     int flags = 0;
-
+    *p_temp = NULL;
+    
     if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
         PyObject *dummy;
         if (!PyArg_ParseTuple(args, "|Oi:load_library",
                               &dummy, &flags))
             return NULL;
         filename_or_null = NULL;
-    }
-    else if (!PyArg_ParseTuple(args, "et|i:load_library",
-                          Py_FileSystemDefaultEncoding, &filename_or_null,
-                          &flags))
-        return NULL;
-
+        *p_printable_filename = "<None>";
+    }
+    else
+    {
+        PyObject *s = PyTuple_GET_ITEM(args, 0);
+#ifdef MS_WIN32
+        Py_UNICODE *filenameW;
+        if (PyArg_ParseTuple(args, "u|i:load_library", &filenameW, &flags))
+        {
+#if PY_MAJOR_VERSION < 3
+            s = PyUnicode_AsUTF8String(s);
+            if (s == NULL)
+                return NULL;
+            *p_temp = s;
+#endif
+            *p_printable_filename = PyText_AsUTF8(s);
+            if (*p_printable_filename == NULL)
+                return NULL;
+
+            handle = dlopenW(filenameW);
+            goto got_handle;
+        }
+        PyErr_Clear();
+#endif
+        if (!PyArg_ParseTuple(args, "et|i:load_library",
+                     Py_FileSystemDefaultEncoding, &filename_or_null, &flags))
+            return NULL;
+
+        *p_printable_filename = PyText_AsUTF8(s);
+        if (*p_printable_filename == NULL)
+            return NULL;
+    }
     if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
         flags |= RTLD_NOW;
 
-    printable_filename = filename_or_null ? filename_or_null : "<None>";
     handle = dlopen(filename_or_null, flags);
+
+#ifdef MS_WIN32
+  got_handle:
+#endif
     if (handle == NULL) {
         const char *error = dlerror();
-        PyErr_Format(PyExc_OSError, "cannot load library %s: %s",
-                     printable_filename, error);
+        PyErr_Format(PyExc_OSError, "cannot load library '%s': %s",
+                     *p_printable_filename, error);
         return NULL;
     }
+    return handle;
+}
+
+static PyObject *b_load_library(PyObject *self, PyObject *args)
+{
+    char *printable_filename;
+    PyObject *temp;
+    void *handle;
+    DynLibObject *dlobj = NULL;
+
+    handle = b_do_dlopen(args, &printable_filename, &temp);
+    if (handle == NULL)
+        goto error;
 
     dlobj = PyObject_New(DynLibObject, &dl_type);
     if (dlobj == NULL) {
         dlclose(handle);
-        return NULL;
+        goto error;
     }
     dlobj->dl_handle = handle;
     dlobj->dl_name = strdup(printable_filename);
+ 
+ error:
+    Py_XDECREF(temp);
     return (PyObject *)dlobj;
 }
 
@@ -4796,7 +4881,6 @@
             if (PyText_GetSize(fname) == 0 &&
                     ftype->ct_flags & (CT_STRUCT|CT_UNION)) {
                 /* a nested anonymous struct or union */
-                /* note: it seems we only get here with ffi.verify() */
                 CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra;
                 for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) {
                     /* broken complexity in the call to get_field_name(),
diff --git a/c/cdlopen.c b/c/cdlopen.c
--- a/c/cdlopen.c
+++ b/c/cdlopen.c
@@ -40,35 +40,18 @@
 
 static PyObject *ffi_dlopen(PyObject *self, PyObject *args)
 {
-    char *filename_or_null, *printable_filename;
+    char *modname;
+    PyObject *temp, *result = NULL;
     void *handle;
-    int flags = 0;
 
-    if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
-        PyObject *dummy;
-        if (!PyArg_ParseTuple(args, "|Oi:load_library",
-                              &dummy, &flags))
-            return NULL;
-        filename_or_null = NULL;
+    handle = b_do_dlopen(args, &modname, &temp);
+    if (handle != NULL)
+    {
+        result = (PyObject *)lib_internal_new((FFIObject *)self,
+                                              modname, handle);
     }
-    else if (!PyArg_ParseTuple(args, "et|i:load_library",
-                          Py_FileSystemDefaultEncoding, &filename_or_null,
-                          &flags))
-        return NULL;
-
-    if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
-        flags |= RTLD_NOW;
-    printable_filename = filename_or_null ? filename_or_null : "<None>";
-
-    handle = dlopen(filename_or_null, flags);
-    if (handle == NULL) {
-        const char *error = dlerror();
-        PyErr_Format(PyExc_OSError, "cannot load library '%s': %s",
-                     printable_filename, error);
-        return NULL;
-    }
-    return (PyObject *)lib_internal_new((FFIObject *)self,
-                                        printable_filename, handle);
+    Py_XDECREF(temp);
+    return result;
 }
 
 static PyObject *ffi_dlclose(PyObject *self, PyObject *args)
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -192,7 +192,12 @@
 
 static void *dlopen(const char *filename, int flag)
 {
-    return (void *)LoadLibrary(filename);
+    return (void *)LoadLibraryA(filename);
+}
+
+static void *dlopenW(const wchar_t *filename)
+{
+    return (void *)LoadLibraryW(filename);
 }
 
 static void *dlsym(void *handle, const char *symbol)
diff --git a/c/realize_c_type.c b/c/realize_c_type.c
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -737,13 +737,13 @@
                 return -1;
             }
 
-            if (fld->field_offset == (size_t)-1) {
+            if (ctf != NULL && fld->field_offset == (size_t)-1) {
                 /* unnamed struct, with field positions and sizes entirely
                    determined by complete_struct_or_union() and not checked.
                    Or, bitfields (field_size >= 0), similarly not checked. */
                 assert(fld->field_size == (size_t)-1 || fbitsize >= 0);
             }
-            else if (detect_custom_layout(ct, SF_STD_FIELD_POS,
+            else if (ctf == NULL || detect_custom_layout(ct, SF_STD_FIELD_POS,
                                      ctf->ct_size, fld->field_size,
                                      "wrong size for field '",
                                      fld->name, "'") < 0) {
diff --git a/cffi/_embedding.h b/cffi/_embedding.h
--- a/cffi/_embedding.h
+++ b/cffi/_embedding.h
@@ -146,32 +146,6 @@
     PyGILState_STATE state;
     PyObject *pycode=NULL, *global_dict=NULL, *x;
 
-#if PY_MAJOR_VERSION >= 3
-    /* see comments in _cffi_carefully_make_gil() about the
-       Python2/Python3 difference 
-    */
-#else
-    /* Acquire the GIL.  We have no threadstate here.  If Python is 
-       already initialized, it is possible that there is already one
-       existing for this thread, but it is not made current now.
-    */
-    PyEval_AcquireLock();
-
-    _cffi_py_initialize();
-
-    /* The Py_InitializeEx() sometimes made a threadstate for us, but
-       not always.  Indeed Py_InitializeEx() could be called and do
-       nothing.  So do we have a threadstate, or not?  We don't know,
-       but we can replace it with NULL in all cases.
-    */
-    (void)PyThreadState_Swap(NULL);
-
-    /* Now we can release the GIL and re-acquire immediately using the
-       logic of PyGILState(), which handles making or installing the
-       correct threadstate.
-    */
-    PyEval_ReleaseLock();
-#endif
     state = PyGILState_Ensure();
 
     /* Call the initxxx() function from the present module.  It will
@@ -278,16 +252,14 @@
        that we don't hold the GIL before (if it exists), and we don't
        hold it afterwards.
 
-       What it really does is completely different in Python 2 and 
-       Python 3.
+       (What it really does used to be completely different in Python 2
+       and Python 3, with the Python 2 solution avoiding the spin-lock
+       around the Py_InitializeEx() call.  However, after recent changes
+       to CPython 2.7 (issue #358) it no longer works.  So we use the
+       Python 3 solution everywhere.)
 
-    Python 2
-    ========
-
-       Initialize the GIL, without initializing the rest of Python,
-       by calling PyEval_InitThreads().
-
-       PyEval_InitThreads() must not be called concurrently at all.
+       This initializes Python by calling Py_InitializeEx().
+       Important: this must not be called concurrently at all.
        So we use a global variable as a simple spin lock.  This global
        variable must be from 'libpythonX.Y.so', not from this
        cffi-based extension module, because it must be shared from
@@ -297,18 +269,6 @@
        string "ENDMARKER".  We change it temporarily to point to the
        next character in that string.  (Yes, I know it's REALLY
        obscure.)
-
-    Python 3
-    ========
-
-       In Python 3, PyEval_InitThreads() cannot be called before
-       Py_InitializeEx() any more.  So this function calls
-       Py_InitializeEx() first.  It uses the same obscure logic to
-       make sure we never call it concurrently.
-
-       Arguably, this is less good on the spinlock, because
-       Py_InitializeEx() takes much longer to run than
-       PyEval_InitThreads().  But I didn't find a way around it.
     */
 
 #ifdef WITH_THREAD
@@ -332,8 +292,7 @@
     }
 #endif
 
-#if PY_MAJOR_VERSION >= 3
-    /* Python 3: call Py_InitializeEx() */
+    /* call Py_InitializeEx() */
     {
         PyGILState_STATE state = PyGILState_UNLOCKED;
         if (!Py_IsInitialized())
@@ -344,17 +303,6 @@
         PyEval_InitThreads();
         PyGILState_Release(state);
     }
-#else
-    /* Python 2: call PyEval_InitThreads() */
-# ifdef WITH_THREAD
-    if (!PyEval_ThreadsInitialized()) {
-        PyEval_InitThreads();    /* makes the GIL */
-        PyEval_ReleaseLock();    /* then release it */
-    }
-    /* else: there is already a GIL, but we still needed to do the
-       spinlock dance to make sure that we see it as fully ready */
-# endif
-#endif
 
 #ifdef WITH_THREAD
     /* release the lock */
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -143,6 +143,13 @@
             self._libraries.append(lib)
         return lib
 
+    def dlclose(self, lib):
+        """Close a library obtained with ffi.dlopen().  After this call,
+        access to functions or variables from the library will fail
+        (possibly with a segmentation fault).
+        """
+        type(lib).__cffi_close__(lib)
+
     def _typeof_locked(self, cdecl):
         # call me with the lock!
         key = cdecl
@@ -898,6 +905,9 @@
                 return addressof_var(name)
             raise AttributeError("cffi library has no function or "
                                  "global variable named '%s'" % (name,))
+        def __cffi_close__(self):
+            backendlib.close_lib()
+            self.__dict__.clear()
     #
     if libname is not None:
         try:
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -352,21 +352,20 @@
         self.fldquals = fldquals
         self.build_c_name_with_marker()
 
-    def has_anonymous_struct_fields(self):
-        if self.fldtypes is None:
-            return False
-        for name, type in zip(self.fldnames, self.fldtypes):
-            if name == '' and isinstance(type, StructOrUnion):
-                return True
-        return False
+    def anonymous_struct_fields(self):
+        if self.fldtypes is not None:
+            for name, type in zip(self.fldnames, self.fldtypes):
+                if name == '' and isinstance(type, StructOrUnion):
+                    yield type
 
-    def enumfields(self):
+    def enumfields(self, expand_anonymous_struct_union=True):
         fldquals = self.fldquals
         if fldquals is None:
             fldquals = (0,) * len(self.fldnames)
         for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
                                               self.fldbitsize, fldquals):
-            if name == '' and isinstance(type, StructOrUnion):
+            if (name == '' and isinstance(type, StructOrUnion)
+                    and expand_anonymous_struct_union):
                 # nested anonymous struct/union
                 for result in type.enumfields():
                     yield result
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -836,6 +836,10 @@
 
     def _struct_collecttype(self, tp):
         self._do_collect_type(tp)
+        if self.target_is_python:
+            # also requires nested anon struct/unions in ABI mode, recursively
+            for fldtype in tp.anonymous_struct_fields():
+                self._struct_collecttype(fldtype)
 
     def _struct_decl(self, tp, cname, approxname):
         if tp.fldtypes is None:
@@ -884,7 +888,7 @@
                  named_ptr not in self.ffi._parser._included_declarations)):
             if tp.fldtypes is None:
                 pass    # opaque
-            elif tp.partial or tp.has_anonymous_struct_fields():
+            elif tp.partial or any(tp.anonymous_struct_fields()):
                 pass    # field layout obtained silently from the C compiler
             else:
                 flags.append("_CFFI_F_CHECK_FIELDS")
@@ -896,7 +900,8 @@
         flags = '|'.join(flags) or '0'
         c_fields = []
         if reason_for_not_expanding is None:
-            enumfields = list(tp.enumfields())
+            expand_anonymous_struct_union = not self.target_is_python
+            enumfields = list(tp.enumfields(expand_anonymous_struct_union))
             for fldname, fldtype, fbitsize, fqual in enumfields:
                 fldtype = self._field_type(tp, fldname, fldtype)
                 self._check_not_opaque(fldtype,
diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py
--- a/cffi/setuptools_ext.py
+++ b/cffi/setuptools_ext.py
@@ -148,8 +148,8 @@
 
 def _add_py_module(dist, ffi, module_name):
     from distutils.dir_util import mkpath
-    from distutils.command.build_py import build_py
-    from distutils.command.build_ext import build_ext
+    from setuptools.command.build_py import build_py
+    from setuptools.command.build_ext import build_ext
     from distutils import log
     from cffi import recompiler
 
@@ -169,6 +169,17 @@
             generate_mod(os.path.join(self.build_lib, *module_path))
     dist.cmdclass['build_py'] = build_py_make_mod
 
+    # distutils and setuptools have no notion I could find of a
+    # generated python module.  If we don't add module_name to
+    # dist.py_modules, then things mostly work but there are some
+    # combination of options (--root and --record) that will miss
+    # the module.  So we add it here, which gives a few apparently
+    # harmless warnings about not finding the file outside the
+    # build directory.
+    if dist.py_modules is None:
+        dist.py_modules = []
+    dist.py_modules.append(module_name)
+
     # the following is only for "build_ext -i"
     base_class_2 = dist.cmdclass.get('build_ext', build_ext)
     class build_ext_make_mod(base_class_2):
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -560,7 +560,13 @@
 ``NAME.cpython-35m-x86_64-linux-gnu.so``.  You can manually rename it to
 ``NAME.abi3.so``, or use setuptools version 26 or later.  Also, note
 that compiling with a debug version of Python will not actually define
-``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy.
+``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy.  Finally,
+``Py_LIMITED_API`` is not defined on Windows, because this makes
+modules which cannot be used with ``virtualenv`` (issues `#355`__ and
+`#350`__).
+
+.. __: 
https://bitbucket.org/cffi/cffi/issues/355/importerror-dll-load-failed-on-windows
+.. __: 
https://bitbucket.org/cffi/cffi/issues/350/issue-with-py_limited_api-on-windows
 
 **ffibuilder.compile(tmpdir='.', verbose=False, debug=None):**
 explicitly generate the .py or .c file,
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -2,6 +2,42 @@
 What's New
 ======================
 
+
+v1.11.5
+=======
+
+* `Issue #357`_: fix ``ffi.emit_python_code()`` which generated a buggy
+  Python file if you are using a ``struct`` with an anonymous ``union``
+  field or vice-versa.
+
+* Windows: ``ffi.dlopen()`` should now handle unicode filenames.
+
+* ABI mode: implemented ``ffi.dlclose()`` for the in-line case (it used
+  to be present only in the out-of-line case).
+
+* Fixed a corner case for ``setup.py install --record=xx --root=yy``
+  with an out-of-line ABI module.  Also fixed `Issue #345`_.
+
+* More hacks on Windows for running CFFI's own ``setup.py``.
+
+* `Issue #358`_: in embedding, to protect against (the rare case of)
+  Python initialization from several threads in parallel, we have to use
+  a spin-lock.  On CPython 3 it is worse because it might spin-lock for
+  a long time (execution of ``Py_InitializeEx()``).  Sadly, recent
+  changes to CPython make that solution needed on CPython 2 too.
+
+* CPython 3 on Windows: we no longer compile with ``Py_LIMITED_API``
+  by default because such modules cannot be used with virtualenv.
+  `Issue #350`_ mentions a workaround if you still want that and are not
+  concerned about virtualenv: pass a ``define_macros=[("Py_LIMITED_API",
+  None)]`` to the ``ffibuilder.set_source()`` call.
+
+.. _`Issue #345`: https://bitbucket.org/cffi/cffi/issues/345/
+.. _`Issue #350`: https://bitbucket.org/cffi/cffi/issues/350/
+.. _`Issue #358`: https://bitbucket.org/cffi/cffi/issues/358/
+.. _`Issue #357`: https://bitbucket.org/cffi/cffi/issues/357/
+
+
 v1.11.4
 =======
 
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -2,6 +2,10 @@
 import subprocess
 import errno
 
+# on Windows we give up and always import setuptools early to fix things for us
+if sys.platform == "win32":
+    import setuptools
+
 
 sources = ['c/_cffi_backend.c']
 libraries = ['ffi']
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -499,3 +499,23 @@
         """)
         m = ffi.dlopen(lib_m)
         assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 
'myvar']
+
+    def test_dlclose(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("not with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("int foobar(void); int foobaz;")
+        lib = ffi.dlopen(lib_m)
+        ffi.dlclose(lib)
+        e = py.test.raises(ValueError, ffi.dlclose, lib)
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        e = py.test.raises(ValueError, getattr, lib, 'foobar')
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        e = py.test.raises(ValueError, getattr, lib, 'foobaz')
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        e = py.test.raises(ValueError, setattr, lib, 'foobaz', 42)
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py
--- a/testing/cffi0/test_ownlib.py
+++ b/testing/cffi0/test_ownlib.py
@@ -114,8 +114,12 @@
         if sys.platform == 'win32':
             import os
             # did we already build it?
-            if os.path.exists(str(udir.join('testownlib.dll'))):
-                cls.module = str(udir.join('testownlib.dll'))
+            if cls.Backend is CTypesBackend:
+                dll_path = str(udir) + '\\testownlib1.dll'   # only ascii for 
the ctypes backend
+            else:
+                dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll')   # 
non-ascii char
+            if os.path.exists(dll_path):
+                cls.module = dll_path
                 return
             # try (not too hard) to find the version used to compile this 
python
             # no mingw
@@ -135,8 +139,9 @@
             if os.path.isfile(vcvarsall):
                 cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c 
' \
                         ' /LD /Fetestownlib.dll'
-                subprocess.check_call(cmd, cwd = str(udir), shell=True)    
-                cls.module = str(udir.join('testownlib.dll'))
+                subprocess.check_call(cmd, cwd = str(udir), shell=True)
+                os.rename(str(udir) + '\\testownlib.dll', dll_path)
+                cls.module = dll_path
         else:
             subprocess.check_call(
                 'cc testownlib.c -shared -fPIC -o testownlib.so',
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
@@ -1,8 +1,9 @@
-import sys
+import sys, os
 import py
 from cffi import FFI
 from cffi import recompiler, ffiplatform, VerificationMissing
 from testing.udir import udir
+from testing.support import u
 
 
 def setup_module(mod):
@@ -35,6 +36,13 @@
                         'globalconst42', 'globalconsthello']
     )
     outputfilename = ffiplatform.compile(str(tmpdir), ext)
+    if sys.platform == "win32":
+        # test with a non-ascii char
+        outputfn1 = outputfilename
+        ofn, oext = os.path.splitext(outputfn1)
+        outputfilename = ofn + (u+'\u03be') + oext
+        #print(repr(outputfn1) + ' ==> ' + repr(outputfilename))
+        os.rename(outputfn1, outputfilename)
     mod.extmod = outputfilename
     mod.tmpdir = tmpdir
     #
@@ -55,6 +63,9 @@
     typedef struct bar_s { int x; signed char a[]; } bar_t;
     enum foo_e { AA, BB, CC };
     int strlen(const char *);
+    struct with_union { union { int a; char b; }; };
+    union with_struct { struct { int a; char b; }; };
+    struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; };
     """)
     ffi.set_source('re_python_pysrc', None)
     ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py')))
@@ -104,12 +115,16 @@
     from re_python_pysrc import ffi
     lib = ffi.dlopen(extmod)
     ffi.dlclose(lib)
+    if type(extmod) is not str:   # unicode, on python 2
+        str_extmod = extmod.encode('utf-8')
+    else:
+        str_extmod = extmod
     e = py.test.raises(ffi.error, ffi.dlclose, lib)
     assert str(e.value).startswith(
-        "library '%s' is already closed" % (extmod,))
+        "library '%s' is already closed" % (str_extmod,))
     e = py.test.raises(ffi.error, getattr, lib, 'add42')
     assert str(e.value) == (
-        "library '%s' has been closed" % (extmod,))
+        "library '%s' has been closed" % (str_extmod,))
 
 def test_constant_via_lib():
     from re_python_pysrc import ffi
@@ -212,3 +227,23 @@
     ffi.set_source('test_partial_enum', None)
     py.test.raises(VerificationMissing, ffi.emit_python_code,
                    str(tmpdir.join('test_partial_enum.py')))
+
+def test_anonymous_union_inside_struct():
+    # based on issue #357
+    from re_python_pysrc import ffi
+    INT = ffi.sizeof("int")
+    assert ffi.offsetof("struct with_union", "a") == 0
+    assert ffi.offsetof("struct with_union", "b") == 0
+    assert ffi.sizeof("struct with_union") == INT
+    #
+    assert ffi.offsetof("union with_struct", "a") == 0
+    assert ffi.offsetof("union with_struct", "b") == INT
+    assert ffi.sizeof("union with_struct") >= INT + 1
+    #
+    FLOAT = ffi.sizeof("float")
+    assert ffi.sizeof("struct NVGcolor") == FLOAT * 4
+    assert ffi.offsetof("struct NVGcolor", "rgba") == 0
+    assert ffi.offsetof("struct NVGcolor", "r") == 0
+    assert ffi.offsetof("struct NVGcolor", "g") == FLOAT
+    assert ffi.offsetof("struct NVGcolor", "b") == FLOAT * 2
+    assert ffi.offsetof("struct NVGcolor", "a") == FLOAT * 3
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
@@ -2297,3 +2297,11 @@
     else:
         assert lib.__loader__ is None
         assert lib.__spec__ is None
+
+def test_realize_struct_error():
+    ffi = FFI()
+    ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""")
+    lib = verify(ffi, "test_realize_struct_error", """
+        typedef int foo_t; struct foo_s { void (*x)(foo_t); };
+    """)
+    py.test.raises(TypeError, ffi.new, "struct foo_s *")
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to