How to embed Python code in C program

Python is one of the most popular programming languages due to its simple syntax, ease of learning and cross-platform support. Besides, many high quality Python libraries and modules are available at your disposal, allowing you to do heavy lifting with only a few lines of code. This makes Python one of the most productive ways to develop prototypes. However, Python is not as fast as C programming language, and many performance-critical production software such as the Linux operating system, web servers and databases are written in C. If you are developing a program in C, but some part of it needs to be written in Python, you can write a Python module for that and embed the Python module in a C program using Python/C API.

In this tutorial let's examine how to use Python/C API to embed Python code in C. The tutorial will conclude with a full example of calling Python from C code.

Step 1: Install Python Development Package

As you need to access Python/C API, first install Python development package.

On Debian, Ubuntu or Linux Mint:

$ sudo apt-get install python2.7-dev

On CentOS, Fedora or RHEL:

$ sudo yum install python-devel

Once the installation is successful, the Python header file will be found in /usr/include/python2.7. Depending on your Linux distribution, the exact path might be different. For example, it is /usr/include/python2.6 on CentOS 6.

Step 2: Initialize Interpreter and Set Path

The first step towards embedding Python in C is to initialize Python interpreter, which can be done with the following C function.

Py_Initialize();

After the interpreter is initialized, you need to set the path to the Python module you would like to import in your C program. For example, let's say your Python module is located in /usr/local/modules. Then use the following C function call to set the path.

PySys_SetPath("/usr/local/modules");

Step 3: Data Conversion

One of the most important aspects of embedding Python in C is data conversion. To pass data to a Python function from C, you need to first convert the data from C data types to Python data types. Python/C API offers various functions to accomplish this. For example, to convert C string to Python string, we use PyString_FromString() function:

PyObject *pval;
char *cString = "Cyberpersons";
pval = PyString_FromString(cString);

Another similar function is PyInt_FromLong(), which converts long data type in C to Python int. Every Python/C API function returns a reference of PyObject type.

Step 4: Define a Python Module

When you want to embed Python code in another language such as C, the code needs to be written in a Python module, which is then "imported" in another language. So let's examine how to import a Python module in C.

For illustration, let's implement a simple example Python module as follows.

def printData(data):
    return data+data+'\n'

The above Python function takes one string as an argument and returns two repetitions of the string. For example, if input string is "cyberpersons," this function returns "cyberpersonscyberpersons." Name this module file as "printData.py" and place the module in the Python module directory declared earlier (/usr/local/modules).

Step 5: Load a Python Module

Now that you have defined your Python module, it's time to load it in a C program. The C code for importing the module looks like this:

// argv[1] specifies the module file name ("printData.py").
pName = PyString_FromString(argv[1]);
pModule = PyImport_Import(pName);

Step 6: Construct Function Arguments

Once you load a module, you are ready to call Python functions defined in the module. Typically we need to pass one or more arguments to a Python function. We have to construct a Python tuple object that contains the arguments for a Python function.

In our example, printData() function defined in our module takes one argument. Thus we construct a Python tuple object with size one as follows. We can set each item in the tuple object using PyTuple_SetItem().

PyObject *pythonArgument;
pythonArgument = PyTuple_New(1);
pValue = PyString_FromString(argv[3]);
 
if(pValue==NULL){
  return 1;
}
PyTuple_SetItem(pythonArgument, 0, pValue);

We have successfully constructed one argument to be passed along the function call, its time we should call the python function from our C application.

Step 7: Call a Python Function

Once a Python tuple object is successfully created as function arguments, we can call a Python function that takes the arguments. For this, first obtain the reference to the function defined in a module by using PyObject_GetAttrString(), and then call the function with PyObject_CallObject(). For example:

// argv[2] is the function name defined in pModule
// pFunc is the reference to the function
pFunc = PyObject_GetAttrString(pModule, argv[2]);
pValue = PyObject_CallObject(pFunc, pythonArgument);

Step 8: Error Checking

A common way to avoid run-time errors is to check the return value of a function and take appropriate action depending on the return value. Similar to errno global variable in C programs, Python/C API maintains a global indicator which reports on the last error that occurred. When a Python/C API function fails, the global indicator is set to indicate the error, and PyErr_Print() can be used to show a corresponding human-readable trackback. For example:

pModule = PyImport_Import(pName);
if (pModule != NULL) {
    // Do something useful here
}
else {
   PyErr_Print();  // print traceback
   fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
   return 1;
}

You can easily incorporate various error checkings inside your applications.

Here is the complete C program that embeds Python code as described in this tutorial.

// depending on distros, the exact path or Python version may vary.
#include </usr/include/python2.7/Python.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue;

    Py_Initialize();

    PySys_SetPath("/usr/local/modules");  // path to the module to import
    pName = PyString_FromString(argv[1]);

    pModule = PyImport_Import(pName);
    if (pModule != NULL) {
        PyObject *pythonArgument;
        pythonArgument = PyTuple_New(1);
        pValue = PyString_FromString(argv[3]);

        if (pValue == NULL) {
            return 1;
        }
        PyTuple_SetItem(pythonArgument, 0, pValue);
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        if (pFunc && PyCallable_Check(pFunc)) {
            pValue = PyObject_CallObject(pFunc, pythonArgument);
            if (pValue != NULL) {
                printf("Value returuend from the function %s", PyString_AsString(pValue));
            } else {
                PyErr_Print();
            }
        } else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        }
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
}

Step 9: Compile and Execute

Save the above code as finalCode.c, and compile the code while linking with the Python library (-lpython2.7). Depending on distros, you may use different versions (e.g., -lpython2.6).

$ gcc -o final finalCode.c -lpython2.7

Now run the compiled executable with three arguments:

./final printData printData cyberpersons

The three arguments in this examle are the module name, the function name in the module, the string argument to be passed to the Python function.

Your output will be something like this:

Hope this helps.

Subscribe to Xmodulo

Do you want to receive Linux FAQs, detailed tutorials and tips published at Xmodulo? Enter your email address below, and we will deliver our Linux posts straight to your email box, for free. Delivery powered by Google Feedburner.


Support Xmodulo

Did you find this tutorial helpful? Then please be generous and support Xmodulo!

The following two tabs change content below.
Usman Nasir, the founder, and author of Cyberpersons is a Computer Science student. He also worked as a technical support staff at various hosting companies and love to write about Linux and web application security.

Latest posts by Muhammad Usman Nasir (see all)

Leave a comment

Your email address will not be published. Required fields are marked *