Author: Armin Rigo <ar...@tunes.org>
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
+
+@ffi.def_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 <israel.fruch...@gmail.com>"
 
-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
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to