[pypy-commit] extradoc extradoc: in-progress
arigo
pypy.commits at gmail.com
Wed Jan 6 05:18:18 EST 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: extradoc
Changeset: r5591:d5d6783367e1
Date: 2016-01-06 11:18 +0100
http://bitbucket.org/pypy/extradoc/changeset/d5d6783367e1/
Log: in-progress
diff --git a/blog/draft/cffi-embedding.rst b/blog/draft/cffi-embedding.rst
--- a/blog/draft/cffi-embedding.rst
+++ b/blog/draft/cffi-embedding.rst
@@ -6,44 +6,55 @@
Python programs, in a way that is both simple and that works across
CPython 2.x and 3.x and PyPy.
-We are now adding support for *embedding* Python inside non-Python
-programs. This is traditionally done using the CPython C API: from C
-code, you call ``Py_Initialize()`` and then some other functions like
+The major news of CFFI 1.4, released last december, was that you can
+now declare C functions with ``extern "Python"``, in the ``cdef()``.
+These magic keywords make the function callable from C (where it is
+defined automatically), but calling it will call some Python code
+(which you attach with the ``@ffi.def_extern()`` decorator). This is
+useful because it gives a more straightforward, faster and
+libffi-independent way to write callbacks. For more details, see `the
+documentation`_.
+
+You are, in effect, declaring a static family of C functions which
+call Python code. The idea is to take pointers to them, and pass them
+around to other C functions, as callbacks. However, the idea of a set
+of C functions which call Python code opens another path: *embedding*
+Python code inside non-Python programs.
+
+Embedding is traditionally done using the CPython C API: from C code,
+you call ``Py_Initialize()`` and then some other functions like
``PyRun_SimpleString()``. In the simple cases it is, indeed, simple
-enough; but it can become a more complicated story if you throw in
-supporting application-dependent object types, and correctly running
-on multiple threads, and so on.
+enough; but it can become a complicated story if you throw in
+supporting application-dependent object types; and a messy story if
+you add correctly running on multiple threads, for example.
-Moreover, this approach is specific to CPython (2.x or 3.x, which you
-can do in a similar way). It does not work on PyPy, which has its own
-smaller but very different `embedding API`_.
+Moreover, this approach is specific to CPython (2.x or 3.x). It does
+not work at all on PyPy, which has its own very different, minimal
+`embedding API`_.
-The new-and-coming thing about CFFI, meant as replacement of the above
-solutions, is direct embedding support---and it does that with no
-fixed API at all. The idea is to write some Python script with a
-``cdef()`` which declares a number of ``extern "Python"`` functions.
-When running the script, it creates the C source code and compiles it
-to a dynamically-linked library (``.so`` on Linux). This is the same
-as in the regular API-mode usage, and ``extern "Python"`` was
-`introduced in CFFI 1.4`_. What is new is that these ``extern
+The new-and-coming thing about CFFI 1.5, meant as replacement of the
+above solutions, is direct embedding support---with no fixed API at
+all. The idea is to write some Python script with a ``cdef()`` which
+declares a number of ``extern "Python"`` functions. When running the
+script, it creates the C source code and compiles it to a
+dynamically-linked library (``.so`` on Linux). This is the same as in
+the regular API-mode usage. What is new is that these ``extern
"Python"`` can now also be *exported* from the ``.so``, in the C
sense. You also give a bit of initialization-time Python code
-directly in the script, which will be compiled into the ``.so``
-too.
+directly in the script, which will be compiled into the ``.so`` too.
-In other words, this library can now be used directly from any C
-program (and it is still importable in Python). It exposes the C API
-of your choice, which you specified with the ``extern "Python"``
-declarations. You can use it to make whatever custom API makes sense
-in your particular case. You can even directly make a "plug-in" for
-any program that supports them, just by exporting the API expected for
-such plugins.
+This library can now be used directly from any C program (and it is
+still importable in Python). It exposes the C API of your choice,
+which you specified with the ``extern "Python"`` declarations. You
+can use it to make whatever custom API makes sense in your particular
+case. You can even directly make a "plug-in" for any program that
+supports them, just by exporting the API expected for such plugins.
-This is still being finalized, but please try it out. (You can also see
-`embedding.py`_ directly online for a quick glance.) These are the
-instructions on Linux with CPython 2.7 (CPython 3.x and non-Linux
-platforms are still a work in progress right now, but this should be
-quickly fixed):
+This is still being finalized, but please try it out. (You can also
+see `embedding.py`_ directly online for a quick glance.) Here are
+below the instructions on Linux with CPython 2.7 (CPython 3.x and
+non-Linux platforms are still a work in progress right now, but this
+should be quickly fixed):
* get the branch ``static-callback-embedding`` of CFFI::
@@ -59,7 +70,7 @@
cd demo
PYTHONPATH=.. python embedding.py
-* run ``gcc`` to build the C sources---on Linux::
+* this produces ``_embedding_cffi.c``; run ``gcc`` to build it---on Linux::
gcc -shared -fPIC _embedding_cffi.c -o _embedding_cffi.so -lpython2.7 -I/usr/include/python2.7
@@ -75,22 +86,39 @@
Very similar steps can be followed on PyPy, but it requires the
``cffi-static-callback-embedding`` branch of PyPy, which you must
-first translate from sources.
+first translate from sources. The difference is only that you need to
+adapt the first ``gcc`` command line: replace ``-lpython2.7`` with
+``-lpypy-c`` and to fix the ``-I`` path (and possibly add a ``-L``
+path).
Note that CPython/PyPy is automatically initialized (using locks in
case of multi-threading) the first time any of the ``extern "Python"``
-functions is called from the C program. At that time, the custom
-initialization-time Python code you put in
+functions is called from the C program. (This should work even if two
+different threads call the first time a function from two *different*
+embedded CFFI extensions; in other words, explicit initialization is
+never needed). The custom initialization-time Python code you put in
``ffi.embedding_init_code()`` is executed. If this code starts to be
-big, you may consider moving it to independent modules or packages;
-then the initialization-time Python code only needs to import them
-(possibly after hacking around with ``sys.path``).
+big, you can move it to independent modules or packages. Then the
+initialization-time Python code only needs to import them. In that
+case, you have to carefully set up ``sys.path`` if the modules are not
+installed in the usual Python way.
+
+A better alternative would be to use virtualenv. How to do that is
+not fully fleshed out so far. You can certainly run the whole program
+with the environment variables set up by the virtualenv's ``activate``
+script first. There are probably other solutions that involve using
+gcc's ``-Wl,-rpath=\$ORIGIN/`` or ``-Wl,-rpath=/fixed/path/`` options
+to load a specific libpython or libypypy-c library. If you try it out
+and it doesn't work the way you would like, please complain :-)
Another point: right now this does not support CPython's notion of
multiple subinterpreters. The logic creates a single global Python
-interpreter, and runs everything in that context. Idea about how to
-support that cleanly would be welcome ``:-)`` More generally, any
-feedback is appreciated.
+interpreter, and runs everything in that context. Maybe a future
+version would have an explicit API to do that---or maybe it should be
+the job of a 3rd-party extension module to provide a Python interface
+over the notion of subinterpreters...
+
+More generally, any feedback is appreciated.
Have fun,
More information about the pypy-commit
mailing list