Author: Armin Rigo <[email protected]>
Branch:
Changeset: r3319:c1e3a0dc7ac4
Date: 2020-01-05 13:46 +0100
http://bitbucket.org/cffi/cffi/changeset/c1e3a0dc7ac4/
Log: Issue #437
Support ffi.dlopen(<void* cdata>). See updated documentation.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -4201,11 +4201,12 @@
PyObject_HEAD
void *dl_handle;
char *dl_name;
+ int dl_auto_close;
} DynLibObject;
static void dl_dealloc(DynLibObject *dlobj)
{
- if (dlobj->dl_handle != NULL)
+ if (dlobj->dl_handle != NULL && dlobj->dl_auto_close)
dlclose(dlobj->dl_handle);
free(dlobj->dl_name);
PyObject_Del(dlobj);
@@ -4370,7 +4371,7 @@
};
static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
- PyObject **p_temp)
+ PyObject **p_temp, int *auto_close)
{
/* 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
@@ -4381,6 +4382,7 @@
char *filename_or_null;
int flags = 0;
*p_temp = NULL;
+ *auto_close = 1;
if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
PyObject *dummy;
@@ -4390,6 +4392,28 @@
filename_or_null = NULL;
*p_printable_filename = "<None>";
}
+ else if (CData_Check(PyTuple_GET_ITEM(args, 0)))
+ {
+ CDataObject *cd;
+ if (!PyArg_ParseTuple(args, "O|i:load_library", &cd, &flags))
+ return NULL;
+ /* 'flags' is accepted but ignored in this case */
+ if ((cd->c_type->ct_flags & CT_IS_VOID_PTR) == 0) {
+ PyErr_Format(PyExc_TypeError,
+ "dlopen() takes a file name or 'void *' handle, not '%s'",
+ cd->c_type->ct_name);
+ return NULL;
+ }
+ handle = cd->c_data;
+ if (handle == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "cannot call dlopen(NULL)");
+ return NULL;
+ }
+ *p_temp = PyText_FromFormat("%p", handle);
+ *p_printable_filename = PyText_AsUTF8(*p_temp);
+ *auto_close = 0;
+ return handle;
+ }
else
{
PyObject *s = PyTuple_GET_ITEM(args, 0);
@@ -4451,8 +4475,9 @@
PyObject *temp;
void *handle;
DynLibObject *dlobj = NULL;
-
- handle = b_do_dlopen(args, &printable_filename, &temp);
+ int auto_close;
+
+ handle = b_do_dlopen(args, &printable_filename, &temp, &auto_close);
if (handle == NULL)
goto error;
@@ -4463,6 +4488,7 @@
}
dlobj->dl_handle = handle;
dlobj->dl_name = strdup(printable_filename);
+ dlobj->dl_auto_close = auto_close;
error:
Py_XDECREF(temp);
diff --git a/c/cdlopen.c b/c/cdlopen.c
--- a/c/cdlopen.c
+++ b/c/cdlopen.c
@@ -43,12 +43,13 @@
const char *modname;
PyObject *temp, *result = NULL;
void *handle;
+ int auto_close;
- handle = b_do_dlopen(args, &modname, &temp);
+ handle = b_do_dlopen(args, &modname, &temp, &auto_close);
if (handle != NULL)
{
result = (PyObject *)lib_internal_new((FFIObject *)self,
- modname, handle);
+ modname, handle, auto_close);
}
Py_XDECREF(temp);
return result;
diff --git a/c/cffi1_module.c b/c/cffi1_module.c
--- a/c/cffi1_module.c
+++ b/c/cffi1_module.c
@@ -199,7 +199,7 @@
if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0)
return NULL;
- lib = lib_internal_new(ffi, module_name, NULL);
+ lib = lib_internal_new(ffi, module_name, NULL, 0);
if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0)
return NULL;
diff --git a/c/lib_obj.c b/c/lib_obj.c
--- a/c/lib_obj.c
+++ b/c/lib_obj.c
@@ -29,6 +29,7 @@
PyObject *l_libname; /* some string that gives the name of the lib
*/
FFIObject *l_ffi; /* reference back to the ffi object */
void *l_libhandle; /* the dlopen()ed handle, if any */
+ int l_auto_close; /* if we must dlclose() this handle */
};
static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x)
@@ -91,7 +92,8 @@
static void lib_dealloc(LibObject *lib)
{
PyObject_GC_UnTrack(lib);
- cdlopen_close_ignore_errors(lib->l_libhandle);
+ if (lib->l_auto_close)
+ cdlopen_close_ignore_errors(lib->l_libhandle);
Py_DECREF(lib->l_dict);
Py_DECREF(lib->l_libname);
Py_DECREF(lib->l_ffi);
@@ -624,7 +626,7 @@
};
static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name,
- void *dlopen_libhandle)
+ void *dlopen_libhandle, int auto_close)
{
LibObject *lib;
PyObject *libname, *dict;
@@ -647,6 +649,7 @@
Py_INCREF(ffi);
lib->l_ffi = ffi;
lib->l_libhandle = dlopen_libhandle;
+ lib->l_auto_close = auto_close;
return lib;
err3:
@@ -654,7 +657,8 @@
err2:
Py_DECREF(libname);
err1:
- cdlopen_close_ignore_errors(dlopen_libhandle);
+ if (auto_close)
+ cdlopen_close_ignore_errors(dlopen_libhandle);
return NULL;
}
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -141,7 +141,11 @@
linked to a particular library, just like C headers; in the
library we only look for the actual (untyped) symbols.
"""
- assert isinstance(name, basestring) or name is None
+ if not (isinstance(name, basestring) or
+ name is None or
+ isinstance(name, self.CData)):
+ raise TypeError("dlopen(name): name must be a file name, None, "
+ "or an already-opened 'void *' handle")
with self._lock:
lib, function_cache = _make_ffi_library(self, name, flags)
self._function_caches.append(function_cache)
@@ -799,9 +803,9 @@
def _load_backend_lib(backend, name, flags):
import os
- if name is None:
- if sys.platform != "win32":
- return backend.load_library(None, flags)
+ if not isinstance(name, basestring):
+ if sys.platform != "win32" or name is not None:
+ return backend.load_library(name, flags)
name = "c" # Windows: load_library(None) fails, but this works
# on Python 2 (backward compatibility hack only)
first_error = None
@@ -935,7 +939,7 @@
backendlib.close_lib()
self.__dict__.clear()
#
- if libname is not None:
+ if isinstance(libname, basestring):
try:
if not isinstance(libname, str): # unicode, on Python 2
libname = libname.encode('utf-8')
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -369,6 +369,16 @@
``ffi.dlopen(None)`` no longer work on Windows; try instead
``ffi.dlopen(ctypes.util.find_library('c'))``.
+*New in version 1.14:* ``ffi.dlopen(handle)``: instead of a file path,
+you can give an already-opened library handle, as a cdata of type
+``void *``. Such a call converts this handle into a regular FFI object
+with the functions and global variables declared by ``ffi.cdef()``.
+Useful if you have special needs (e.g. you need the GNU extension
+``dlmopen()``, which you can itself declare and call using a different
+``ffi`` object). Note that in this variant, ``dlclose()`` is not called
+automatically if the FFI object is garbage-collected (but you can still
+call ``ffi.dlclose()`` explicitly if needed).
+
ffibuilder.set_source(): preparing out-of-line modules
------------------------------------------------------
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
@@ -371,3 +371,29 @@
assert s.top == 22
assert s.right == 33
assert s.bottom == 44
+
+ def test_dlopen_handle(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if sys.platform == 'win32':
+ py.test.skip("uses 'dl' explicitly")
+ if self.__class__.Backend is CTypesBackend:
+ py.test.skip("not for the ctypes backend")
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi1.cdef("""void *dlopen(const char *filename, int flags);
+ int dlclose(void *handle);""")
+ lib1 = ffi1.dlopen('dl')
+ handle = lib1.dlopen(self.module.encode(sys.getfilesystemencoding()),
+ backend.RTLD_LAZY)
+ assert ffi1.typeof(handle) == ffi1.typeof("void *")
+ assert handle
+
+ ffi = FFI(backend=backend)
+ ffi.cdef("""unsigned short foo_2bytes(unsigned short a);""")
+ lib = ffi.dlopen(handle)
+ x = lib.foo_2bytes(1000)
+ assert x == 1042
+
+ err = lib1.dlclose(handle)
+ assert err == 0
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
@@ -260,3 +260,24 @@
# based on issue #429
from re_python_pysrc import ffi
ffi.new("selfref_ptr_t")
+
+def test_dlopen_handle():
+ import _cffi_backend
+ from re_python_pysrc import ffi
+ if sys.platform == 'win32':
+ py.test.skip("uses 'dl' explicitly")
+ ffi1 = FFI()
+ ffi1.cdef("""void *dlopen(const char *filename, int flags);
+ int dlclose(void *handle);""")
+ lib1 = ffi1.dlopen('dl')
+ handle = lib1.dlopen(extmod.encode(sys.getfilesystemencoding()),
+ _cffi_backend.RTLD_LAZY)
+ assert ffi1.typeof(handle) == ffi1.typeof("void *")
+ assert handle
+
+ lib = ffi.dlopen(handle)
+ assert lib.add42(-10) == 32
+ assert type(lib.add42) is _cffi_backend.FFI.CData
+
+ err = lib1.dlclose(handle)
+ assert err == 0
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit