Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r2585:fc7471ccde10
Date: 2016-01-15 11:26 +0100
http://bitbucket.org/cffi/cffi/changeset/fc7471ccde10/

Log:    hg merge static-callback-embedding

        Embedding!

diff too long, truncating to 2000 out of 2798 lines

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -6354,7 +6354,7 @@
 #endif
 
 struct _cffi_externpy_s;      /* forward declaration */
-static void _cffi_call_python(struct _cffi_externpy_s *, char *args);
+static void cffi_call_python(struct _cffi_externpy_s *, char *args);
 
 static void *cffi_exports[] = {
     NULL,
@@ -6387,7 +6387,7 @@
     _cffi_to_c__Bool,
     _prepare_pointer_call_argument,
     convert_array_from_object,
-    _cffi_call_python,
+    cffi_call_python,
 };
 
 static struct { const char *name; int value; } all_dlopen_flags[] = {
diff --git a/c/call_python.c b/c/call_python.c
--- a/c/call_python.c
+++ b/c/call_python.c
@@ -94,7 +94,7 @@
         return NULL;
 
     /* force _update_cache_to_call_python() to be called the next time
-       the C function invokes _cffi_call_python, to update the cache */
+       the C function invokes cffi_call_python, to update the cache */
     old1 = externpy->reserved1;
     externpy->reserved1 = Py_None;   /* a non-NULL value */
     Py_INCREF(Py_None);
@@ -143,7 +143,15 @@
     return 2;   /* out of memory? */
 }
 
-static void _cffi_call_python(struct _cffi_externpy_s *externpy, char *args)
+#if (defined(WITH_THREAD) && !defined(_MSC_VER) &&   \
+     !defined(__amd64__) && !defined(__x86_64__) &&   \
+     !defined(__i386__) && !defined(__i386))
+# define read_barrier()  __sync_synchronize()
+#else
+# define read_barrier()  (void)0
+#endif
+
+static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args)
 {
     /* Invoked by the helpers generated from extern "Python" in the cdef.
 
@@ -164,6 +172,21 @@
        at least 8 bytes in size.
     */
     int err = 0;
+
+    /* This read barrier is needed for _embedding.h.  It is paired
+       with the write_barrier() there.  Without this barrier, we can
+       in theory see the following situation: the Python
+       initialization code already ran (in another thread), and the
+       '_cffi_call_python' function pointer directed execution here;
+       but any number of other data could still be seen as
+       uninitialized below.  For example, 'externpy' would still
+       contain NULLs even though it was correctly set up, or
+       'interpreter_lock' (the GIL inside CPython) would still be seen
+       as NULL, or 'autoInterpreterState' (used by
+       PyGILState_Ensure()) would be NULL or contain bogus fields.
+    */
+    read_barrier();
+
     save_errno();
 
     /* We need the infotuple here.  We could always go through
diff --git a/c/cffi1_module.c b/c/cffi1_module.c
--- a/c/cffi1_module.c
+++ b/c/cffi1_module.c
@@ -3,7 +3,7 @@
 #include "realize_c_type.c"
 
 #define CFFI_VERSION_MIN    0x2601
-#define CFFI_VERSION_MAX    0x26FF
+#define CFFI_VERSION_MAX    0x27FF
 
 typedef struct FFIObject_s FFIObject;
 typedef struct LibObject_s LibObject;
@@ -214,5 +214,12 @@
                              (PyObject *)lib) < 0)
         return NULL;
 
+#if PY_MAJOR_VERSION >= 3
+    /* add manually 'module_name' in sys.modules: it seems that 
+       Py_InitModule() is not enough to do that */
+    if (PyDict_SetItemString(modules_dict, module_name, m) < 0)
+        return NULL;
+#endif
+
     return m;
 }
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -12,7 +12,7 @@
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.4.2", ("This test_c.py file is for testing a version"
+assert __version__ == "1.4.3", ("This test_c.py file is for testing a version"
                                 " of cffi that differs from the one that we"
                                 " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
diff --git a/cffi/__init__.py b/cffi/__init__.py
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.4.2"
-__version_info__ = (1, 4, 2)
+__version__ = "1.4.3"
+__version_info__ = (1, 4, 3)
 
 # 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/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -146,8 +146,9 @@
     ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
 #define _cffi_convert_array_from_object                                  \
     ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
+#define _CFFI_CPIDX  25
 #define _cffi_call_python                                                \
-    ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[25])
+    ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX])
 #define _CFFI_NUM_EXPORTS 26
 
 typedef struct _ctypedescr CTypeDescrObject;
@@ -206,7 +207,8 @@
 /**********  end CPython-specific section  **********/
 #else
 _CFFI_UNUSED_FN
-static void (*_cffi_call_python)(struct _cffi_externpy_s *, char *);
+static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *);
+# define _cffi_call_python  _cffi_call_python_org
 #endif
 
 
diff --git a/cffi/_embedding.h b/cffi/_embedding.h
new file mode 100644
--- /dev/null
+++ b/cffi/_embedding.h
@@ -0,0 +1,517 @@
+
+/***** Support code for embedding *****/
+
+#if defined(_MSC_VER)
+#  define CFFI_DLLEXPORT  __declspec(dllexport)
+#elif defined(__GNUC__)
+#  define CFFI_DLLEXPORT  __attribute__((visibility("default")))
+#else
+#  define CFFI_DLLEXPORT  /* nothing */
+#endif
+
+
+/* There are two global variables of type _cffi_call_python_fnptr:
+
+   * _cffi_call_python, which we declare just below, is the one called
+     by ``extern "Python"`` implementations.
+
+   * _cffi_call_python_org, which on CPython is actually part of the
+     _cffi_exports[] array, is the function pointer copied from
+     _cffi_backend.
+
+   After initialization is complete, both are equal.  However, the
+   first one remains equal to &_cffi_start_and_call_python until the
+   very end of initialization, when we are (or should be) sure that
+   concurrent threads also see a completely initialized world, and
+   only then is it changed.
+*/
+#undef _cffi_call_python
+typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *);
+static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *);
+static _cffi_call_python_fnptr _cffi_call_python = 
&_cffi_start_and_call_python;
+
+
+#ifndef _MSC_VER
+   /* --- Assuming a GCC not infinitely old --- */
+# define cffi_compare_and_swap(l,o,n)  __sync_bool_compare_and_swap(l,o,n)
+# define cffi_write_barrier()          __sync_synchronize()
+# if !defined(__amd64__) && !defined(__x86_64__) &&   \
+     !defined(__i386__) && !defined(__i386)
+#   define cffi_read_barrier()         __sync_synchronize()
+# else
+#   define cffi_read_barrier()         (void)0
+# endif
+#else
+   /* --- Windows threads version --- */
+# include <Windows.h>
+# define cffi_compare_and_swap(l,o,n) \
+                               (InterlockedCompareExchangePointer(l,n,o) == 
(o))
+# define cffi_write_barrier()       
InterlockedCompareExchange(&_cffi_dummy,0,0)
+# define cffi_read_barrier()           (void)0
+static volatile LONG _cffi_dummy;
+#endif
+
+#ifdef WITH_THREAD
+# ifndef _MSC_VER
+#  include <pthread.h>
+   static pthread_mutex_t _cffi_embed_startup_lock;
+# else
+   static CRITICAL_SECTION _cffi_embed_startup_lock;
+# endif
+  static char _cffi_embed_startup_lock_ready = 0;
+#endif
+
+static void _cffi_acquire_reentrant_mutex(void)
+{
+    static void *volatile lock = NULL;
+
+    while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) {
+        /* should ideally do a spin loop instruction here, but
+           hard to do it portably and doesn't really matter I
+           think: pthread_mutex_init() should be very fast, and
+           this is only run at start-up anyway. */
+    }
+
+#ifdef WITH_THREAD
+    if (!_cffi_embed_startup_lock_ready) {
+# ifndef _MSC_VER
+        pthread_mutexattr_t attr;
+        pthread_mutexattr_init(&attr);
+        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+        pthread_mutex_init(&_cffi_embed_startup_lock, &attr);
+# else
+        InitializeCriticalSection(&_cffi_embed_startup_lock);
+# endif
+        _cffi_embed_startup_lock_ready = 1;
+    }
+#endif
+
+    while (!cffi_compare_and_swap(&lock, (void *)1, NULL))
+        ;
+
+#ifndef _MSC_VER
+    pthread_mutex_lock(&_cffi_embed_startup_lock);
+#else
+    EnterCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+static void _cffi_release_reentrant_mutex(void)
+{
+#ifndef _MSC_VER
+    pthread_mutex_unlock(&_cffi_embed_startup_lock);
+#else
+    LeaveCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+
+/**********  CPython-specific section  **********/
+#ifndef PYPY_VERSION
+
+
+#define _cffi_call_python_org  _cffi_exports[_CFFI_CPIDX]
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void);   /* forward */
+
+static void _cffi_py_initialize(void)
+{
+    /* XXX use initsigs=0, which "skips initialization registration of
+       signal handlers, which might be useful when Python is
+       embedded" according to the Python docs.  But review and think
+       if it should be a user-controllable setting.
+
+       XXX we should also give a way to write errors to a buffer
+       instead of to stderr.
+
+       XXX if importing 'site' fails, CPython (any version) calls
+       exit().  Should we try to work around this behavior here?
+    */
+    Py_InitializeEx(0);
+}
+
+static int _cffi_initialize_python(void)
+{
+    /* This initializes Python, imports _cffi_backend, and then the
+       present .dll/.so is set up as a CPython C extension module.
+    */
+    int result;
+    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
+       create and initialize us as a CPython extension module, instead
+       of letting the startup Python code do it---it might reimport
+       the same .dll/.so and get maybe confused on some platforms.
+       It might also have troubles locating the .dll/.so again for all
+       I know.
+    */
+    (void)_CFFI_PYTHON_STARTUP_FUNC();
+    if (PyErr_Occurred())
+        goto error;
+
+    /* Now run the Python code provided to ffi.embedding_init_code().
+     */
+    pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE,
+                              "<init code for '" _CFFI_MODULE_NAME "'>",
+                              Py_file_input);
+    if (pycode == NULL)
+        goto error;
+    global_dict = PyDict_New();
+    if (global_dict == NULL)
+        goto error;
+    if (PyDict_SetItemString(global_dict, "__builtins__",
+                             PyThreadState_GET()->interp->builtins) < 0)
+        goto error;
+    x = PyEval_EvalCode(
+#if PY_MAJOR_VERSION < 3
+                        (PyCodeObject *)
+#endif
+                        pycode, global_dict, global_dict);
+    if (x == NULL)
+        goto error;
+    Py_DECREF(x);
+
+    /* Done!  Now if we've been called from
+       _cffi_start_and_call_python() in an ``extern "Python"``, we can
+       only hope that the Python code did correctly set up the
+       corresponding @ffi.def_extern() function.  Otherwise, the
+       general logic of ``extern "Python"`` functions (inside the
+       _cffi_backend module) will find that the reference is still
+       missing and print an error.
+     */
+    result = 0;
+ done:
+    Py_XDECREF(pycode);
+    Py_XDECREF(global_dict);
+    PyGILState_Release(state);
+    return result;
+
+ error:;
+    {
+        /* Print as much information as potentially useful.
+           Debugging load-time failures with embedding is not fun
+        */
+        PyObject *exception, *v, *tb, *f, *modules, *mod;
+        PyErr_Fetch(&exception, &v, &tb);
+        if (exception != NULL) {
+            PyErr_NormalizeException(&exception, &v, &tb);
+            PyErr_Display(exception, v, tb);
+        }
+        Py_XDECREF(exception);
+        Py_XDECREF(v);
+        Py_XDECREF(tb);
+
+        f = PySys_GetObject((char *)"stderr");
+        if (f != NULL && f != Py_None) {
+            PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
+                               "\ncompiled with cffi version: 1.4.3"
+                               "\n_cffi_backend module: ", f);
+            modules = PyImport_GetModuleDict();
+            mod = PyDict_GetItemString(modules, "_cffi_backend");
+            if (mod == NULL) {
+                PyFile_WriteString("not loaded", f);
+            }
+            else {
+                v = PyObject_GetAttrString(mod, "__file__");
+                PyFile_WriteObject(v, f, 0);
+                Py_XDECREF(v);
+            }
+            PyFile_WriteString("\nsys.path: ", f);
+            PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0);
+            PyFile_WriteString("\n\n", f);
+        }
+    }
+    result = -1;
+    goto done;
+}
+
+PyAPI_DATA(char *) _PyParser_TokenNames[];  /* from CPython */
+
+static int _cffi_carefully_make_gil(void)
+{
+    /* This does the basic initialization of Python.  It can be called
+       completely concurrently from unrelated threads.  It assumes
+       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.
+
+    Python 2
+    ========
+
+       Initialize the GIL, without initializing the rest of Python,
+       by calling PyEval_InitThreads().
+
+       PyEval_InitThreads() 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
+       different cffi-based extension modules.  We choose
+       _PyParser_TokenNames[0] as a completely arbitrary pointer value
+       that is never written to.  The default is to point to the
+       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
+    char *volatile *lock = (char *volatile *)_PyParser_TokenNames;
+    char *old_value;
+
+    while (1) {    /* spin loop */
+        old_value = *lock;
+        if (old_value[0] == 'E') {
+            assert(old_value[1] == 'N');
+            if (cffi_compare_and_swap(lock, old_value, old_value + 1))
+                break;
+        }
+        else {
+            assert(old_value[0] == 'N');
+            /* should ideally do a spin loop instruction here, but
+               hard to do it portably and doesn't really matter I
+               think: PyEval_InitThreads() should be very fast, and
+               this is only run at start-up anyway. */
+        }
+    }
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+    /* Python 3: call Py_InitializeEx() */
+    {
+        PyGILState_STATE state = PyGILState_UNLOCKED;
+        if (!Py_IsInitialized())
+            _cffi_py_initialize();
+        else
+            state = PyGILState_Ensure();
+
+        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 */
+    while (!cffi_compare_and_swap(lock, old_value + 1, old_value))
+        ;
+#endif
+
+    return 0;
+}
+
+/**********  end CPython-specific section  **********/
+
+
+#else
+
+
+/**********  PyPy-specific section  **********/
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]);   /* forward */
+
+static struct _cffi_pypy_init_s {
+    const char *name;
+    void (*func)(const void *[]);
+    const char *code;
+} _cffi_pypy_init = {
+    _CFFI_MODULE_NAME,
+    _CFFI_PYTHON_STARTUP_FUNC,
+    _CFFI_PYTHON_STARTUP_CODE,
+};
+
+extern int pypy_carefully_make_gil(const char *);
+extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *);
+
+static int _cffi_carefully_make_gil(void)
+{
+    return pypy_carefully_make_gil(_CFFI_MODULE_NAME);
+}
+
+static int _cffi_initialize_python(void)
+{
+    return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init);
+}
+
+/**********  end PyPy-specific section  **********/
+
+
+#endif
+
+
+#ifdef __GNUC__
+__attribute__((noinline))
+#endif
+static _cffi_call_python_fnptr _cffi_start_python(void)
+{
+    /* Delicate logic to initialize Python.  This function can be
+       called multiple times concurrently, e.g. when the process calls
+       its first ``extern "Python"`` functions in multiple threads at
+       once.  It can also be called recursively, in which case we must
+       ignore it.  We also have to consider what occurs if several
+       different cffi-based extensions reach this code in parallel
+       threads---it is a different copy of the code, then, and we
+       can't have any shared global variable unless it comes from
+       'libpythonX.Y.so'.
+
+       Idea:
+
+       * _cffi_carefully_make_gil(): "carefully" call
+         PyEval_InitThreads() (possibly with Py_InitializeEx() first).
+
+       * then we use a (local) custom lock to make sure that a call to this
+         cffi-based extension will wait if another call to the *same*
+         extension is running the initialization in another thread.
+         It is reentrant, so that a recursive call will not block, but
+         only one from a different thread.
+
+       * then we grab the GIL and (Python 2) we call Py_InitializeEx().
+         At this point, concurrent calls to Py_InitializeEx() are not
+         possible: we have the GIL.
+
+       * do the rest of the specific initialization, which may
+         temporarily release the GIL but not the custom lock.
+         Only release the custom lock when we are done.
+    */
+    static char called = 0;
+
+    if (_cffi_carefully_make_gil() != 0)
+        return NULL;
+
+    _cffi_acquire_reentrant_mutex();
+
+    /* Here the GIL exists, but we don't have it.  We're only protected
+       from concurrency by the reentrant mutex. */
+
+    /* This file only initializes the embedded module once, the first
+       time this is called, even if there are subinterpreters. */
+    if (!called) {
+        called = 1;  /* invoke _cffi_initialize_python() only once,
+                        but don't set '_cffi_call_python' right now,
+                        otherwise concurrent threads won't call
+                        this function at all (we need them to wait) */
+        if (_cffi_initialize_python() == 0) {
+            /* now initialization is finished.  Switch to the fast-path. */
+
+            /* We would like nobody to see the new value of
+               '_cffi_call_python' without also seeing the rest of the
+               data initialized.  However, this is not possible.  But
+               the new value of '_cffi_call_python' is the function
+               'cffi_call_python()' from _cffi_backend.  So:  */
+            cffi_write_barrier();
+            /* ^^^ we put a write barrier here, and a corresponding
+               read barrier at the start of cffi_call_python().  This
+               ensures that after that read barrier, we see everything
+               done here before the write barrier.
+            */
+
+            assert(_cffi_call_python_org != NULL);
+            _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org;
+        }
+        else {
+            /* initialization failed.  Reset this to NULL, even if it was
+               already set to some other value.  Future calls to
+               _cffi_start_python() are still forced to occur, and will
+               always return NULL from now on. */
+            _cffi_call_python_org = NULL;
+        }
+    }
+
+    _cffi_release_reentrant_mutex();
+
+    return (_cffi_call_python_fnptr)_cffi_call_python_org;
+}
+
+static
+void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args)
+{
+    _cffi_call_python_fnptr fnptr;
+    int current_err = errno;
+#ifdef _MSC_VER
+    int current_lasterr = GetLastError();
+#endif
+    fnptr = _cffi_start_python();
+    if (fnptr == NULL) {
+        fprintf(stderr, "function %s() called, but initialization code "
+                        "failed.  Returning 0.\n", externpy->name);
+        memset(args, 0, externpy->size_of_result);
+    }
+#ifdef _MSC_VER
+    SetLastError(current_lasterr);
+#endif
+    errno = current_err;
+
+    if (fnptr != NULL)
+        fnptr(externpy, args);
+}
+
+
+/* The cffi_start_python() function makes sure Python is initialized
+   and our cffi module is set up.  It can be called manually from the
+   user C code.  The same effect is obtained automatically from any
+   dll-exported ``extern "Python"`` function.  This function returns
+   -1 if initialization failed, 0 if all is OK.  */
+_CFFI_UNUSED_FN
+static int cffi_start_python(void)
+{
+    if (_cffi_call_python == &_cffi_start_and_call_python) {
+        if (_cffi_start_python() == NULL)
+            return -1;
+    }
+    cffi_read_barrier();
+    return 0;
+}
+
+#undef cffi_compare_and_swap
+#undef cffi_write_barrier
+#undef cffi_read_barrier
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -74,6 +74,7 @@
         self._windows_unicode = None
         self._init_once_cache = {}
         self._cdef_version = None
+        self._embedding = None
         if hasattr(backend, 'set_ffi'):
             backend.set_ffi(self)
         for name in backend.__dict__:
@@ -101,13 +102,21 @@
         If 'packed' is specified as True, all structs declared inside this
         cdef are packed, i.e. laid out without any field alignment at all.
         """
+        self._cdef(csource, override=override, packed=packed)
+
+    def embedding_api(self, csource, packed=False):
+        self._cdef(csource, packed=packed, dllexport=True)
+        if self._embedding is None:
+            self._embedding = ''
+
+    def _cdef(self, csource, override=False, **options):
         if not isinstance(csource, str):    # unicode, on Python 2
             if not isinstance(csource, basestring):
                 raise TypeError("cdef() argument must be a string")
             csource = csource.encode('ascii')
         with self._lock:
             self._cdef_version = object()
-            self._parser.parse(csource, override=override, packed=packed)
+            self._parser.parse(csource, override=override, **options)
             self._cdefsources.append(csource)
             if override:
                 for cache in self._function_caches:
@@ -533,6 +542,25 @@
                                        ('_UNICODE', '1')]
         kwds['define_macros'] = defmacros
 
+    def _apply_embedding_fix(self, kwds):
+        # must include an argument like "-lpython2.7" for the compiler
+        if '__pypy__' in sys.builtin_module_names:
+            pythonlib = "pypy-c"
+        else:
+            if sys.platform == "win32":
+                template = "python%d%d"
+                if sys.flags.debug:
+                    template = template + '_d'
+            else:
+                template = "python%d.%d"
+            pythonlib = (template %
+                    (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+            if hasattr(sys, 'abiflags'):
+                pythonlib += sys.abiflags
+        libraries = kwds.get('libraries', [])
+        if pythonlib not in libraries:
+            kwds['libraries'] = libraries + [pythonlib]
+
     def set_source(self, module_name, source, source_extension='.c', **kwds):
         if hasattr(self, '_assigned_source'):
             raise ValueError("set_source() cannot be called several times "
@@ -592,14 +620,23 @@
         recompile(self, module_name, source,
                   c_file=filename, call_c_compiler=False, **kwds)
 
-    def compile(self, tmpdir='.', verbose=0):
+    def compile(self, tmpdir='.', verbose=0, target=None):
+        """The 'target' argument gives the final file name of the
+        compiled DLL.  Use '*' to force distutils' choice, suitable for
+        regular CPython C API modules.  Use a file name ending in '.*'
+        to ask for the system's default extension for dynamic libraries
+        (.so/.dll).
+
+        The default is '*' when building a non-embedded C API extension,
+        and (module_name + '.*') when building an embedded library.
+        """
         from .recompiler import recompile
         #
         if not hasattr(self, '_assigned_source'):
             raise ValueError("set_source() must be called before compile()")
         module_name, source, source_extension, kwds = self._assigned_source
         return recompile(self, module_name, source, tmpdir=tmpdir,
-                         source_extension=source_extension,
+                         target=target, source_extension=source_extension,
                          compiler_verbose=verbose, **kwds)
 
     def init_once(self, func, tag):
@@ -626,6 +663,32 @@
             self._init_once_cache[tag] = (True, result)
         return result
 
+    def embedding_init_code(self, pysource):
+        if self._embedding:
+            raise ValueError("embedding_init_code() can only be called once")
+        # fix 'pysource' before it gets dumped into the C file:
+        # - remove empty lines at the beginning, so it starts at "line 1"
+        # - dedent, if all non-empty lines are indented
+        # - check for SyntaxErrors
+        import re
+        match = re.match(r'\s*\n', pysource)
+        if match:
+            pysource = pysource[match.end():]
+        lines = pysource.splitlines() or ['']
+        prefix = re.match(r'\s*', lines[0]).group()
+        for i in range(1, len(lines)):
+            line = lines[i]
+            if line.rstrip():
+                while not line.startswith(prefix):
+                    prefix = prefix[:-1]
+        i = len(prefix)
+        lines = [line[i:]+'\n' for line in lines]
+        pysource = ''.join(lines)
+        #
+        compile(pysource, "cffi_init", "exec")
+        #
+        self._embedding = pysource
+
 
 def _load_backend_lib(backend, name, flags):
     if name is None:
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -220,8 +220,7 @@
         self._included_declarations = set()
         self._anonymous_counter = 0
         self._structnode2type = weakref.WeakKeyDictionary()
-        self._override = False
-        self._packed = False
+        self._options = None
         self._int_constants = {}
         self._recomplete = []
         self._uses_new_feature = None
@@ -281,16 +280,15 @@
             msg = 'parse error\n%s' % (msg,)
         raise api.CDefError(msg)
 
-    def parse(self, csource, override=False, packed=False):
-        prev_override = self._override
-        prev_packed = self._packed
+    def parse(self, csource, override=False, packed=False, dllexport=False):
+        prev_options = self._options
         try:
-            self._override = override
-            self._packed = packed
+            self._options = {'override': override,
+                             'packed': packed,
+                             'dllexport': dllexport}
             self._internal_parse(csource)
         finally:
-            self._override = prev_override
-            self._packed = prev_packed
+            self._options = prev_options
 
     def _internal_parse(self, csource):
         ast, macros, csource = self._parse(csource)
@@ -376,10 +374,13 @@
 
     def _declare_function(self, tp, quals, decl):
         tp = self._get_type_pointer(tp, quals)
-        if self._inside_extern_python:
-            self._declare('extern_python ' + decl.name, tp)
+        if self._options['dllexport']:
+            tag = 'dllexport_python '
+        elif self._inside_extern_python:
+            tag = 'extern_python '
         else:
-            self._declare('function ' + decl.name, tp)
+            tag = 'function '
+        self._declare(tag + decl.name, tp)
 
     def _parse_decl(self, decl):
         node = decl.type
@@ -449,7 +450,7 @@
             prevobj, prevquals = self._declarations[name]
             if prevobj is obj and prevquals == quals:
                 return
-            if not self._override:
+            if not self._options['override']:
                 raise api.FFIError(
                     "multiple declarations of %s (for interactive usage, "
                     "try cdef(xx, override=True))" % (name,))
@@ -728,7 +729,7 @@
             if isinstance(tp, model.StructType) and tp.partial:
                 raise NotImplementedError("%s: using both bitfields and '...;'"
                                           % (tp,))
-        tp.packed = self._packed
+        tp.packed = self._options['packed']
         if tp.completed:    # must be re-completed: it is not opaque any more
             tp.completed = 0
             self._recomplete.append(tp)
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -21,12 +21,14 @@
         allsources.append(os.path.normpath(src))
     return Extension(name=modname, sources=allsources, **kwds)
 
-def compile(tmpdir, ext, compiler_verbose=0):
+def compile(tmpdir, ext, compiler_verbose=0, target_extension=None,
+            embedding=False):
     """Compile a C extension module using distutils."""
 
     saved_environ = os.environ.copy()
     try:
-        outputfilename = _build(tmpdir, ext, compiler_verbose)
+        outputfilename = _build(tmpdir, ext, compiler_verbose,
+                                target_extension, embedding)
         outputfilename = os.path.abspath(outputfilename)
     finally:
         # workaround for a distutils bugs where some env vars can
@@ -36,7 +38,32 @@
                 os.environ[key] = value
     return outputfilename
 
-def _build(tmpdir, ext, compiler_verbose=0):
+def _save_val(name):
+    import distutils.sysconfig
+    config_vars = distutils.sysconfig.get_config_vars()
+    return config_vars.get(name, Ellipsis)
+
+def _restore_val(name, value):
+    import distutils.sysconfig
+    config_vars = distutils.sysconfig.get_config_vars()
+    config_vars[name] = value
+    if value is Ellipsis:
+        del config_vars[name]
+
+def _win32_hack_for_embedding():
+    from distutils.msvc9compiler import MSVCCompiler
+    if not hasattr(MSVCCompiler, '_remove_visual_c_ref_CFFI_BAK'):
+        MSVCCompiler._remove_visual_c_ref_CFFI_BAK = \
+            MSVCCompiler._remove_visual_c_ref
+    MSVCCompiler._remove_visual_c_ref = lambda self,manifest_file: 
manifest_file
+
+def _win32_unhack_for_embedding():
+    from distutils.msvc9compiler import MSVCCompiler
+    MSVCCompiler._remove_visual_c_ref = \
+        MSVCCompiler._remove_visual_c_ref_CFFI_BAK
+
+def _build(tmpdir, ext, compiler_verbose=0, target_extension=None,
+           embedding=False):
     # XXX compact but horrible :-(
     from distutils.core import Distribution
     import distutils.errors, distutils.log
@@ -49,18 +76,29 @@
     options['build_temp'] = ('ffiplatform', tmpdir)
     #
     try:
+        if sys.platform == 'win32' and embedding:
+            _win32_hack_for_embedding()
         old_level = distutils.log.set_threshold(0) or 0
+        old_SO = _save_val('SO')
+        old_EXT_SUFFIX = _save_val('EXT_SUFFIX')
         try:
+            if target_extension is not None:
+                _restore_val('SO', target_extension)
+                _restore_val('EXT_SUFFIX', target_extension)
             distutils.log.set_verbosity(compiler_verbose)
             dist.run_command('build_ext')
+            cmd_obj = dist.get_command_obj('build_ext')
+            [soname] = cmd_obj.get_outputs()
         finally:
             distutils.log.set_threshold(old_level)
+            _restore_val('SO', old_SO)
+            _restore_val('EXT_SUFFIX', old_EXT_SUFFIX)
+            if sys.platform == 'win32' and embedding:
+                _win32_unhack_for_embedding()
     except (distutils.errors.CompileError,
             distutils.errors.LinkError) as e:
         raise VerificationError('%s: %s' % (e.__class__.__name__, e))
     #
-    cmd_obj = dist.get_command_obj('build_ext')
-    [soname] = cmd_obj.get_outputs()
     return soname
 
 try:
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -3,6 +3,7 @@
 from .cffi_opcode import *
 
 VERSION = "0x2601"
+VERSION_EMBEDDED = "0x2701"
 
 
 class GlobalExpr:
@@ -281,6 +282,29 @@
         lines[i:i+1] = self._rel_readlines('parse_c_type.h')
         prnt(''.join(lines))
         #
+        # if we have ffi._embedding != None, we give it here as a macro
+        # and include an extra file
+        base_module_name = self.module_name.split('.')[-1]
+        if self.ffi._embedding is not None:
+            prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))
+            prnt('#define _CFFI_PYTHON_STARTUP_CODE  %s' %
+                 (self._string_literal(self.ffi._embedding),))
+            prnt('#ifdef PYPY_VERSION')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (
+                base_module_name,))
+            prnt('#elif PY_MAJOR_VERSION >= 3')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (
+                base_module_name,))
+            prnt('#else')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (
+                base_module_name,))
+            prnt('#endif')
+            lines = self._rel_readlines('_embedding.h')
+            prnt(''.join(lines))
+            version = VERSION_EMBEDDED
+        else:
+            version = VERSION
+        #
         # then paste the C source given by the user, verbatim.
         prnt('/************************************************************/')
         prnt()
@@ -365,17 +389,16 @@
         prnt()
         #
         # the init function
-        base_module_name = self.module_name.split('.')[-1]
         prnt('#ifdef PYPY_VERSION')
         prnt('PyMODINIT_FUNC')
         prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
         prnt('{')
         if self._num_externpy:
             prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')
-            prnt('        _cffi_call_python = '
+            prnt('        _cffi_call_python_org = '
                  '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
             prnt('    }')
-        prnt('    p[0] = (const void *)%s;' % VERSION)
+        prnt('    p[0] = (const void *)%s;' % version)
         prnt('    p[1] = &_cffi_type_context;')
         prnt('}')
         # on Windows, distutils insists on putting init_cffi_xyz in
@@ -394,14 +417,14 @@
         prnt('PyInit_%s(void)' % (base_module_name,))
         prnt('{')
         prnt('  return _cffi_init("%s", %s, &_cffi_type_context);' % (
-            self.module_name, VERSION))
+            self.module_name, version))
         prnt('}')
         prnt('#else')
         prnt('PyMODINIT_FUNC')
         prnt('init%s(void)' % (base_module_name,))
         prnt('{')
         prnt('  _cffi_init("%s", %s, &_cffi_type_context);' % (
-            self.module_name, VERSION))
+            self.module_name, version))
         prnt('}')
         prnt('#endif')
 
@@ -1123,7 +1146,10 @@
         assert isinstance(tp, model.FunctionPtrType)
         self._do_collect_type(tp)
 
-    def _generate_cpy_extern_python_decl(self, tp, name):
+    def _generate_cpy_dllexport_python_collecttype(self, tp, name):
+        self._generate_cpy_extern_python_collecttype(tp, name)
+
+    def _generate_cpy_extern_python_decl(self, tp, name, dllexport=False):
         prnt = self._prnt
         if isinstance(tp.result, model.VoidType):
             size_of_result = '0'
@@ -1156,7 +1182,11 @@
             size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
                 tp.result.get_c_name(''), size_of_a,
                 tp.result.get_c_name(''), size_of_a)
-        prnt('static %s' % tp.result.get_c_name(name_and_arguments))
+        if dllexport:
+            tag = 'CFFI_DLLEXPORT'
+        else:
+            tag = 'static'
+        prnt('%s %s' % (tag, tp.result.get_c_name(name_and_arguments)))
         prnt('{')
         prnt('  char a[%s];' % size_of_a)
         prnt('  char *p = a;')
@@ -1174,6 +1204,9 @@
         prnt()
         self._num_externpy += 1
 
+    def _generate_cpy_dllexport_python_decl(self, tp, name):
+        self._generate_cpy_extern_python_decl(tp, name, dllexport=True)
+
     def _generate_cpy_extern_python_ctx(self, tp, name):
         if self.target_is_python:
             raise ffiplatform.VerificationError(
@@ -1185,6 +1218,21 @@
         self._lsts["global"].append(
             GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
 
+    def _generate_cpy_dllexport_python_ctx(self, tp, name):
+        self._generate_cpy_extern_python_ctx(tp, name)
+
+    def _string_literal(self, s):
+        def _char_repr(c):
+            # escape with a '\' the characters '\', '"' or (for trigraphs) '?'
+            if c in '\\"?': return '\\' + c
+            if ' ' <= c < '\x7F': return c
+            if c == '\n': return '\\n'
+            return '\\%03o' % ord(c)
+        lines = []
+        for line in s.splitlines(True):
+            lines.append('"%s"' % ''.join([_char_repr(c) for c in line]))
+        return ' \\\n'.join(lines)
+
     # ----------
     # emitting the opcodes for individual types
 
@@ -1311,12 +1359,15 @@
 
 def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
               c_file=None, source_extension='.c', extradir=None,
-              compiler_verbose=1, **kwds):
+              compiler_verbose=1, target=None, **kwds):
     if not isinstance(module_name, str):
         module_name = module_name.encode('ascii')
     if ffi._windows_unicode:
         ffi._apply_windows_unicode(kwds)
     if preamble is not None:
+        embedding = (ffi._embedding is not None)
+        if embedding:
+            ffi._apply_embedding_fix(kwds)
         if c_file is None:
             c_file, parts = _modname_to_file(tmpdir, module_name,
                                              source_extension)
@@ -1325,13 +1376,40 @@
             ext_c_file = os.path.join(*parts)
         else:
             ext_c_file = c_file
-        ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
+        #
+        if target is None:
+            if embedding:
+                target = '%s.*' % module_name
+            else:
+                target = '*'
+        if target == '*':
+            target_module_name = module_name
+            target_extension = None      # use default
+        else:
+            if target.endswith('.*'):
+                target = target[:-2]
+                if sys.platform == 'win32':
+                    target += '.dll'
+                else:
+                    target += '.so'
+            # split along the first '.' (not the last one, otherwise the
+            # preceeding dots are interpreted as splitting package names)
+            index = target.find('.')
+            if index < 0:
+                raise ValueError("target argument %r should be a file name "
+                                 "containing a '.'" % (target,))
+            target_module_name = target[:index]
+            target_extension = target[index:]
+        #
+        ext = ffiplatform.get_extension(ext_c_file, target_module_name, **kwds)
         updated = make_c_source(ffi, module_name, preamble, c_file)
         if call_c_compiler:
             cwd = os.getcwd()
             try:
                 os.chdir(tmpdir)
-                outputfilename = ffiplatform.compile('.', ext, 
compiler_verbose)
+                outputfilename = ffiplatform.compile('.', ext, 
compiler_verbose,
+                                                     target_extension,
+                                                     embedding=embedding)
             finally:
                 os.chdir(cwd)
             return outputfilename
diff --git a/demo/embedding.py b/demo/embedding.py
new file mode 100644
--- /dev/null
+++ b/demo/embedding.py
@@ -0,0 +1,21 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add(int, int);
+""")
+
+ffi.embedding_init_code("""
+    from _embedding_cffi import ffi
+    print("preparing")   # printed once
+
+    @ffi.def_extern()
+    def add(x, y):
+        print("adding %d and %d" % (x, y))
+        return x + y
+""")
+
+ffi.set_source("_embedding_cffi", "")
+
+ffi.compile(verbose=True)
diff --git a/demo/embedding_test.c b/demo/embedding_test.c
new file mode 100644
--- /dev/null
+++ b/demo/embedding_test.c
@@ -0,0 +1,19 @@
+/* Link this program with libembedding_test.so.
+   E.g. with gcc:
+
+      gcc -o embedding_test embedding_test.c _embedding_cffi*.so
+*/
+
+#include <stdio.h>
+
+extern int add(int x, int y);
+
+
+int main(void)
+{
+    int res = add(40, 2);
+    printf("result: %d\n", res);
+    res = add(100, -5);
+    printf("result: %d\n", res);
+    return 0;
+}
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -138,6 +138,8 @@
 for ``lib.__class__`` before version 1.4.
 
 
+.. _cdef:
+
 ffi.cdef(): declaring types and functions
 -----------------------------------------
 
diff --git a/doc/source/conf.py b/doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -47,7 +47,7 @@
 # The short X.Y version.
 version = '1.4'
 # The full version, including alpha/beta/rc tags.
-release = '1.4.2'
+release = '1.4.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/doc/source/embedding.rst b/doc/source/embedding.rst
new file mode 100644
--- /dev/null
+++ b/doc/source/embedding.rst
@@ -0,0 +1,369 @@
+================================
+Using CFFI for embedding
+================================
+
+.. contents::
+
+You can use CFFI to generate a ``.so/.dll`` which exports the API of
+your choice to any C application that wants to link with this
+``.so/.dll``.
+
+The general idea is as follows:
+
+* You write and execute a Python script, which produces a ``.so/.dll``
+  file with the API of your choice.  The script also gives some Python
+  code to be "frozen" inside the ``.so``.
+
+* At runtime, the C application loads this ``.so/.dll`` without having
+  to know that it was produced by Python and CFFI.
+
+* The first time a C function is called, Python is initialized and
+  the frozen Python code is executed.
+
+* The frozen Python code attaches Python functions that implement the
+  C functions of your API, which are then used for all subsequent C
+  function calls.
+
+One of the goals of this approach is to be entirely independent from
+the CPython C API: no ``Py_Initialize()`` nor ``PyRun_SimpleString()``
+nor even ``PyObject``.  It works identically on CPython and PyPy.
+
+.. note:: PyPy release 4.0.1 contains CFFI 1.4 only.
+
+This is entirely *new in version 1.5.*
+
+
+Usage
+-----
+
+.. __: overview.html#embedding
+
+See the `paragraph in the overview page`__ for a quick introduction.
+In this section, we explain every step in more details.  We will use
+here this slightly expanded example:
+
+.. code-block:: c
+
+    /* file plugin.h */
+    typedef struct { int x, y; } point_t;
+    extern int do_stuff(point_t *);
+
+.. code-block:: python
+
+    # file plugin_build.py
+    import cffi
+    ffi = cffi.FFI()
+
+    with open('plugin.h') as f:
+        ffi.embedding_api(f.read())
+
+    ffi.set_source("my_plugin", '''
+        #include "plugin.h"
+    ''')
+
+    ffi.embedding_init_code("""
+        from my_plugin import ffi
+
+        @ffi.def_extern()
+        def do_stuff(p):
+            print("adding %d and %d" % (p.x, p.y))
+            return p.x + p.y
+    """)
+
+    ffi.compile(target="plugin-1.5.*", verbose=True)
+
+Running the code above produces a *DLL*, i,e, a dynamically-loadable
+library.  It is a file with the extension ``.dll`` on Windows or
+``.so`` on other platforms.  As usual, it is produced by generating
+some intermediate ``.c`` code and then calling the regular
+platform-specific C compiler.
+
+Here are some details about the methods used above:
+
+* **ffi.embedding_api(source):** parses the given C source, which
+  declares functions that you want to be exported by the DLL.  It can
+  also declare types, constants and global variables that are part of
+  the C-level API of your DLL.
+
+  The functions that are found in ``source`` will be automatically
+  defined in the ``.c`` file: they will contain code that initializes
+  the Python interpreter the first time any of them is called,
+  followed by code to call the attached Python function (with
+  ``@ffi.def_extern()``, see next point).
+
+  The global variables, on the other hand, are not automatically
+  produced.  You have to write their definition explicitly in
+  ``ffi.set_source()``, as regular C code (see the point after next).
+
+* **ffi.embedding_init_code(python_code):** this gives
+  initialization-time Python source code.  This code is copied
+  ("frozen") inside the DLL.  At runtime, the code is executed when
+  the DLL is first initialized, just after Python itself is
+  initialized.  This newly initialized Python interpreter has got an
+  extra "built-in" module that can be loaded magically without
+  accessing any files, with a line like "``from my_plugin import ffi,
+  lib``".  The name ``my_plugin`` comes from the first argument to
+  ``ffi.set_source()``.  This module represents "the caller's C world"
+  from the point of view of Python.
+
+  The initialization-time Python code can import other modules or
+  packages as usual.  You may have typical Python issues like needing
+  to set up ``sys.path`` somehow manually first.
+
+  For every function declared within ``ffi.embedding_api()``, the
+  initialization-time Python code or one of the modules it imports
+  should use the decorator ``@ffi.def_extern()`` to attach a
+  corresponding Python function to it.
+
+  If the initialization-time Python code fails with an exception, then
+  you get a traceback printed to stderr, along with more information
+  to help you identify problems like wrong ``sys.path``.  If some
+  function remains unattached at the time where the C code tries to
+  call it, an error message is also printed to stderr and the function
+  returns zero/null.
+
+  Note that the CFFI module never calls ``exit()``, but CPython itself
+  contains code that calls ``exit()``, for example if importing
+  ``site`` fails.  This may be worked around in the future.
+
+* **ffi.set_source(c_module_name, c_code):** set the name of the
+  module from Python's point of view.  It also gives more C code which
+  will be included in the generated C code.  In trivial examples it
+  can be an empty string.  It is where you would ``#include`` some
+  other files, define global variables, and so on.  The macro
+  ``CFFI_DLLEXPORT`` is available to this C code: it expands to the
+  platform-specific way of saying "the following declaration should be
+  exported from the DLL".  For example, you would put "``extern int
+  my_glob;``" in ``ffi.embedding_api()`` and "``CFFI_DLLEXPORT int
+  my_glob = 42;``" in ``ffi.set_source()``.
+
+  Currently, any *type* declared in ``ffi.embedding_api()`` must also
+  be present in the ``c_code``.  This is automatic if this code
+  contains a line like ``#include "plugin.h"`` in the example above.
+
+* **ffi.compile([target=...] [, verbose=True]):** make the C code and
+  compile it.  By default, it produces a file called
+  ``c_module_name.dll`` or ``c_module_name.so``, but the default can
+  be changed with the optional ``target`` keyword argument.  You can
+  use ``target="foo.*"`` with a literal ``*`` to ask for a file called
+  ``foo.dll`` on Windows or ``foo.so`` elsewhere.  One reason for
+  specifying an alternate ``target`` is to include characters not
+  usually allowed in Python module names, like "``plugin-1.5.*``".
+
+  For more complicated cases, you can call instead
+  ``ffi.emit_c_code("foo.c")`` and compile the resulting ``foo.c``
+  file using other means.  CFFI's compilation logic is based on the
+  standard library ``distutils`` package, which is really developed
+  and tested for the purpose of making CPython extension modules, not
+  other DLLs.
+
+
+More reading
+------------
+
+If you're reading this page about embedding and you are not familiar
+with CFFI already, here are a few pointers to what you could read
+next:
+
+* For the ``@ffi.def_extern()`` functions, integer C types are passed
+  simply as Python integers; and simple pointers-to-struct and basic
+  arrays are all straightforward enough.  However, sooner or later you
+  will need to read about this topic in more details here__.
+
+* ``@ffi.def_extern()``: see `documentation here,`__ notably on what
+  happens if the Python function raises an exception.
+
+* To create Python objects attached to C data, one common solution is
+  to use ``ffi.new_handle()``.  See documentation here__.
+
+* In embedding mode, the major direction is C code that calls Python
+  functions.  This is the opposite of the regular extending mode of
+  CFFI, in which the major direction is Python code calling C.  That's
+  why the page `Using the ffi/lib objects`_ talks first about the
+  latter, and why the direction "C code that calls Python" is
+  generally referred to as "callbacks" in that page.  If you also
+  need to have your Python code call C code, read more about
+  `Embedding and Extending`_ below.
+
+* ``ffi.embedding_api(source)``: follows the same syntax as
+  ``ffi.cdef()``, `documented here.`__  You can use the "``...``"
+  syntax as well, although in practice it may be less useful than it
+  is for ``cdef()``.  On the other hand, it is expected that often the
+  C sources that you need to give to ``ffi.embedding_api()`` would be
+  exactly the same as the content of some ``.h`` file that you want to
+  give to users of your DLL.  That's why the example above does this::
+
+      with open('foo.h') as f:
+          ffi.embedding(f.read())
+
+  Note that a drawback of this approach is that ``ffi.embedding()``
+  doesn't support ``#ifdef`` directives.  You may have to use a more
+  convoluted expression like::
+
+      with open('foo.h') as f:
+          lines = [line for line in f if not line.startswith('#')]
+          ffi.embedding(''.join(lines))
+
+  As in the example above, you can also use the same ``foo.h`` from
+  ``ffi.set_source()``::
+
+      ffi.set_source('module_name', '#include "foo.h"')
+
+
+.. __: using.html#working
+.. __: using.html#def-extern
+.. __: using.html#ffi-new_handle
+.. __: cdef.html#cdef
+
+.. _`Using the ffi/lib objects`: using.html
+
+
+Troubleshooting
+---------------
+
+The error message
+
+    cffi extension module 'c_module_name' has unknown version 0x2701
+
+means that the running Python interpreter located a CFFI version older
+than 1.5.  CFFI 1.5 or newer must be installed in the running Python.
+
+
+Using multiple CFFI-made DLLs
+-----------------------------
+
+Multiple CFFI-made DLLs can be used by the same process.
+
+Note that all CFFI-made DLLs in a process share a single Python
+interpreter.  The effect is the same as the one you get by trying to
+build a large Python application by assembling a lot of unrelated
+packages.  Some of these might be libraries that monkey-patch some
+functions from the standard library, for example, which might be
+unexpected from other parts.
+
+
+Multithreading
+--------------
+
+Multithreading should work transparently, based on Python's standard
+Global Interpreter Lock.
+
+If two threads both try to call a C function when Python is not yet
+initialized, then locking occurs.  One thread proceeds with
+initialization and blocks the other thread.  The other thread will be
+allowed to continue only when the execution of the initialization-time
+Python code is done.
+
+If the two threads call two *different* CFFI-made DLLs, the Python
+initialization itself will still be serialized, but the two pieces of
+initialization-time Python code will not.  The idea is that there is a
+priori no reason for one DLL to wait for initialization of the other
+DLL to be complete.
+
+After initialization, Python's standard Global Interpreter Lock kicks
+in.  The end result is that when one CPU progresses on executing
+Python code, no other CPU can progress on executing more Python code
+from another thread of the same process.  At regular intervals, the
+lock switches to a different thread, so that no single thread should
+appear to block indefinitely.
+
+
+Testing
+-------
+
+For testing purposes, a CFFI-made DLL can be imported in a running
+Python interpreter instead of being loaded like a C shared library.
+
+You might have some issues with the file name: for example, on
+Windows, Python expects the file to be called ``c_module_name.pyd``,
+but the CFFI-made DLL is called ``target.dll`` instead.  The base name
+``target`` is the one specified in ``ffi.compile()``, and on Windows
+the extension is ``.dll`` instead of ``.pyd``.  You have to rename or
+copy the file, or on POSIX use a symlink.
+
+The module then works like a regular CFFI extension module.  It is
+imported with "``from c_module_name import ffi, lib``" and exposes on
+the ``lib`` object all C functions.  You can test it by calling these
+C functions.  The initialization-time Python code frozen inside the
+DLL is executed the first time such a call is done.
+
+
+Embedding and Extending
+-----------------------
+
+The embedding mode is not incompatible with the non-embedding mode of
+CFFI.
+
+You can use *both* ``ffi.embedding_api()`` and ``ffi.cdef()`` in the
+same build script.  You put in the former the declarations you want to
+be exported by the DLL; you put in the latter only the C functions and
+types that you want to share between C and Python, but not export from
+the DLL.
+
+As an example of that, consider the case where you would like to have
+a DLL-exported C function written in C directly, maybe to handle some
+cases before calling Python functions.  To do that, you must *not* put
+the function's signature in ``ffi.embedding_api()``.  (Note that this
+requires more hacks if you use ``ffi.embedding(f.read())``.)  You must
+only write the custom function definition in ``ffi.set_source()``, and
+prefix it with the macro CFFI_DLLEXPORT:
+
+.. code-block:: c
+
+    CFFI_DLLEXPORT int myfunc(int a, int b)
+    {
+        /* implementation here */
+    }
+
+This function can, if it wants, invoke Python functions using the
+general mechanism of "callbacks"---called this way because it is a
+call from C to Python, although in this case it is not calling
+anything back:
+
+.. code-block:: python
+
+    ffi.cdef("""
+        extern "Python" int mycb(int);
+    """)
+
+    ffi.set_source("my_plugin", """
+
+        static int mycb(int);   /* the callback: forward declaration, to make
+                                   it accessible from the C code that follows 
*/
+
+        CFFI_DLLEXPORT int myfunc(int a, int b)
+        {
+            int product = a * b;   /* some custom C code */
+            return mycb(product);
+        }
+    """)
+
+and then the Python initialization code needs to contain the lines:
+
+.. code-block:: python
+
+    @ffi.def_extern()
+    def mycb(x):
+        print "hi, I'm called with x =", x
+        return x * 10
+
+This ``@ffi.def_extern`` is attaching a Python function to the C
+callback ``mycb()``, which in this case is not exported from the DLL.
+Nevertheless, the automatic initialization of Python occurs when
+``mycb()`` is called, if it happens to be the first function called
+from C.  More precisely, it does not happen when ``myfunc()`` is
+called: this is just a C function, with no extra code magically
+inserted around it.  It only happens when ``myfunc()`` calls
+``mycb()``.
+
+As the above explanation hints, this is how ``ffi.embedding_api()``
+actually implements function calls that directly invoke Python code;
+here, we have merely decomposed it explicitly, in order to add some
+custom C code in the middle.
+
+In case you need to force, from C code, Python to be initialized
+before the first ``@ffi.def_extern()`` is called, you can do so by
+calling the C function ``cffi_start_python()`` with no argument.  It
+returns an integer, 0 or -1, to tell if the initialization succeeded
+or not.  Currently there is no way to prevent a failing initialization
+from also dumping a traceback and more information to stderr.
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -18,6 +18,7 @@
    overview
    using
    cdef
+   embedding
 
 
 Goals
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -51,11 +51,11 @@
 
 Download and Installation:
 
-* http://pypi.python.org/packages/source/c/cffi/cffi-1.4.2.tar.gz
+* http://pypi.python.org/packages/source/c/cffi/cffi-1.4.3.tar.gz
 
-   - MD5: 81357fe5042d00650b85b728cc181df2
+   - MD5: ...
 
-   - SHA: 76cff6f1ff5bfb2b9c6c8e2cfa8bf90b5c944394
+   - SHA: ...
 
 * Or grab the most current version from the `Bitbucket page`_:
   ``hg clone https://bitbucket.org/cffi/cffi``
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -287,6 +287,54 @@
 distributed in precompiled form like any other extension module.*
 
 
+.. _embedding:
+
+Embedding
+---------
+
+*New in version 1.5.*
+
+CFFI can be used for embedding__: creating a standard
+dynamically-linked library (``.dll`` under Windows, ``.so`` elsewhere)
+which can be used from a C application.
+
+.. code-block:: python
+
+    import cffi
+    ffi = cffi.FFI()
+
+    ffi.embedding_api("""
+        int do_stuff(int, int);
+    """)
+
+    ffi.set_source("my_plugin", "")
+
+    ffi.embedding_init_code("""
+        from my_plugin import ffi
+
+        @ffi.def_extern()
+        def do_stuff(x, y):
+            print("adding %d and %d" % (x, y))
+            return x + y
+    """)
+
+    ffi.compile(target="plugin-1.5.*", verbose=True)
+
+This simple example creates ``plugin-1.5.dll`` or ``plugin-1.5.so`` as
+a DLL with a single exported function, ``do_stuff()``.  You execute
+the script above once, with the interpreter you want to have
+internally used; it can be CPython 2.x or 3.x or PyPy.  This DLL can
+then be used "as usual" from an application; the application doesn't
+need to know that it is talking with a library made with Python and
+CFFI.  At runtime, when the application calls ``int do_stuff(int,
+int)``, the Python interpreter is automatically initialized and ``def
+do_stuff(x, y):`` gets called.  `See the details in the documentation
+about embedding.`__
+
+.. __: embedding.html
+.. __: embedding.html
+
+
 What actually happened?
 -----------------------
 
diff --git a/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -423,6 +423,7 @@
 with ``int foo();`` really means ``int foo(void);``.)
 
 
+.. _extern-python:
 .. _`extern "Python"`:
 
 Extern "Python" (new-style callbacks)
@@ -603,6 +604,7 @@
         }
     """)
 
+
 Extern "Python": reference
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -629,6 +631,8 @@
 return a default value.  This can be controlled with ``error`` and
 ``onerror``, described below.
 
+.. _def-extern:
+
 The ``@ffi.def_extern()`` decorator takes these optional arguments:
 
 * ``name``: the name of the function as written in the cdef.  By default
@@ -1066,12 +1070,13 @@
 points in time, and using it in a ``with`` statement.
 
 
+.. _ffi-new_handle:
 .. _`ffi.new_handle()`:
 
 **ffi.new_handle(python_object)**: return a non-NULL cdata of type
 ``void *`` that contains an opaque reference to ``python_object``.  You
 can pass it around to C functions or store it into C structures.  Later,
-you can use **ffi.from_handle(p)** to retrive the original
+you can use **ffi.from_handle(p)** to retrieve the original
 ``python_object`` from a value with the same ``void *`` pointer.
 *Calling ffi.from_handle(p) is invalid and will likely crash if
 the cdata object returned by new_handle() is not kept alive!*
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -144,9 +144,10 @@
 
 `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
 """,
-        version='1.4.2',
+        version='1.4.3',
         packages=['cffi'] if cpython else [],
-        package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h']}
+        package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', 
+                               '_embedding.h']}
                      if cpython else {},
         zip_safe=False,
 
diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py
--- a/testing/cffi0/test_version.py
+++ b/testing/cffi0/test_version.py
@@ -53,3 +53,10 @@
     content = open(p).read()
     #v = BACKEND_VERSIONS.get(v, v)
     assert (('assert __version__ == "%s"' % v) in content)
+
+def test_embedding_h():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    v = cffi.__version__
+    p = os.path.join(parent, 'cffi', '_embedding.h')
+    content = open(p).read()
+    assert ('cffi version: %s"' % (v,)) in content
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
--- a/testing/cffi1/test_zdist.py
+++ b/testing/cffi1/test_zdist.py
@@ -59,11 +59,16 @@
             if (name.endswith('.so') or name.endswith('.pyd') or
                 name.endswith('.dylib')):
                 found_so = os.path.join(curdir, name)
-                # foo.cpython-34m.so => foo
-                name = name.split('.')[0]
-                # foo_d.so => foo (Python 2 debug builds)
+                # foo.so => foo
+                parts = name.split('.')
+                del parts[-1]
+                if len(parts) > 1 and parts[-1] != 'bar':
+                    # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar
+                    del parts[-1]
+                name = '.'.join(parts)
+                # foo_d => foo (Python 2 debug builds)
                 if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
-                    name = name.rsplit('_', 1)[0]
+                    name = name[:-2]
                 name += '.SO'
             if name.startswith('pycparser') and name.endswith('.egg'):
                 continue    # no clue why this shows up sometimes and not 
others
@@ -208,6 +213,58 @@
                         'Release': '?'}})
 
     @chdir_to_tmp
+    def test_api_compile_explicit_target_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(target="foo.bar.*")
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'foo.bar.SO': None,
+                'mod_name_in_package': {'mymod.c': None,
+                                        'mymod.o': None}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'foo.bar.SO': None,
+                'mod_name_in_package': {'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
+    def test_api_compile_explicit_target_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(target=os.path.join("mod_name_in_package", 
"foo.bar.*"))
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.SO': None,
+                                        'mymod.c': None,
+                                        'mymod.o': None}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.SO': None,
+                                        'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
+    def test_api_compile_explicit_target_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(target="foo.bar.baz")
+        if sys.platform != 'win32':
+            self.check_produced_files({
+                'foo.bar.baz': None,
+                'mod_name_in_package': {'mymod.c': None,
+                                        'mymod.o': None}})
+            sofile = os.path.join(str(self.udir), 'foo.bar.baz')
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'foo.bar.baz': None,
+                'mod_name_in_package': {'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
     def test_api_distutils_extension_1(self):
         ffi = cffi.FFI()
         ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
diff --git a/testing/embedding/__init__.py b/testing/embedding/__init__.py
new file mode 100644
diff --git a/testing/embedding/add1-test.c b/testing/embedding/add1-test.c
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add1-test.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+extern int add1(int, int);
+
+
+int main(void)
+{
+    int x, y;
+    x = add1(40, 2);
+    y = add1(100, -5);
+    printf("got: %d %d\n", x, y);
+    return 0;
+}
diff --git a/testing/embedding/add1.py b/testing/embedding/add1.py
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add1.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    import sys, time
+    sys.stdout.write("preparing")
+    for i in range(3):
+        sys.stdout.flush()
+        time.sleep(0.02)
+        sys.stdout.write(".")
+    sys.stdout.write("\n")
+
+    from _add1_cffi import ffi
+
+    int(ord("A"))    # check that built-ins are there
+
+    @ffi.def_extern()
+    def add1(x, y):
+        sys.stdout.write("adding %d and %d\n" % (x, y))
+        sys.stdout.flush()
+        return x + y
+""")
+
+ffi.set_source("_add1_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add2-test.c b/testing/embedding/add2-test.c
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add2-test.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+
+int main(void)
+{
+    int x, y;
+    x = add1(40, 2);
+    y = add2(100, -5, -20);
+    printf("got: %d %d\n", x, y);
+    return 0;
+}
diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add2.py
@@ -0,0 +1,29 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add2(int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    import sys
+    sys.stdout.write("prepADD2\n")
+
+    assert '_add2_cffi' in sys.modules
+    m = sys.modules['_add2_cffi']
+    import _add2_cffi
+    ffi = _add2_cffi.ffi
+
+    @ffi.def_extern()
+    def add2(x, y, z):
+        sys.stdout.write("adding %d and %d and %d\n" % (x, y, z))
+        sys.stdout.flush()
+        return x + y + z
+""")
+
+ffi.set_source("_add2_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add3.py b/testing/embedding/add3.py
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add3.py
@@ -0,0 +1,24 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add3(int, int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    from _add3_cffi import ffi
+    import sys
+
+    @ffi.def_extern()
+    def add3(x, y, z, t):
+        sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t))
+        sys.stdout.flush()
+        return x + y + z + t
+""")
+
+ffi.set_source("_add3_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add_recursive-test.c 
b/testing/embedding/add_recursive-test.c
new file mode 100644
--- /dev/null
+++ b/testing/embedding/add_recursive-test.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#  define DLLIMPORT  __declspec(dllimport)
+#else
+#  define DLLIMPORT  extern
+#endif
+
+DLLIMPORT int add_rec(int, int);
+DLLIMPORT int (*my_callback)(int);
+
+static int some_callback(int x)
+{
+    printf("some_callback(%d)\n", x);
+    fflush(stdout);
+    return add_rec(x, 9);
+}
+
+int main(void)
+{
+    int x, y;
+    my_callback = some_callback;
+    x = add_rec(40, 2);
+    y = add_rec(100, -5);
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to