06/12/05
As I wrote in my previous article, I did not know yet how to handle Python objects, and call their member functions.
After getting some help from Adam "adamh" Hooper, it became clear this is actually very easy to do.
Here's some code (I'm not reposting the whole C file, just append this to the end, it should be clear):
g_debug("Calling pytest::test using the helper function");
PyObject_CallMethod(ret, "test", NULL);
Py_XDECREF(ret);
ret = NULL;
g_debug("Calling pytest::test, no helper function");
g_debug("Creating a new pytest object");
ret = PyInstance_New(PyDict_GetItemString(dict, "" MODULE_NAME), NULL, NULL);
g_assert(ret != NULL);
PyObject_CallMethod(ret, "test", NULL);
Py_XDECREF(ret);
Watch out: you should not Py_XDECREF(ret) before doing this, of course.
As you can see there are 2 ways to achieve our goal:
- In the first part of the snipper, we use "ret". This is the return value of the global function called "test", which is a "pytest" instance (the "test" function ends with
return pytest()
). We just call PyObject_CallMethod(object, name, args) to call the function. - In the first "solution" we use some ugly hack to get a pytest instance. In the second part we use a cleaner method, by calling PyInstance_New(type, constructor_args, kw). As you can see, we get "type" in the same way we got the entry point to the global "test" function. Now again we can just call a method on the object as in the first part.
This should be the output now:
** (process:13267): DEBUG: Initializing Python ** (process:13267): DEBUG: Setting PATH ** (process:13267): DEBUG: Trying to import "pytest" ** (process:13267): DEBUG: Success loading global test function In main test function, argument is "testarg" Initializing a pytest object ** (process:13267): DEBUG: Calling pytest::test using the helper function In pytest's test function ** (process:13267): DEBUG: Calling pytest::test, no helper function ** (process:13267): DEBUG: Creating a new pytest object Initializing a pytest object In pytest's test function
Looks like Python/C interop is not that hard actually :-) You can find the final code here.
06/11/05
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!
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 :-)
06/09/05
- Created an initial page on OPluginManager on live.gnome.org.
- Started writing a longer article on women in the FOSS community, now Gnome-Women was launched (actually, did that yesterday ;-))
- Sent an email to the author of this article in our local newspaper to explain crackers are no hackers (and the other way around). WikiPedia got some decent information on this issue.
- Did not study enough :-(
TODO:
- Continue working on the OPluginManager design, then implement some more of it. Get to learn some more on GObject<->Python/Mono interaction, and Python/Mono<->native calling code (ie how to allow using an OPluginManager object in Python/C#, and how to allow OModuleLoader/OPluginManager to work with Python/C# based plugins).
- Finish the mentioned article
- Poke jdub to get some response (even if it's negative) ;-)
- Write some of my ideas on the Tag based desktop down, decide wether to send it to desktop-devel@gnome or xdg@fdo, and do so accordingly.
- Study!!!!
06/08/05
Alle scholen van het basis- en secundair onderwijs ontvangen volgende week een uitgebreide handleiding over het gebruik van vrije software. Minister van Onderwijs Frank Vandenbroucke wil hen zo laten kennismaken met de toepassingen die mogelijk zijn in klasverband. Daarnaast is een zogenaamde "leermiddelendatabank" ontwikkeld op de educatieve portaalsite Klascement. Eind 2005 krijgen alle scholen ook een cd-rom met vrije software en testresultaten van scholen. Minister Vandenbroucke trekt voor deze acties 40.000 euro uit.
Prachtig nieuws vanuit het kabinet onderwijs dus. Lees hier de volledige persmededeling.