[pypy-commit] pypy default: Updated the embedding documentation to contain only the modern way we
arigo
noreply at buildbot.pypy.org
Thu May 14 11:12:39 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r77318:88b5d686f575
Date: 2015-05-14 11:12 +0200
http://bitbucket.org/pypy/pypy/changeset/88b5d686f575/
Log: Updated the embedding documentation to contain only the modern way
we look at it. Thanks Amaury on pypy-dev for providing the basic
example.
diff --git a/pypy/doc/embedding.rst b/pypy/doc/embedding.rst
--- a/pypy/doc/embedding.rst
+++ b/pypy/doc/embedding.rst
@@ -51,6 +51,9 @@
otherwise return 0. You should really do your own error handling in the
source. It'll acquire the GIL.
+ Note: this is meant to be called *only once* or a few times at most. See
+ the `more complete example`_ below.
+
.. function:: int pypy_execute_source_ptr(char* source, void* ptr);
.. note:: Not available in PyPy <= 2.2.1
@@ -65,8 +68,9 @@
Note that this function is not thread-safe itself, so you need to guard it
with a mutex.
-Simple example
---------------
+
+Minimal example
+---------------
Note that this API is a lot more minimal than say CPython C API, so at first
it's obvious to think that you can't do much. However, the trick is to do
@@ -78,10 +82,10 @@
.. code-block:: c
- #include "include/PyPy.h"
+ #include "PyPy.h"
#include <stdio.h>
- const char source[] = "print 'hello from pypy'";
+ static char source[] = "print 'hello from pypy'";
int main(void)
{
@@ -103,154 +107,115 @@
If we save it as ``x.c`` now, compile it and run it (on linux) with::
- fijal at hermann:/opt/pypy$ gcc -o x x.c -lpypy-c -L.
- fijal at hermann:/opt/pypy$ LD_LIBRARY_PATH=. ./x
+ $ gcc -g -o x x.c -lpypy-c -L/opt/pypy/bin -I/opt/pypy/include
+ $ LD_LIBRARY_PATH=/opt/pypy/bin ./x
hello from pypy
-on OSX it is necessary to set the rpath of the binary if one wants to link to it::
+.. note:: If the compilation fails because of missing PyPy.h header file,
+ you are running PyPy <= 2.2.1. Get it here__.
+
+.. __: https://bitbucket.org/pypy/pypy/raw/c4cd6eca9358066571500ac82aaacfdaa3889e8c/include/PyPy.h
+
+On OSX it is necessary to set the rpath of the binary if one wants to link to it,
+with a command like::
gcc -o x x.c -lpypy-c -L. -Wl,-rpath -Wl, at executable_path
./x
hello from pypy
-Worked!
-.. note:: If the compilation fails because of missing PyPy.h header file,
- you are running PyPy <= 2.2.1, please see the section `Missing PyPy.h`_.
-
-Missing PyPy.h
---------------
-
-.. note:: PyPy.h is in the nightly builds and goes to new PyPy releases (>2.2.1).
-
-For PyPy <= 2.2.1, you can download PyPy.h from PyPy repository (it has been added in commit c4cd6ec):
-
-.. code-block:: bash
-
- cd /opt/pypy/include
- wget https://bitbucket.org/pypy/pypy/raw/c4cd6eca9358066571500ac82aaacfdaa3889e8c/include/PyPy.h
-
-
-More advanced example
+More complete example
---------------------
.. note:: This example depends on pypy_execute_source_ptr which is not available
- in PyPy <= 2.2.1. You might want to see the alternative example
- below.
+ in PyPy <= 2.2.1.
Typically we need something more to do than simply execute source. The following
is a fully fledged example, please consult cffi documentation for details.
It's a bit longish, but it captures a gist what can be done with the PyPy
embedding interface:
+.. code-block:: python
+
+ # file "interface.py"
+
+ import cffi
+
+ ffi = cffi.FFI()
+ ffi.cdef('''
+ struct API {
+ double (*add_numbers)(double x, double y);
+ };
+ ''')
+
+ # Better define callbacks at module scope, it's important to
+ # keep this object alive.
+ @ffi.callback("double (double, double)")
+ def add_numbers(x, y):
+ return x + y
+
+ def fill_api(ptr):
+ global api
+ api = ffi.cast("struct API*", ptr)
+ api.add_numbers = add_numbers
+
.. code-block:: c
- #include "include/PyPy.h"
+ /* C example */
+ #include "PyPy.h"
#include <stdio.h>
- char source[] = "from cffi import FFI\n\
- ffi = FFI()\n\
- @ffi.callback('int(int)')\n\
- def func(a):\n\
- print 'Got from C %d' % a\n\
- return a * 2\n\
- ffi.cdef('int callback(int (*func)(int));')\n\
- c_func = ffi.cast('int(*)(int(*)(int))', c_argument)\n\
- c_func(func)\n\
- print 'finished the Python part'\n\
- ";
+ struct API {
+ double (*add_numbers)(double x, double y);
+ };
- int callback(int (*func)(int))
+ struct API api; /* global var */
+
+ int initialize_api(void)
{
- printf("Calling to Python, result: %d\n", func(3));
- }
-
- int main()
- {
+ static char source[] =
+ "import sys; sys.path.insert(0, '.'); "
+ "import interface; interface.fill_api(c_argument)";
int res;
- void *lib, *func;
rpython_startup_code();
res = pypy_setup_home("/opt/pypy/bin/libpypy-c.so", 1);
if (res) {
- printf("Error setting pypy home!\n");
+ fprintf(stderr, "Error setting pypy home!\n");
+ return -1;
+ }
+ res = pypy_execute_source_ptr(source, &api);
+ if (res) {
+ fprintf(stderr, "Error calling pypy_execute_source_ptr!\n");
+ return -1;
+ }
+ return 0;
+ }
+
+ int main(void)
+ {
+ if (initialize_api() < 0)
return 1;
- }
- res = pypy_execute_source_ptr(source, (void*)callback);
- if (res) {
- printf("Error calling pypy_execute_source_ptr!\n");
- }
- return res;
+
+ printf("sum: %f\n", api.add_numbers(12.3, 45.6));
+
+ return 0;
}
you can compile and run it with::
- fijal at hermann:/opt/pypy$ gcc -g -o x x.c -lpypy-c -L.
- fijal at hermann:/opt/pypy$ LD_LIBRARY_PATH=. ./x
- Got from C 3
- Calling to Python, result: 6
- finished the Python part
+ $ gcc -g -o x x.c -lpypy-c -L/opt/pypy/bin -I/opt/pypy/include
+ $ LD_LIBRARY_PATH=/opt/pypy/bin ./x
+ sum: 57.900000
-As you can see, we successfully managed to call Python from C and C from
-Python. Now having one callback might not be enough, so what typically happens
-is that we would pass a struct full of callbacks to ``pypy_execute_source_ptr``
-and fill the structure from Python side for the future use.
+As you can see, what we did is create a ``struct API`` that contains
+the custom API that we need in our particular case. This struct is
+filled by Python to contain a function pointer that is then called
+form the C side. It is also possible to do have other function
+pointers that are filled by the C side and called by the Python side,
+or even non-function-pointer fields: basically, the two sides
+communicate via this single C structure that defines your API.
-Alternative example
--------------------
-
-As ``pypy_execute_source_ptr`` is not available in PyPy 2.2.1, you might want to try
-an alternative approach which relies on -export-dynamic flag to the GNU linker.
-The downside to this approach is that it is platform dependent.
-
-.. code-block:: c
-
- #include "include/PyPy.h"
- #include <stdio.h>
-
- char source[] = "from cffi import FFI\n\
- ffi = FFI()\n\
- @ffi.callback('int(int)')\n\
- def func(a):\n\
- print 'Got from C %d' % a\n\
- return a * 2\n\
- ffi.cdef('int callback(int (*func)(int));')\n\
- lib = ffi.verify('int callback(int (*func)(int));')\n\
- lib.callback(func)\n\
- print 'finished the Python part'\n\
- ";
-
- int callback(int (*func)(int))
- {
- printf("Calling to Python, result: %d\n", func(3));
- }
-
- int main()
- {
- int res;
- void *lib, *func;
-
- rpython_startup_code();
- res = pypy_setup_home("/opt/pypy/bin/libpypy-c.so", 1);
- if (res) {
- printf("Error setting pypy home!\n");
- return 1;
- }
- res = pypy_execute_source(source);
- if (res) {
- printf("Error calling pypy_execute_source!\n");
- }
- return res;
- }
-
-
-Make sure to pass -export-dynamic flag when compiling::
-
- $ gcc -g -o x x.c -lpypy-c -L. -export-dynamic
- $ LD_LIBRARY_PATH=. ./x
- Got from C 3
- Calling to Python, result: 6
- finished the Python part
Finding pypy_home
-----------------
More information about the pypy-commit
mailing list