Author: Armin Rigo <[email protected]>
Branch: static-callback-embedding
Changeset: r2571:1cc859d4fc3d
Date: 2016-01-12 19:03 +0100
http://bitbucket.org/cffi/cffi/changeset/1cc859d4fc3d/
Log: hg merge default
diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h
new file mode 100644
--- /dev/null
+++ b/c/misc_thread_common.h
@@ -0,0 +1,136 @@
+#ifndef WITH_THREAD
+# error "xxx no-thread configuration not tested, please report if you need
that"
+#endif
+
+
+struct cffi_tls_s {
+ /* The locally-made thread state. This is only non-null in case
+ we build the thread state here. It remains null if this thread
+ had already a thread state provided by CPython. */
+ PyThreadState *local_thread_state;
+
+#ifndef USE__THREAD
+ /* The saved errno. If the C compiler supports '__thread', then
+ we use that instead. */
+ int saved_errno;
+#endif
+
+#ifdef MS_WIN32
+ /* The saved lasterror, on Windows. */
+ int saved_lasterror;
+#endif
+};
+
+static struct cffi_tls_s *get_cffi_tls(void); /* in misc_thread_posix.h
+ or misc_win32.h */
+
+static void cffi_thread_shutdown(void *p)
+{
+ struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
+
+ if (tls->local_thread_state != NULL) {
+ /* We need to re-acquire the GIL temporarily to free the
+ thread state. I hope it is not a problem to do it in
+ a thread-local destructor.
+ */
+ PyEval_RestoreThread(tls->local_thread_state);
+ PyThreadState_DeleteCurrent();
+ }
+ free(tls);
+}
+
+/* USE__THREAD is defined by setup.py if it finds that it is
+ syntactically valid to use "__thread" with this C compiler. */
+#ifdef USE__THREAD
+
+static __thread int cffi_saved_errno = 0;
+static void save_errno_only(void) { cffi_saved_errno = errno; }
+static void restore_errno_only(void) { errno = cffi_saved_errno; }
+
+#else
+
+static void save_errno_only(void)
+{
+ int saved = errno;
+ struct cffi_tls_s *tls = get_cffi_tls();
+ if (tls != NULL)
+ tls->saved_errno = saved;
+}
+
+static void restore_errno_only(void)
+{
+ struct cffi_tls_s *tls = get_cffi_tls();
+ if (tls != NULL)
+ errno = tls->saved_errno;
+}
+
+#endif
+
+
+/* Seems that CPython 3.5.1 made our job harder. Did not find out how
+ to do that without these hacks. We can't use PyThreadState_GET(),
+ because that calls PyThreadState_Get() which fails an assert if the
+ result is NULL. */
+#if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed)
+ /* this was abruptly un-defined in 3.5.1 */
+void *volatile _PyThreadState_Current;
+ /* XXX simple volatile access is assumed atomic */
+# define _Py_atomic_load_relaxed(pp) (*(pp))
+#endif
+
+static PyThreadState *get_current_ts(void)
+{
+#if PY_MAJOR_VERSION >= 3
+ return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
+#else
+ return _PyThreadState_Current;
+#endif
+}
+
+static PyGILState_STATE gil_ensure(void)
+{
+ /* Called at the start of a callback. Replacement for
+ PyGILState_Ensure().
+ */
+ PyGILState_STATE result;
+ struct cffi_tls_s *tls;
+ PyThreadState *ts = PyGILState_GetThisThreadState();
+
+ if (ts != NULL) {
+ ts->gilstate_counter++;
+ if (ts != get_current_ts()) {
+ /* common case: 'ts' is our non-current thread state and
+ we have to make it current and acquire the GIL */
+ PyEval_RestoreThread(ts);
+ return PyGILState_UNLOCKED;
+ }
+ else {
+ return PyGILState_LOCKED;
+ }
+ }
+ else {
+ /* no thread state here so far. */
+ result = PyGILState_Ensure();
+ assert(result == PyGILState_UNLOCKED);
+
+ ts = PyGILState_GetThisThreadState();
+ assert(ts != NULL);
+ assert(ts == get_current_ts());
+ assert(ts->gilstate_counter >= 1);
+
+ /* Save the now-current thread state inside our 'local_thread_state'
+ field, to be removed at thread shutdown */
+ tls = get_cffi_tls();
+ if (tls != NULL) {
+ tls->local_thread_state = ts;
+ ts->gilstate_counter++;
+ }
+
+ return result;
+ }
+}
+
+static void gil_release(PyGILState_STATE oldstate)
+{
+ PyGILState_Release(oldstate);
+}
diff --git a/c/misc_thread_posix.h b/c/misc_thread_posix.h
--- a/c/misc_thread_posix.h
+++ b/c/misc_thread_posix.h
@@ -13,41 +13,15 @@
shut down, using a destructor on the tls key.
*/
-#ifdef WITH_THREAD
#include <pthread.h>
+#include "misc_thread_common.h"
static pthread_key_t cffi_tls_key;
-struct cffi_tls_s {
- /* The locally-made thread state. This is only non-null in case
- we build the thread state here. It remains null if this thread
- had already a thread state provided by CPython. */
- PyThreadState *local_thread_state;
-
- /* The saved errno. If the C compiler supports '__thread', then
- we use that instead; this value is not used at all in this case. */
- int saved_errno;
-};
-
-static void _tls_destructor(void *p)
-{
- struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
-
- if (tls->local_thread_state != NULL) {
- /* We need to re-acquire the GIL temporarily to free the
- thread state. I hope it is not a problem to do it in
- a thread-local destructor.
- */
- PyEval_RestoreThread(tls->local_thread_state);
- PyThreadState_DeleteCurrent();
- }
- free(tls);
-}
-
static void init_cffi_tls(void)
{
- if (pthread_key_create(&cffi_tls_key, _tls_destructor) != 0)
+ if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0)
PyErr_SetString(PyExc_OSError, "pthread_key_create() failed");
}
@@ -71,116 +45,5 @@
return (struct cffi_tls_s *)p;
}
-
-/* USE__THREAD is defined by setup.py if it finds that it is
- syntactically valid to use "__thread" with this C compiler. */
-#ifdef USE__THREAD
-
-static __thread int cffi_saved_errno = 0;
-static void save_errno(void) { cffi_saved_errno = errno; }
-static void restore_errno(void) { errno = cffi_saved_errno; }
-
-#else
-
-static void save_errno(void)
-{
- int saved = errno;
- struct cffi_tls_s *tls = get_cffi_tls();
- if (tls != NULL)
- tls->saved_errno = saved;
-}
-
-static void restore_errno(void)
-{
- struct cffi_tls_s *tls = get_cffi_tls();
- if (tls != NULL)
- errno = tls->saved_errno;
-}
-
-#endif
-
-
-/* Seems that CPython 3.5.1 made our job harder. Did not find out how
- to do that without these hacks. We can't use PyThreadState_GET(),
- because that calls PyThreadState_Get() which fails an assert if the
- result is NULL. */
-#if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed)
- /* this was abruptly un-defined in 3.5.1 */
-void *volatile _PyThreadState_Current;
- /* XXX simple volatile access is assumed atomic */
-# define _Py_atomic_load_relaxed(pp) (*(pp))
-#endif
-
-
-static PyThreadState *get_current_ts(void)
-{
-#if PY_MAJOR_VERSION >= 3
- return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
-#else
- return _PyThreadState_Current;
-#endif
-}
-
-static PyGILState_STATE gil_ensure(void)
-{
- /* Called at the start of a callback. Replacement for
- PyGILState_Ensure().
- */
- PyGILState_STATE result;
- struct cffi_tls_s *tls;
- PyThreadState *ts = PyGILState_GetThisThreadState();
-
- if (ts != NULL) {
- ts->gilstate_counter++;
- if (ts != get_current_ts()) {
- /* common case: 'ts' is our non-current thread state and
- we have to make it current and acquire the GIL */
- PyEval_RestoreThread(ts);
- return PyGILState_UNLOCKED;
- }
- else {
- return PyGILState_LOCKED;
- }
- }
- else {
- /* no thread state here so far. */
- result = PyGILState_Ensure();
- assert(result == PyGILState_UNLOCKED);
-
- ts = PyGILState_GetThisThreadState();
- assert(ts != NULL);
- assert(ts == get_current_ts());
- assert(ts->gilstate_counter >= 1);
-
- /* Save the now-current thread state inside our 'local_thread_state'
- field, to be removed at thread shutdown */
- tls = get_cffi_tls();
- if (tls != NULL) {
- tls->local_thread_state = ts;
- ts->gilstate_counter++;
- }
-
- return result;
- }
-}
-
-static void gil_release(PyGILState_STATE oldstate)
-{
- PyGILState_Release(oldstate);
-}
-
-
-#else /* !WITH_THREAD */
-
-static int cffi_saved_errno = 0;
-static void save_errno(void) { cffi_saved_errno = errno; }
-static void restore_errno(void) { errno = cffi_saved_errno; }
-
-static PyGILState_STATE gil_ensure(void) { return -1; }
-static void gil_release(PyGILState_STATE oldstate) { }
-
-#endif /* !WITH_THREAD */
-
-
-#define save_errno_only save_errno
-#define restore_errno_only restore_errno
+#define save_errno save_errno_only
+#define restore_errno restore_errno_only
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -1,15 +1,37 @@
#include <malloc.h> /* for alloca() */
+
/************************************************************/
/* errno and GetLastError support */
-struct cffi_errno_s {
- int saved_errno;
- int saved_lasterror;
-};
+#include "misc_thread_common.h"
static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES;
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+ DWORD reason_for_call,
+ LPVOID reserved)
+{
+ LPVOID p;
+
+ switch (reason_for_call) {
+
+ case DLL_THREAD_DETACH:
+ if (cffi_tls_index != TLS_OUT_OF_INDEXES) {
+ p = TlsGetValue(cffi_tls_index);
+ if (p != NULL) {
+ TlsSetValue(cffi_tls_index, NULL);
+ cffi_thread_shutdown(p);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return TRUE;
+}
+
static void init_cffi_tls(void)
{
if (cffi_tls_index == TLS_OUT_OF_INDEXES) {
@@ -19,28 +41,29 @@
}
}
-static struct cffi_errno_s *_geterrno_object(void)
+static struct cffi_tls_s *get_cffi_tls(void)
{
LPVOID p = TlsGetValue(cffi_tls_index);
if (p == NULL) {
- /* XXX this malloc() leaks */
- p = malloc(sizeof(struct cffi_errno_s));
+ p = malloc(sizeof(struct cffi_tls_s));
if (p == NULL)
return NULL;
- memset(p, 0, sizeof(struct cffi_errno_s));
+ memset(p, 0, sizeof(struct cffi_tls_s));
TlsSetValue(cffi_tls_index, p);
}
- return (struct cffi_errno_s *)p;
+ return (struct cffi_tls_s *)p;
}
+#ifdef USE__THREAD
+# error "unexpected USE__THREAD on Windows"
+#endif
+
static void save_errno(void)
{
int current_err = errno;
int current_lasterr = GetLastError();
- struct cffi_errno_s *p;
-
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p != NULL) {
p->saved_errno = current_err;
p->saved_lasterror = current_lasterr;
@@ -48,23 +71,9 @@
/* else: cannot report the error */
}
-static void save_errno_only(void)
-{
- int current_err = errno;
- struct cffi_errno_s *p;
-
- p = _geterrno_object();
- if (p != NULL) {
- p->saved_errno = current_err;
- }
- /* else: cannot report the error */
-}
-
static void restore_errno(void)
{
- struct cffi_errno_s *p;
-
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p != NULL) {
SetLastError(p->saved_lasterror);
errno = p->saved_errno;
@@ -72,16 +81,8 @@
/* else: cannot report the error */
}
-static void restore_errno_only(void)
-{
- struct cffi_errno_s *p;
+/************************************************************/
- p = _geterrno_object();
- if (p != NULL) {
- errno = p->saved_errno;
- }
- /* else: cannot report the error */
-}
#if PY_MAJOR_VERSION >= 3
static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
@@ -96,8 +97,7 @@
return NULL;
if (err == -1) {
- struct cffi_errno_s *p;
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
@@ -138,7 +138,7 @@
int len;
char *s;
char *s_buf = NULL; /* Free via LocalFree */
- char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
+ char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */
PyObject *v;
static char *keywords[] = {"code", NULL};
@@ -146,8 +146,7 @@
return NULL;
if (err == -1) {
- struct cffi_errno_s *p;
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
@@ -183,16 +182,6 @@
#endif
-#ifdef WITH_THREAD
-/* XXX should port the code from misc_thread_posix.h */
-static PyGILState_STATE gil_ensure(void) { return PyGILState_Ensure(); }
-static void gil_release(PyGILState_STATE oldst) { PyGILState_Release(oldst); }
-#else
-static PyGILState_STATE gil_ensure(void) { return -1; }
-static void gil_release(PyGILState_STATE oldstate) { }
-#endif
-
-
/************************************************************/
/* Emulate dlopen()&co. from the Windows API */
diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py
--- a/demo/btrfs-snap.py
+++ b/demo/btrfs-snap.py
@@ -22,10 +22,14 @@
};
""")
-v = ffi.verify("#include <btrfs/ioctl.h>")
+ffi.set_source("_btrfs_cffi", "#include <btrfs/ioctl.h>")
+ffi.compile()
+# ____________________________________________________________
+from _btrfs_cffi import ffi, lib
+
parser = argparse.ArgumentParser(usage=__doc__.strip())
parser.add_argument('source', help='source subvolume')
parser.add_argument('target', help='target directory')
@@ -41,7 +45,7 @@
args.fd = source
args_buffer = ffi.buffer(args)
try:
- fcntl.ioctl(target, v.BTRFS_IOC_SNAP_CREATE_V2, args_buffer)
+ fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer)
except IOError as e:
print e
sys.exit(1)
diff --git a/demo/extern_python_varargs.py b/demo/extern_python_varargs.py
new file mode 100644
--- /dev/null
+++ b/demo/extern_python_varargs.py
@@ -0,0 +1,61 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.cdef("""
+ int my_algo(int);
+ typedef ... va_list;
+ extern "Python" int f(int, va_list *);
+
+ int fetch_int(va_list *);
+ double fetch_double(va_list *);
+ void *fetch_ptr(va_list *);
+""")
+
+ffi.set_source("_extern_python_cffi", """
+ #include <stdarg.h>
+
+ static int f(int, va_list *);
+
+ static int f1(int n, ...)
+ {
+ va_list ap;
+ va_start(ap, n);
+ int res = f(n, &ap);
+ va_end(ap);
+ return res;
+ }
+
+ static int fetch_int(va_list *va) { return va_arg((*va), int); }
+ static double fetch_double(va_list *va) { return va_arg((*va), double); }
+ static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); }
+
+ static int my_algo(int n) {
+ return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6);
+ }
+""")
+
+ffi.compile()
+
+
+from _extern_python_cffi import ffi, lib
+
[email protected]_extern()
+def f(n, va):
+ if n == 3:
+ x = lib.fetch_int(va)
+ y = lib.fetch_int(va)
+ z = lib.fetch_int(va)
+ print (x, y, z)
+ elif n == 1:
+ ptr = lib.fetch_ptr(va)
+ print 'ptr to:', ffi.cast("int *", ptr)[0]
+ elif n == 2:
+ x = lib.fetch_double(va)
+ y = lib.fetch_double(va)
+ print (x, y)
+ else:
+ raise AssertionError(n)
+ return 14
+
+print lib.my_algo(10)
diff --git a/demo/fastcsv.py b/demo/fastcsv.py
--- a/demo/fastcsv.py
+++ b/demo/fastcsv.py
@@ -4,9 +4,8 @@
# IN-PROGRESS. See the demo at the end of the file
-dialect2ffi = {}
-
-def _make_ffi_from_dialect(dialect):
+def _make_ffi_from_dialect(dialect_name):
+ dialect = csv.get_dialect(dialect_name)
ffi = cffi.FFI()
@@ -26,7 +25,7 @@
else:
d['is_escape_char'] = '&& 0'
- lib = ffi.verify(r'''
+ ffi.set_source('_fastcsv_' + dialect_name, r'''
typedef enum {
START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD,
@@ -237,15 +236,16 @@
}
''' % d)
- return ffi, lib
+ ffi.compile()
-def fastcsv_reader(f, dialect):
- dialect = csv.get_dialect(dialect)
+def fastcsv_reader(f, dialect_name):
try:
- ffi, lib = dialect2ffi[dialect]
- except KeyError:
- ffi, lib = dialect2ffi[dialect] = _make_ffi_from_dialect(dialect)
+ module = __import__('_fastcsv_' + dialect_name)
+ except ImportError:
+ _make_ffi_from_dialect(dialect_name)
+ module = __import__('_fastcsv_' + dialect_name)
+ ffi, lib = module.ffi, module.lib
#
linelen = -1
for line in f:
diff --git a/demo/gmp.py b/demo/gmp.py
--- a/demo/gmp.py
+++ b/demo/gmp.py
@@ -1,33 +1,30 @@
import sys
-import cffi
-
#
# This is only a demo based on the GMP library.
-# There is a rather more complete version available at:
+# There is a rather more complete (but perhaps outdated) version available at:
# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files
#
-ffi = cffi.FFI()
+try:
+ from _gmp_cffi import ffi, lib
+except ImportError:
+ print 'run gmp_build first, then make sure the shared object is on
sys.path'
+ sys.exit(1)
-ffi.cdef("""
-
- typedef struct { ...; } MP_INT;
- typedef MP_INT mpz_t[1];
-
- int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base);
- void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2);
- char * mpz_get_str (char *string, int base, MP_INT *integer);
-
-""")
-
-lib = ffi.verify("#include <gmp.h>",
- libraries=['gmp', 'm'])
+# ffi "knows" about the declared variables and functions from the
+# cdef parts of the module created from gmp_build
+# lib "knows" how to call the functions from the set_source parts
+# of the module.
# ____________________________________________________________
a = ffi.new("mpz_t")
b = ffi.new("mpz_t")
+if len(sys.argv) < 3:
+ print 'call as %s bigint1, bigint2' % sys.argv[0]
+ sys.exit(2)
+
lib.mpz_init_set_str(a, sys.argv[1], 10) # Assume decimal integers
lib.mpz_init_set_str(b, sys.argv[2], 10) # Assume decimal integers
lib.mpz_add(a, a, b) # a=a+b
diff --git a/demo/gmp_build.py b/demo/gmp_build.py
new file mode 100644
--- /dev/null
+++ b/demo/gmp_build.py
@@ -0,0 +1,27 @@
+import cffi
+
+#
+# This is only a demo based on the GMP library.
+# There is a rather more complete (but perhaps outdated) version available at:
+# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files
+#
+
+ffi = cffi.FFI()
+
+ffi.cdef("""
+
+ typedef struct { ...; } MP_INT;
+ typedef MP_INT mpz_t[1];
+
+ int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base);
+ void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2);
+ char * mpz_get_str (char *string, int base, MP_INT *integer);
+
+""")
+
+ffi.set_source('_gmp_cffi', "#include <gmp.h>",
+ libraries=['gmp', 'm'])
+
+if __name__ == '__main__':
+ ffi.compile()
+
diff --git a/demo/pwuid.py b/demo/pwuid.py
--- a/demo/pwuid.py
+++ b/demo/pwuid.py
@@ -1,14 +1,7 @@
-from cffi import FFI
-ffi = FFI()
-ffi.cdef(""" // some declarations from the man page
- struct passwd {
- char *pw_name;
- ...;
- };
- struct passwd *getpwuid(int uid);
-""")
-C = ffi.verify(""" // passed to the real C compiler
-#include <sys/types.h>
-#include <pwd.h>
-""")
-print ffi.string(C.getpwuid(0).pw_name)
+import sys, os
+
+# run pwuid_build first, then make sure the shared object is on sys.path
+from _pwuid_cffi import ffi, lib
+
+
+print ffi.string(lib.getpwuid(0).pw_name)
diff --git a/demo/pwuid_build.py b/demo/pwuid_build.py
new file mode 100644
--- /dev/null
+++ b/demo/pwuid_build.py
@@ -0,0 +1,18 @@
+from cffi import FFI
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+
+ffi.set_source('_pwuid_cffi', """ // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""")
+
+
+if __name__ == '__main__':
+ ffi.compile()
diff --git a/demo/readdir2.py b/demo/readdir2.py
--- a/demo/readdir2.py
+++ b/demo/readdir2.py
@@ -1,11 +1,13 @@
-# A Linux-only demo, using verify() instead of hard-coding the exact layouts
+# A Linux-only demo, using set_source() instead of hard-coding the exact
layouts
#
import sys
-from _readdir2 import ffi, lib
if not sys.platform.startswith('linux'):
raise Exception("Linux-only demo")
+# run readdir2_build first, then make sure the shared object is on sys.path
+from _readdir2_cffi import ffi, lib
+
def walk(basefd, path):
print '{', path
diff --git a/demo/readdir2_build.py b/demo/readdir2_build.py
--- a/demo/readdir2_build.py
+++ b/demo/readdir2_build.py
@@ -20,7 +20,7 @@
static const int DT_DIR;
""")
-ffi.set_source("_readdir2", """
+ffi.set_source("_readdir2_cffi", """
#ifndef _ATFILE_SOURCE
# define _ATFILE_SOURCE
#endif
diff --git a/demo/winclipboard.py b/demo/winclipboard.py
--- a/demo/winclipboard.py
+++ b/demo/winclipboard.py
@@ -1,60 +1,40 @@
__author__ = "Israel Fruchter <[email protected]>"
-from cffi import FFI
+import sys, os
-ffi = FFI()
-ffi.cdef('''
- typedef void * HANDLE;
- typedef HANDLE HWND;
- typedef int BOOL;
- typedef unsigned int UINT;
- typedef int SIZE_T;
- typedef char * LPTSTR;
- typedef HANDLE HGLOBAL;
- typedef HANDLE LPVOID;
+if not sys.platform == 'win32':
+ raise Exception("Windows-only demo")
- HWND GetConsoleWindow(void);
+try:
+ from _winclipboard_cffi import ffi, lib
+except ImportError:
+ print 'run winclipboard_build first, then make sure the shared object is
on sys.path'
+ sys.exit(1)
- LPVOID GlobalLock( HGLOBAL hMem );
- BOOL GlobalUnlock( HGLOBAL hMem );
- HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
-
- BOOL OpenClipboard(HWND hWndNewOwner);
- BOOL CloseClipboard(void);
- BOOL EmptyClipboard(void);
- HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);
-
- #define CF_TEXT ...
- #define GMEM_MOVEABLE ...
-
- void * memcpy(void * s1, void * s2, int n);
- ''')
-
-lib = ffi.verify('''
- #include <windows.h>
-''', libraries=["user32"])
-
-globals().update(lib.__dict__)
+# ffi "knows" about the declared variables and functions from the
+# cdef parts of the module _winclipboard_cffi created,
+# lib "knows" how to call the functions from the set_source parts
+# of the module.
def CopyToClipboard(string):
'''
use win32 api to copy `string` to the clipboard
'''
- hWnd = GetConsoleWindow()
+ hWnd = lib.GetConsoleWindow()
- if OpenClipboard(hWnd):
+ if lib.OpenClipboard(hWnd):
cstring = ffi.new("char[]", string)
size = ffi.sizeof(cstring)
# make it a moveable memory for other processes
- hGlobal = GlobalAlloc(GMEM_MOVEABLE, size)
- buffer = GlobalLock(hGlobal)
- memcpy(buffer, cstring, size)
- GlobalUnlock(hGlobal)
+ hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size)
+ buffer = lib.GlobalLock(hGlobal)
+ lib.memcpy(buffer, cstring, size)
+ lib.GlobalUnlock(hGlobal)
- res = EmptyClipboard()
- res = SetClipboardData(CF_TEXT, buffer)
+ res = lib.EmptyClipboard()
+ res = lib.SetClipboardData(lib.CF_TEXT, buffer)
- CloseClipboard()
+ lib.CloseClipboard()
CopyToClipboard("hello world from cffi")
diff --git a/demo/winclipboard_build.py b/demo/winclipboard_build.py
new file mode 100644
--- /dev/null
+++ b/demo/winclipboard_build.py
@@ -0,0 +1,36 @@
+from cffi import FFI
+
+ffi = FFI()
+ffi.cdef('''
+ typedef void * HANDLE;
+ typedef HANDLE HWND;
+ typedef int BOOL;
+ typedef unsigned int UINT;
+ typedef int SIZE_T;
+ typedef char * LPTSTR;
+ typedef HANDLE HGLOBAL;
+ typedef HANDLE LPVOID;
+
+ HWND GetConsoleWindow(void);
+
+ LPVOID GlobalLock( HGLOBAL hMem );
+ BOOL GlobalUnlock( HGLOBAL hMem );
+ HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
+
+ BOOL OpenClipboard(HWND hWndNewOwner);
+ BOOL CloseClipboard(void);
+ BOOL EmptyClipboard(void);
+ HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);
+
+ #define CF_TEXT ...
+ #define GMEM_MOVEABLE ...
+
+ void * memcpy(void * s1, void * s2, int n);
+ ''')
+
+ffi.set_source('_winclipboard_cffi', '''
+ #include <windows.h>
+''', libraries=["user32"])
+
+if __name__ == '__main__':
+ ffi.compile()
diff --git a/demo/xclient.py b/demo/xclient.py
--- a/demo/xclient.py
+++ b/demo/xclient.py
@@ -1,40 +1,27 @@
-from cffi import FFI
+import sys, os
-ffi = FFI()
-ffi.cdef("""
+# run xclient_build first, then make sure the shared object is on sys.path
+from _xclient_cffi import ffi, lib
-typedef ... Display;
-typedef struct { ...; } Window;
-typedef struct { int type; ...; } XEvent;
+# ffi "knows" about the declared variables and functions from the
+# cdef parts of the module xclient_build created,
+# lib "knows" how to call the functions from the set_source parts
+# of the module.
-Display *XOpenDisplay(char *display_name);
-Window DefaultRootWindow(Display *display);
-int XMapRaised(Display *display, Window w);
-Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
- unsigned int width, unsigned int height,
- unsigned int border_width, unsigned long border,
- unsigned long background);
-int XNextEvent(Display *display, XEvent *event_return);
-""")
-lib = ffi.verify("""
-#include <X11/Xlib.h>
-""", libraries=['X11'])
-
-globals().update(lib.__dict__)
class XError(Exception):
pass
def main():
- display = XOpenDisplay(ffi.NULL)
+ display = lib.XOpenDisplay(ffi.NULL)
if display == ffi.NULL:
raise XError("cannot open display")
- w = XCreateSimpleWindow(display, DefaultRootWindow(display),
+ w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display),
10, 10, 500, 350, 0, 0, 0)
- XMapRaised(display, w)
+ lib.XMapRaised(display, w)
event = ffi.new("XEvent *")
- XNextEvent(display, event)
+ lib.XNextEvent(display, event)
if __name__ == '__main__':
main()
diff --git a/demo/xclient_build.py b/demo/xclient_build.py
new file mode 100644
--- /dev/null
+++ b/demo/xclient_build.py
@@ -0,0 +1,25 @@
+from cffi import FFI
+ffi = FFI()
+ffi.cdef("""
+
+typedef ... Display;
+typedef struct { ...; } Window;
+
+typedef struct { int type; ...; } XEvent;
+
+Display *XOpenDisplay(char *display_name);
+Window DefaultRootWindow(Display *display);
+int XMapRaised(Display *display, Window w);
+Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
+ unsigned int width, unsigned int height,
+ unsigned int border_width, unsigned long border,
+ unsigned long background);
+int XNextEvent(Display *display, XEvent *event_return);
+""")
+
+ffi.set_source('_xclient_cffi', """
+ #include <X11/Xlib.h>
+""", libraries=['X11'])
+
+if __name__ == '__main__':
+ ffi.compile()
diff --git a/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -476,7 +476,7 @@
``@ffi.def_extern()``.
The ``@ffi.def_extern()`` decorator should be applied to a global
-function, once. This is because each function from the cdef with
+function, but *only once.* This is because each function from the cdef with
``extern "Python"`` turns into only one C function. To support some
corner cases, it is possible to redefine the attached Python function
by calling ``@ffi.def_extern()`` again---but this is not recommended!
@@ -616,7 +616,10 @@
}
The ``extern "Python"`` functions cannot be variadic for now. This
-may be implemented in the future.
+may be implemented in the future. (`This demo`__ shows how to do it
+anyway, but it is a bit lengthy.)
+
+.. __:
https://bitbucket.org/cffi/cffi/src/default/demo/extern_python_varargs.py
Each corresponding Python callback function is defined with the
``@ffi.def_extern()`` decorator. Be careful when writing this
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
@@ -1718,3 +1718,10 @@
exec("from _test_import_from_lib.lib import *", d)
assert (set(key for key in d if not key.startswith('_')) ==
set(['myfunc', 'MYFOO']))
+ #
+ # also test "import *" on the module itself, which should be
+ # equivalent to "import ffi, lib"
+ d = {}
+ exec("from _test_import_from_lib import *", d)
+ assert (sorted([x for x in d.keys() if not x.startswith('__')]) ==
+ ['ffi', 'lib'])
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit