Embedding Cython modules in C/C++ applications

This is a stub documentation page. PRs very welcome.

Quick links:

Initialising your main module

Most importantly, DO NOT call the module init function instead of importing the module. This is not the right way to initialise an extension module. (It was always wrong but used to work before, but since Python 3.5, it is wrong and no longer works.)

For details, see the documentation of the module init function in CPython and PEP 489 regarding the module initialisation mechanism in CPython 3.5 and later.

The PyImport_AppendInittab() function in CPython allows registering statically (or dynamically) linked extension modules for later imports. An example is given in the documentation of the module init function that is linked above.

Embedding example code

The following is a simple example that shows the main steps for embedding a Cython module (embedded.pyx) in Python 3.x.

First, here is a Cython module that exports a C function to be called by external code. Note that the say_hello_from_python() function is declared as public to export it as a linker symbol that can be used by other C files, which in this case is embedded_main.c.

# embedded.pyx

# The following two lines are for test purposed only, please ignore them.
# distutils: sources = embedded_main.c
# tag: py3only

TEXT_TO_SAY = 'Hello from Python!'

cdef public int say_hello_from_python() except -1:
    print(TEXT_TO_SAY)
    return 0

The C main() function of your program could look like this:

 1/* embedded_main.c */
 2
 3/* This include file is automatically generated by Cython for 'public' functions. */
 4#include "embedded.h"
 5
 6#ifdef __cplusplus
 7extern "C" {
 8#endif
 9
10int
11main(int argc, char *argv[])
12{
13    PyObject *pmodule;
14    wchar_t *program;
15
16    program = Py_DecodeLocale(argv[0], NULL);
17    if (program == NULL) {
18        fprintf(stderr, "Fatal error: cannot decode argv[0], got %d arguments\n", argc);
19        exit(1);
20    }
21
22    /* Add a built-in module, before Py_Initialize */
23    if (PyImport_AppendInittab("embedded", PyInit_embedded) == -1) {
24        fprintf(stderr, "Error: could not extend in-built modules table\n");
25        exit(1);
26    }
27
28    /* Pass argv[0] to the Python interpreter */
29    Py_SetProgramName(program);
30
31    /* Initialize the Python interpreter.  Required.
32       If this step fails, it will be a fatal error. */
33    Py_Initialize();
34
35    /* Optionally import the module; alternatively,
36       import can be deferred until the embedded script
37       imports it. */
38    pmodule = PyImport_ImportModule("embedded");
39    if (!pmodule) {
40        PyErr_Print();
41        fprintf(stderr, "Error: could not import module 'embedded'\n");
42        goto exit_with_error;
43    }
44
45    /* Now call into your module code. */
46    if (say_hello_from_python() < 0) {
47        PyErr_Print();
48        fprintf(stderr, "Error in Python code, exception was printed.\n");
49        goto exit_with_error;
50    }
51
52    /* ... */
53
54    /* Clean up after using CPython. */
55    PyMem_RawFree(program);
56    Py_Finalize();
57
58    return 0;
59
60    /* Clean up in the error cases above. */
61exit_with_error:
62    PyMem_RawFree(program);
63    Py_Finalize();
64    return 1;
65}
66
67#ifdef __cplusplus
68}
69#endif

(Adapted from the CPython documentation.)

Instead of writing such a main() function yourself, you can also let Cython generate one into your module’s C file with the cython --embed option. Or use the cython_freeze script to embed multiple modules. See the embedding demo program for a complete example setup.