Changeset 1753

Show
Ignore:
Timestamp:
02/19/07 09:48:32 (22 months ago)
Author:
abaumann
Message:

first pass at updating the python-module wrapper/plugin for the new API

this includes many changes to the wrapper, and to the structure of the
python modules it wraps

notable besides the API change, is that the wrapper now takes care of
any exceptions in plugin code, reporting them as errors to opensync, and
automatically calls osync_context_report_success if no exception occurred

also removed unused setup.py file, and changed Makefile to not install
the sample plugin by default, as it is only useful for developers

Location:
plugins/python-module
Files:
1 removed
3 modified

Legend:

Unmodified
Added
Removed
  • plugins/python-module/Makefile.am

    r1294 r1753  
    33 
    44INCLUDES = -I$(top_srcdir) 
    5  
    6 EXTRA_DIST = src/sample.py 
    75 
    86OPENSYNC_LIBS=-lopensync 
     
    1614python_module_la_LIBADD = @PYTHON_LIBS@ 
    1715 
    18 pythonplg_DATA = src/sample.py 
     16# uncomment these to install the sample plugin 
     17#EXTRA_DIST = src/sample.py 
     18#pythonplg_DATA = src/sample.py 
  • plugins/python-module/src/python_module.c

    r1232 r1753  
    11/* Python module for OpenSync 
    22 * Copyright (C) 2005  Eduardo Pereira Habkost <ehabkost@conectiva.com.br> 
     3 * Copyright (C) 2007  Andrew Baumann <andrewb@cse.unsw.edu.au> 
    34 * 
    45 * This library is free software; you can redistribute it and/or 
     
    1718 * 
    1819 * @author Eduardo Pereira Habkost <ehabkost@conectiva.com.br> 
     20 * @author Andrew Baumann <andrewb@cse.unsw.edu.au> 
    1921 * 
    2022 * Additional changes by Armin Bauer <armin.bauer@desscon.com> 
     
    2224 
    2325#include <Python.h> 
    24  
    2526#include <opensync/opensync.h> 
     27#include <opensync/opensync-plugin.h> 
     28#include <opensync/opensync-context.h> 
    2629#include <signal.h> 
    2730#include <glib.h> 
    2831#include "config.h" 
     32 
     33/* change this define for python exception output on stderr */ 
     34//#define PYERR_CLEAR() PyErr_Clear() 
     35#define PYERR_CLEAR() PyErr_Print() 
    2936 
    3037typedef struct MemberData { 
     
    3239        PyObject *osync_module; 
    3340        PyObject *module; 
    34         PyObject *object; 
     41        GSList *sinks; 
    3542} MemberData; 
    3643 
     
    4047        if (!osync_module) { 
    4148                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load OpenSync module"); 
    42                 //PyErr_Print(); 
     49                PYERR_CLEAR(); 
    4350                return NULL; 
    4451        } 
    4552        return osync_module; 
    46 } 
    47  
    48 static PyObject *pm_load_script(const char *filename, OSyncError **error) 
    49 { 
    50         FILE *fp = fopen(filename, "r"); 
    51         if (!fp) { 
    52                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to open file %s", filename); 
    53                 return NULL; 
    54         } 
    55          
    56         if (PyRun_SimpleFile(fp, filename) == -1) { 
    57                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't run module from file %s", filename); 
    58                 PyErr_Print(); 
    59                 return NULL; 
    60         } 
    61          
    62         PyObject *module = PyImport_AddModule("__main__"); 
    63         if (!module) { 
    64                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module from file %s", filename); 
    65                 PyErr_Print(); 
    66                 return NULL; 
    67         } 
    68         return module; 
    6953} 
    7054 
     
    7458        if (!pychg_cobject) { 
    7559                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pychg cobject"); 
    76                 PyErr_Print(); 
    77                 return NULL; 
    78         } 
    79          
    80         PyObject *pychg = PyObject_CallMethod(osync_module, "OSyncChange", "O", pychg_cobject); 
     60                PYERR_CLEAR(); 
     61                return NULL; 
     62        } 
     63         
     64        PyObject *pychg = PyObject_CallMethod(osync_module, "Change", "O", pychg_cobject); 
     65        Py_DECREF(pychg_cobject); 
    8166        if (!pychg) { 
    8267                osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncChange"); 
    83                 PyErr_Print(); 
    84                 Py_XDECREF(pychg_cobject); 
     68                PYERR_CLEAR(); 
    8569                return NULL; 
    8670        } 
     
    9377        if (!pyctx_cobject) { 
    9478                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyctx cobject"); 
    95                 PyErr_Print(); 
    96                 return NULL; 
    97         } 
    98          
    99         PyObject *pyctx = PyObject_CallMethod(osync_module, "OSyncContext", "O", pyctx_cobject); 
     79                PYERR_CLEAR(); 
     80                return NULL; 
     81        } 
     82         
     83        PyObject *pyctx = PyObject_CallMethod(osync_module, "Context", "O", pyctx_cobject); 
     84        Py_DECREF(pyctx_cobject); 
    10085        if (!pyctx) { 
    10186                osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncContext"); 
    102                 PyErr_Print(); 
    103                 Py_XDECREF(pyctx_cobject); 
     87                PYERR_CLEAR(); 
    10488                return NULL; 
    10589        } 
     
    10791} 
    10892 
    109 static PyObject *pm_make_member(PyObject *osync_module, OSyncMember *member, OSyncError **error) 
    110 { 
    111         PyObject *pymember_cobject = PyCObject_FromVoidPtr(member, NULL); 
    112         if (!pymember_cobject) { 
    113                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pymember cobject"); 
    114                 PyErr_Print(); 
    115                 return NULL; 
    116         } 
    117          
    118         PyObject *pymember = PyObject_CallMethod(osync_module, "OSyncMember", "O", pymember_cobject); 
    119         if (!pymember) { 
    120                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncMember"); 
    121                 PyErr_Print(); 
    122                 Py_XDECREF(pymember_cobject); 
    123                 return NULL; 
    124         } 
    125         return pymember; 
    126 } 
     93static PyObject *pm_make_info(PyObject *osync_module, OSyncPluginInfo *info, OSyncError **error) 
     94{ 
     95        PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL); 
     96        if (!pyinfo_cobject) { 
     97                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject"); 
     98                PYERR_CLEAR(); 
     99                return NULL; 
     100        } 
     101         
     102        PyObject *pyinfo = PyObject_CallMethod(osync_module, "PluginInfo", "O", pyinfo_cobject); 
     103        Py_DECREF(pyinfo_cobject); 
     104        if (!pyinfo) { 
     105                osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPluginInfo"); 
     106                PYERR_CLEAR(); 
     107                return NULL; 
     108        } 
     109        return pyinfo; 
     110} 
     111 
     112/* convert a python exception to an OSyncError containing the traceback of the exception */ 
     113static void pm_pyexcept_to_oserror(PyObject *pytype, PyObject *pyvalue, PyObject *pytraceback, OSyncError **error) 
     114{ 
     115        const char *errmsg = NULL; 
     116        PyObject *tracebackmod = NULL, *stringmod = NULL; 
     117        PyObject *pystrs = NULL, *pystr = NULL; 
     118         
     119        tracebackmod = PyImport_ImportModule("traceback"); 
     120        if (!tracebackmod) { 
     121                errmsg = "import traceback"; 
     122                goto error; 
     123        } 
     124 
     125        pystrs = PyObject_CallMethod(tracebackmod, "format_exception", "OOO", pytype, pyvalue, pytraceback); 
     126        if (!pystrs) { 
     127                errmsg = "traceback.format_exception"; 
     128                goto error; 
     129        } 
     130 
     131        stringmod = PyImport_ImportModule("string"); 
     132        if (!stringmod) { 
     133                errmsg = "import string"; 
     134                goto error; 
     135        } 
     136 
     137        pystr = PyObject_CallMethod(stringmod, "join", "Os", pystrs, ""); 
     138        if (!pystr) { 
     139                errmsg = "string.join"; 
     140                goto error; 
     141        } 
     142 
     143        osync_error_set(error, OSYNC_ERROR_GENERIC, "%s", PyString_AsString(pystr)); 
     144 
     145error: 
     146        Py_XDECREF(tracebackmod); 
     147        Py_XDECREF(stringmod); 
     148        Py_XDECREF(pystrs); 
     149        Py_XDECREF(pystr); 
     150 
     151        if (errmsg) { 
     152                PYERR_CLEAR(); 
     153                osync_error_set(error, OSYNC_ERROR_GENERIC, "pm_pyexcept_to_oserror: failed to report error: exception in %s", errmsg); 
     154        } 
     155} 
     156 
     157/** Call a python method, report any exception it raises as an error, if no exception was raised report success 
     158 * 
     159 * Methods called using this function can 
     160 * have one of these formats: 
     161 * 
     162 * - function(info, context) 
     163 * - function(info, context, change) 
     164 */ 
     165static osync_bool pm_call_module_method(MemberData *data, char *name, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *chg) 
     166{ 
     167        osync_trace(TRACE_ENTRY, "%s(%s, %p, %p, %p)", __func__, name, info, ctx, chg); 
     168        PyObject *ret = NULL; 
     169        OSyncError *error = NULL; 
     170        osync_bool report_error = TRUE; 
     171 
     172        PyEval_AcquireThread(data->interp_thread); 
     173 
     174        PyObject *pyinfo = pm_make_info(data->osync_module, info, &error); 
     175        if (!pyinfo) 
     176                goto error; 
     177 
     178        PyObject *pycontext = pm_make_context(data->osync_module, ctx, &error); 
     179        if (!pycontext) { 
     180                Py_DECREF(pyinfo); 
     181                goto error; 
     182        } 
     183 
     184        OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info); 
     185        PyObject *sink_pyobject = osync_objtype_sink_get_userdata(sink); 
     186 
     187        if (chg) { 
     188                PyObject *pychange = pm_make_change(data->osync_module, chg, &error); 
     189                if (!pychange) { 
     190                        Py_DECREF(pyinfo); 
     191                        Py_DECREF(pycontext); 
     192                        goto error; 
     193                } 
     194                 
     195                ret = PyObject_CallMethod(sink_pyobject, name, "OOO", pyinfo, pycontext, pychange); 
     196                 
     197                Py_DECREF(pychange); 
     198        } else { 
     199                ret = PyObject_CallMethod(sink_pyobject, name, "OO", pyinfo, pycontext); 
     200        } 
     201 
     202        Py_DECREF(pyinfo); 
     203 
     204        if (ret) { 
     205                Py_DECREF(pycontext); 
     206                Py_DECREF(ret); 
     207                PyEval_ReleaseThread(data->interp_thread); 
     208                osync_context_report_success(ctx); 
     209                osync_trace(TRACE_EXIT, "%s", __func__); 
     210                return TRUE; 
     211        } 
     212 
     213        /* an exception occurred. get the python exception data */ 
     214        PyObject *pytype, *pyvalue, *pytraceback; 
     215        PyErr_Fetch(&pytype, &pyvalue, &pytraceback); 
     216         
     217        PyObject *osyncerror = NULL; 
     218        osyncerror = PyObject_GetAttrString(data->osync_module, "Error"); 
     219        if (!osyncerror) { 
     220                PYERR_CLEAR(); 
     221                osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed to get OSyncError class object"); 
     222                goto out; 
     223        } 
     224         
     225        if (PyErr_GivenExceptionMatches(pytype, osyncerror)) { 
     226                /* if it's an OSyncError, just report that up on the context object */ 
     227                PyObject *obj = PyObject_CallMethod(pyvalue, "report", "OO", pyvalue, pycontext); 
     228                if (!obj) { 
     229                        PYERR_CLEAR(); 
     230                        osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting OSyncError"); 
     231                        goto out; 
     232                } 
     233                 
     234                Py_DECREF(obj); 
     235                osync_error_set(&error, OSYNC_ERROR_GENERIC, "Reported OSyncError"); 
     236                report_error = FALSE; 
     237        } else if (PyErr_GivenExceptionMatches(pytype, PyExc_IOError) 
     238                   || PyErr_GivenExceptionMatches(pytype, PyExc_OSError)) { 
     239                /* for IOError or OSError, we just report the &error message */ 
     240                PyObject *pystr = PyObject_Str(pyvalue); 
     241                if (!pystr) { 
     242                        PYERR_CLEAR(); 
     243                        osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting IOError/OSError"); 
     244                        goto out; 
     245                } 
     246 
     247                osync_error_set(&error, OSYNC_ERROR_IO_ERROR, "%s", PyString_AsString(pystr)); 
     248                Py_DECREF(pystr); 
     249        } else { 
     250                /* for other exceptions, we report a full traceback */ 
     251                pm_pyexcept_to_oserror(pytype, pyvalue, pytraceback, &error); 
     252        } 
     253 
     254out: 
     255        Py_DECREF(pycontext); 
     256        Py_XDECREF(pytype); 
     257        Py_XDECREF(pyvalue); 
     258        Py_XDECREF(pytraceback); 
     259        Py_XDECREF(osyncerror); 
     260 
     261error: 
     262        PyEval_ReleaseThread(data->interp_thread); 
     263        if (report_error) 
     264                osync_context_report_osyncerror(ctx, error); 
     265        osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error)); 
     266        return FALSE; 
     267} 
     268 
     269static void pm_connect(void *data, OSyncPluginInfo *info, OSyncContext *ctx) 
     270{ 
     271        pm_call_module_method(data, "connect", info, ctx, NULL); 
     272} 
     273 
     274static void pm_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx) 
     275{ 
     276        pm_call_module_method(data, "disconnect", info, ctx, NULL); 
     277} 
     278 
     279static void pm_get_changes(void *data, OSyncPluginInfo *info, OSyncContext *ctx) 
     280{ 
     281        pm_call_module_method(data, "get_changes", info, ctx, NULL); 
     282} 
     283 
     284static void pm_commit(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change) 
     285{        
     286        pm_call_module_method(data, "commit", info, ctx, change); 
     287} 
     288 
     289static void pm_committed_all(void *data, OSyncPluginInfo *info, OSyncContext *ctx) 
     290{        
     291        pm_call_module_method(data, "committed_all", info, ctx, NULL); 
     292} 
     293 
     294static osync_bool pm_write(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change) 
     295{        
     296        return pm_call_module_method(data, "write", info, ctx, change); 
     297} 
     298 
     299static osync_bool pm_read(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change) 
     300{        
     301        return pm_call_module_method(data, "read", info, ctx, change); 
     302} 
     303 
     304static void pm_sync_done(void *data, OSyncPluginInfo *info, OSyncContext *ctx) 
     305{ 
     306        pm_call_module_method(data, "sync_done", info, ctx, NULL); 
     307} 
     308 
     309static OSyncObjTypeSinkFunctions pm_sink_functions = { 
     310        .connect = pm_connect, 
     311        .disconnect = pm_disconnect, 
     312        .get_changes = pm_get_changes, 
     313        .commit = pm_commit, 
     314        .write = pm_write, 
     315        .committed_all = pm_committed_all, 
     316        .read = pm_read, 
     317        .batch_commit = NULL, /* not (yet) supported for python plugins */ 
     318        .sync_done = pm_sync_done 
     319}; 
    127320 
    128321/** Calls the method initialize function 
    129322 * 
    130  * The python initialize() function should return an object that 
    131  * has the other plugin methods (get_changeinfo, commit, etc.) 
     323 * The python initialize() function register one or more sink objects 
     324 * that have the other plugin methods (get_changeinfo, commit, etc.) 
    132325 */ 
    133 static void *pm_initialize(OSyncMember *member, OSyncError **error) 
    134 { 
    135         const char *name = osync_member_get_plugindata(member); 
    136         if (!name) { 
    137                 osync_error_set(error, OSYNC_ERROR_GENERIC, "No script name was set"); 
    138                 return NULL; 
    139         } 
    140  
    141         MemberData *data = g_malloc(sizeof(MemberData)); 
     326static void *pm_initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error) 
     327{ 
     328        MemberData *data = g_malloc0(sizeof(MemberData)); 
     329        char *modulename; 
     330 
     331        if (!(modulename = osync_plugin_get_data(plugin))) 
     332                return NULL; 
     333        osync_plugin_set_data(plugin, NULL); 
    142334 
    143335        data->interp_thread = Py_NewInterpreter(); 
    144336        if (!data->interp_thread) { 
     337                PYERR_CLEAR(); 
    145338                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize python sub interpreter"); 
    146                 goto error_free_data; 
    147         } 
    148          
     339                free(modulename); 
     340                goto error; 
     341        } 
     342 
     343        if (!(data->module = PyImport_ImportModule(modulename))) { 
     344                PYERR_CLEAR(); 
     345                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module %s", modulename); 
     346                free(modulename); 
     347                goto error; 
     348        } 
     349 
     350        free(modulename); 
     351 
    149352        if (!(data->osync_module = pm_load_opensync(error))) 
    150                 goto error_free_interp; 
    151          
    152         if (!(data->module = pm_load_script(name, error))) 
    153                 goto error_free_interp; 
    154          
    155         PyObject *pymember = pm_make_member(data->osync_module, member, error); 
    156         if (!pymember) 
    157                 goto error_unload_module; 
    158          
    159         data->object = PyObject_CallMethod(data->module, "initialize", "O", pymember); 
    160         if (!data->object) { 
     353                goto error; 
     354         
     355        PyObject *pyinfo = pm_make_info(data->osync_module, info, error); 
     356        if (!pyinfo) 
     357                goto error; 
     358         
     359        PyObject *ret = PyObject_CallMethod(data->module, "initialize", "O", pyinfo); 
     360        Py_DECREF(pyinfo); 
     361        if (!ret) { 
    161362                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize module"); 
    162                 PyErr_Print(); 
    163                 goto error_unload_module; 
     363                PYERR_CLEAR(); 
     364                goto error; 
     365        } 
     366        Py_DECREF(ret); 
     367 
     368        /* loop through all objtype sinks, set up function pointers */ 
     369        int n, max = osync_plugin_info_num_objtypes(info); 
     370        for (n = 0; n < max; n++) { 
     371                OSyncObjTypeSink *sink = osync_plugin_info_nth_objtype(info, n); 
     372                PyObject *sinkobj = osync_objtype_sink_get_userdata(sink); 
     373                osync_objtype_sink_set_functions(sink, pm_sink_functions, sinkobj); 
     374                Py_INCREF(sinkobj); 
     375                data->sinks = g_slist_prepend(data->sinks, sinkobj); 
    164376        } 
    165377 
     
    168380        return data; 
    169381 
    170 error_unload_module: 
    171         Py_DECREF(data->module); 
    172 error_free_interp: 
    173         Py_EndInterpreter(data->interp_thread); 
    174 error_free_data: 
     382error: 
     383        Py_XDECREF(data->module); 
     384        Py_XDECREF(data->osync_module); 
     385        if (data->interp_thread) 
     386                Py_EndInterpreter(data->interp_thread); 
    175387        free(data); 
    176388        return NULL; 
     389} 
     390 
     391static osync_bool pm_discover(void *data_in, OSyncPluginInfo *info, OSyncError **error) 
     392{ 
     393        MemberData *data = data_in; 
     394 
     395        PyEval_AcquireThread(data->interp_thread); 
     396 
     397        PyObject *pyinfo = pm_make_info(data->osync_module, info, error); 
     398        if (!pyinfo) 
     399                goto error; 
     400 
     401        PyObject *ret = PyObject_CallMethod(data->module, "discover", "O", pyinfo); 
     402        Py_DECREF(pyinfo); 
     403        if (!ret) 
     404                goto error; 
     405 
     406        Py_DECREF(ret); 
     407        PyEval_ReleaseThread(data->interp_thread); 
     408        return TRUE; 
     409 
     410error: 
     411        osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't call discover method"); 
     412        PYERR_CLEAR(); 
     413        PyEval_ReleaseThread(data->interp_thread); 
     414        return FALSE; 
    177415} 
    178416 
     
    182420        MemberData *mydata = data; 
    183421        PyEval_AcquireThread(mydata->interp_thread); 
    184         { 
    185                 PyObject *ret = PyObject_CallMethod(mydata->object, "finalize", NULL); 
    186                 if (!ret) { 
    187                         osync_trace(TRACE_INTERNAL, "Error during finalize()"); 
    188                         PyErr_Print(); 
    189                 } else 
    190                         Py_DECREF(ret); 
    191         } 
    192         Py_DECREF(mydata->object); 
     422 
     423        /* free all sink objects */ 
     424        while (mydata->sinks) { 
     425                Py_DECREF((PyObject *)mydata->sinks->data); 
     426                mydata->sinks = g_slist_delete_link(mydata->sinks, mydata->sinks); 
     427        } 
     428 
    193429        Py_DECREF(mydata->module); 
     430        Py_DECREF(mydata->osync_module); 
    194431        Py_EndInterpreter(mydata->interp_thread); 
    195          
    196432        free(mydata); 
    197         osync_trace(TRACE_EXIT, "%s", __func__); 
    198 } 
    199  
    200 /** Call a python method 
    201  * 
    202  * Methods called using this function can 
    203  * have one of these formats: 
    204  * 
    205  * - function(context) 
    206  * - function(context, change) 
    207  */ 
    208 static osync_bool pm_call_module_method(OSyncContext *ctx, OSyncChange *chg, char *name, OSyncError **error) 
    209 { 
    210         osync_trace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, ctx, chg, name, error); 
    211         PyObject *pycontext = NULL; 
    212         PyObject *ret = NULL; 
    213  
    214         MemberData *data = osync_context_get_plugin_data(ctx); 
    215         PyEval_AcquireThread(data->interp_thread); 
    216  
    217         pycontext = pm_make_context(data->osync_module, ctx, error); 
    218         if (!pycontext) { 
    219                 PyEval_ReleaseThread(data->interp_thread); 
    220                 osync_context_report_osyncerror(ctx, error); 
    221                 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    222                 return FALSE; 
    223         } 
    224  
    225         if (chg) { 
    226                 PyObject *pychange = pm_make_change(data->osync_module, chg, error); 
    227                 if (!pychange) { 
    228                         PyEval_ReleaseThread(data->interp_thread); 
    229                         osync_context_report_osyncerror(ctx, error); 
    230                         osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    231                         return FALSE; 
    232                 } 
    233                  
    234                 ret = PyObject_CallMethod(data->object, name, "OO", pycontext, pychange); 
    235                  
    236                 Py_XDECREF(pychange); 
    237         } else { 
    238                 ret = PyObject_CallMethod(data->object, name, "O", pycontext); 
    239         } 
    240          
    241         if (!ret) { 
    242                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Error during %s() method", name); 
    243                 PyErr_Print(); 
    244                 PyEval_ReleaseThread(data->interp_thread); 
    245                 osync_context_report_osyncerror(ctx, error); 
    246                 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    247                 return FALSE; 
    248         } 
    249  
    250         Py_XDECREF(ret); 
    251         PyEval_ReleaseThread(data->interp_thread); 
    252          
    253         osync_trace(TRACE_EXIT, "%s", __func__); 
    254         return TRUE; 
    255 } 
    256  
    257 static void pm_connect(OSyncContext *ctx) 
    258 { 
    259         osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); 
    260         OSyncError *error = NULL; 
    261         pm_call_module_method(ctx, NULL, "connect", &error); 
    262         osync_trace(TRACE_EXIT, "%s", __func__); 
    263 } 
    264  
    265  
    266 static void pm_get_changeinfo(OSyncContext *ctx) 
    267 { 
    268         osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); 
    269         OSyncError *error = NULL; 
    270         pm_call_module_method(ctx, NULL, "get_changeinfo", &error); 
    271         osync_trace(TRACE_EXIT, "%s", __func__); 
    272 } 
    273  
    274 static osync_bool pm_access(OSyncContext *ctx, OSyncChange *change) 
    275 {        
    276         osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change); 
    277         OSyncError *error = NULL; 
    278         pm_call_module_method(ctx, change, "access", &error); 
    279         osync_trace(TRACE_EXIT, "%s", __func__); 
    280         return TRUE; 
    281 } 
    282  
    283 static osync_bool pm_commit_change(OSyncContext *ctx, OSyncChange *change) 
    284 {        
    285         osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change); 
    286         OSyncError *error = NULL; 
    287         pm_call_module_method(ctx, change, "commit_change", &error); 
    288         osync_trace(TRACE_EXIT, "%s", __func__); 
    289         return TRUE; 
    290 } 
    291  
    292 static void pm_sync_done(OSyncContext *ctx) 
    293 { 
    294         osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); 
    295         OSyncError *error = NULL; 
    296         pm_call_module_method(ctx, NULL, "sync_done", &error); 
    297         osync_trace(TRACE_EXIT, "%s", __func__); 
    298 } 
    299  
    300 static void pm_disconnect(OSyncContext *ctx) 
    301 { 
    302         osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx); 
    303         OSyncError *error = NULL; 
    304         pm_call_module_method(ctx, NULL, "disconnect", &error); 
    305433        osync_trace(TRACE_EXIT, "%s", __func__); 
    306434} 
     
    313441 *       accepted objtypes/formats info) 
    314442 */ 
    315 static osync_bool register_plugin(OSyncEnv *env, PyObject *osync_module, 
    316                                   const char *filename, OSyncError **error) 
    317 { 
    318         osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, filename, error); 
    319  
    320         PyObject *module = pm_load_script(filename, error); 
     443static osync_bool register_plugin(OSyncPluginEnv *env, PyObject *osync_module, 
     444                                  char *modulename, OSyncError **error) 
     445{ 
     446        osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, modulename, error); 
     447 
     448        PyObject *module = NULL, *pyplugin_cobj = NULL, *pyplugin = NULL; 
     449 
     450        OSyncPlugin *plugin = osync_plugin_new(error); 
     451        if (!plugin) 
     452                return FALSE; 
     453 
     454        module = PyImport_ImportModule(modulename); 
    321455        if (!module) { 
    322                 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    323                 return FALSE; 
    324         } 
    325          
    326         OSyncPluginInfo *info = osync_plugin_new_info(env); 
    327         info->functions.initialize = pm_initialize; 
    328         info->functions.connect = pm_connect; 
    329         info->functions.get_changeinfo = pm_get_changeinfo; 
    330         info->functions.sync_done = pm_sync_done; 
    331         info->functions.disconnect = pm_disconnect; 
    332         info->functions.finalize = pm_finalize; 
    333          
    334         info->plugin_data = g_strdup(filename); 
    335          
    336         PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL); 
    337         if (!pyinfo_cobject) { 
    338                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject"); 
    339                 PyErr_Print(); 
    340                 P