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