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 :-)
Advertisement:
Comments:
This post has 2 feedbacks awaiting moderation...