Archives for: June 2005, 11

06/11/05

In the middle

Thursday, my exam Multimedia Networks was OK, I think. With three exams done and three to go, I'm in the middle now. Next one is Information Security wich is really interesting. Seems not that difficult and I allready used lots of those things in practice, so normally it should go well.

Still 20 days to go and counting!

Permalink . Peter . 18:24:48 . 57 Words . Studies . Email . No views
Calling Python from C

As I wrote on the OPluginManager overview page, I'd like to provide functionality to write plugins in Python. To achieve this I need to be able to call Python code from C (as the base OPluginManager framework is C-based). After asking around a little and reading some code samples (mostly in the Epiphany extension loading code) I figured out the basic calls one needs.

Here's what we need to do:
First we need some Python code. Take this as a test (pytest.py):

 class pytest:
        def __init__(self):
                print "Initializing a pytest object"

        def test(self):
                print "In pytest's test function"

def test(a):
        print "In main test function, argument is \"" + a + "\""
        return pytest()

Now we want to call some of this from C (pytest.c):


/* We want Python functionality */
#include <Python.h>
#include <glib.h>

/* To make life easier for us */
#define MODULE_NAME "pytest"

gint main(guint argc, gchar *argv[]) {
        /* The module object */
        PyObject *module = NULL;
        /* Objects we need to get a reference to a function */
        PyObject *dict = NULL, *func = NULL;
        /* Stuff we need to be able to load a module not in PYTHONPATH */
        PyObject *path = NULL, *pwd = NULL;
        /* Args we offer to the called function, and a reference to the return value */
        PyObject *args = NULL, *ret = NULL;

        /* Initialize the Python framework */
        g_debug("Initializing Python");
        Py_Initialize();

        /* "pytest.py" is in ".", so we need to alter the module search path
           "." is not in it by default */
        g_debug("Setting PATH");
        /* Get the current path (this is a list) */
        path = PySys_GetObject("path");
        /* Create a value to add to the list */
        pwd = PyString_FromString(".");
        /* And add it */
        PyList_Insert(path, 0, pwd);
        /* We don't need that string value anymore, so deref it */
        Py_DECREF(pwd);

        /* Load the module */
        g_debug("Trying to import \"%s\"", MODULE_NAME);
        module = PyImport_ImportModule("" MODULE_NAME);
        /* Check whether we succeeded */
        if(module == NULL) {
                /* If not, print the error message and get out of here */
                PyErr_Print();
                PyErr_Clear();
                g_warning("Failed to initialize \"%s\"", MODULE_NAME);
                return 1;
        }

        /* Get a dict from the module
           I should look up the API, but I presume this is something like
           "function_name" => function_entry_point */
        dict = PyModule_GetDict(module);
        /* Get the entry point of our "test" function
           This is -not- the pytest:test function */
        func = PyDict_GetItemString(dict, "test");

        /* Check again whether we succeeded, and whether the function can be called */
        if(func != NULL && PyCallable_Check(func) == TRUE) {
                g_debug("Success loading global test function");
        }
        else {
                /* Something bad occured, print out the Python error and abort */
                g_debug("Failed loading %s", MODULE_NAME);
                if(PyErr_Occurred()) {
                        PyErr_Print();
                        PyErr_Clear();
                }
                return 1;
        }

        /* We want to offer some args to the test(a) function
           These args should go into a tuple */
        /* Create a tuple with one element */
        args = PyTuple_New(1);
        /* Add a new element to the tuple, at position 0, a new string with content "testarg" */
        PyTuple_SetItem(args, 0, PyString_FromString("testarg"));

        /* Call the test function, with the "args" tuple as arguments */
        ret = PyObject_CallObject(func, args);
        /* Something went wrong.
           I must admit I still have to figure out what the return value of CallObject actually is.
           What would ret be if test(a) returns nothing? */
        if(ret == NULL) {
                /* Print error and abort */
                PyErr_Print();
                PyErr_Clear();
                g_warning("Failed to call test function");
                return 1;
        }

        /* Free the returned value, and the args tuple
           We don't really free, we unref the objects.
           I should look up what the difference between XDECREF and DECREF is. DECREF seems to be a standard unref thing */
        Py_XDECREF(ret);
        Py_DECREF(args);

        return 0;

        /* Maybe we should free the module too, and deinitialize Python
           This is not done in the code I read though */
}

Comments inline. This code might look huge, but actually it mostly consists of error checking ;-)

We need a rather "large" line to compile this:

gcc -o pytest -g `pkg-config --cflags --libs glib-2.0` -I/usr/include/python2.3 -lpython2.3 pytest.c

(You should adjust this to match your Python version if necessary)

When we run the program, this is what we get:

** (process:30233): DEBUG: Initializing Python
** (process:30233): DEBUG: Setting PATH
** (process:30233): DEBUG: Trying to import "pytest"
** (process:30233): DEBUG: Success loading global test function
In main test function, argument is "testarg"
Initializing a pytest object

Which is somewhat what we could expect.

In this sample I only use a global method, I don't use any Python object (like "pytest"). I must admit I don't know how to do this yet :oops: but hey, I'm just taking my first steps :-)

Permalink . Ikke . 12:31:29 pm . 756 Words . Technology, Desktop, Coding Corner . . 2800 views . 1 comment