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 purposes only, please ignore them.
# distutils: sources = embedded_main.c
# tag: py3only
# tag: no-cpp

TEXT_TO_SAY = 'Hello from Python!'

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

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

 1/* embedded_main.c */
 3/* This include file is automatically generated by Cython for 'public' functions. */
 4#include "embedded.h"
 6#ifdef __cplusplus
 7extern "C" {
11main(int argc, char *argv[])
13    PyObject *pmodule;
14    wchar_t *program;
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    }
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    }
28    /* Pass argv[0] to the Python interpreter */
29    Py_SetProgramName(program);
31    /* Initialize the Python interpreter.  Required.
32       If this step fails, it will be a fatal error. */
33    Py_Initialize();
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    }
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    }
52    /* ... */
54    /* Clean up after using CPython. */
55    PyMem_RawFree(program);
56    Py_Finalize();
58    return 0;
60    /* Clean up in the error cases above. */
62    PyMem_RawFree(program);
63    Py_Finalize();
64    return 1;
67#ifdef __cplusplus

(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.

Be aware that your application will not contain any external dependencies that you use (including Python standard library modules) and so may not be truly portable. If you want to generate a portable application we recommend using a specialized tool (e.g. PyInstaller or cx_freeze) to find and bundle these dependencies.