Author: Armin Rigo <[email protected]>
Branch:
Changeset: r93897:f80f173700c8
Date: 2018-02-27 19:45 +0100
http://bitbucket.org/pypy/pypy/changeset/f80f173700c8/
Log: import cffi/70790d813156 (1.11.5)
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
from .api import FFI
from .error import CDefError, FFIError, VerificationError, VerificationMissing
-__version__ = "1.11.4"
-__version_info__ = (1, 11, 4)
+__version__ = "1.11.5"
+__version_info__ = (1, 11, 5)
# The verifier module file names are based on the CRC32 of a string that
# contains the following version number. It may be older than __version__
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/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
@@ -247,7 +221,7 @@
if (f != NULL && f != Py_None) {
PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
- "\ncompiled with cffi version: 1.11.4"
+ "\ncompiled with cffi version: 1.11.5"
"\n_cffi_backend module: ", f);
modules = PyImport_GetModuleDict();
mod = PyDict_GetItemString(modules, "_cffi_backend");
@@ -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/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/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/lib_pypy/cffi/model.py b/lib_pypy/cffi/model.py
--- a/lib_pypy/cffi/model.py
+++ b/lib_pypy/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/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/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/lib_pypy/cffi/setuptools_ext.py b/lib_pypy/cffi/setuptools_ext.py
--- a/lib_pypy/cffi/setuptools_ext.py
+++ b/lib_pypy/cffi/setuptools_ext.py
@@ -81,8 +81,13 @@
it doesn't so far, creating troubles. That's why we check
for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
+
+ On Windows, it's better not to use py_limited_api until issue #355
+ can be resolved (by having virtualenv copy PYTHON3.DLL). See also
+ the start of _cffi_include.h.
"""
- if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'):
+ if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
+ and sys.platform != 'win32'):
import setuptools
try:
setuptools_major_version =
int(setuptools.__version__.partition('.')[0])
@@ -143,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
@@ -164,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/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py
@@ -500,3 +500,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/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ownlib.py
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ownlib.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ownlib.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ownlib.py
@@ -115,8 +115,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
@@ -136,8 +140,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/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_re_python.py
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_re_python.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_re_python.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_re_python.py
@@ -1,9 +1,10 @@
# Generated by pypy/tool/import_cffi.py
-import sys
+import sys, os
import py
from cffi import FFI
from cffi import recompiler, ffiplatform, VerificationMissing
from pypy.module.test_lib_pypy.cffi_tests.udir import udir
+from pypy.module.test_lib_pypy.cffi_tests.support import u
def setup_module(mod):
@@ -36,6 +37,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
#
@@ -56,6 +64,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')))
@@ -105,12 +116,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
@@ -213,3 +228,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
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit