Changeset 487

Show
Ignore:
Timestamp:
04/11/05 17:41:43 (4 years ago)
Author:
ehabkost
Message:

Getting the changes from the python-plugins branch

Location:
plugins/python-module
Files:
6 modified
3 copied

Legend:

Unmodified
Added
Removed
  • plugins/python-module

    • Property svn:ignore
      •  

        old new  
        1313missing 
        1414install-sh 
         15config.log 
         16config.h 
         17.libs 
         18.deps 
         19Makefile 
         20config.status 
         21stamp-h1 
         22libtool 
  • plugins/python-module/Makefile.am

    r108 r487  
    11plugindir=@OPENSYNC_PLUGINDIR@ 
    22pythonplgdir=@OPENSYNC_PYTHONPLG_DIR@ 
    3 pythonlibdir=@OPENSYNC_PYTHONLIB_DIR@ 
    43 
    54OPENSYNC_LIBS=-lopensync 
     
    76#FIXME: set EXTRA_DIST 
    87 
     8EXTRA_DIST = \ 
     9        src/pywrap.c \ 
     10        src/opensync.pyx 
    911 
    1012plugin_LTLIBRARIES = python_module.la 
    1113 
    1214python_module_la_SOURCES = src/python_module.c 
    13 python_module_la_LDFLAGS = @PYTHON_LDFLAGS@ -avoid-version -export-dynamic -module 
    14 python_module_la_CFLAGS = -Wall -Werror 
     15python_module_la_LDFLAGS = @PYTHON_LDFLAGS@ -avoid-version -export-dynamic -module $(OSYNC_LIBS) 
     16python_module_la_CFLAGS = -Wall -Werror $(OSYNC_CFLAGS) 
    1517python_module_la_CPPFLAGS = @PYTHON_CFLAGS@ \ 
    16         -DOPENSYNC_PYTHONPLG_DIR=\"$(pythonplgdir)\" \ 
    17         -DOPENSYNC_PYTHONLIB_DIR=\"$(pythonlibdir)\" 
     18        -DOPENSYNC_PYTHONPLG_DIR=\"$(pythonplgdir)\" 
    1819python_module_la_LIBADD = @PYTHON_LIBS@ 
    1920 
    2021 
    21 # yes, _PROGRAMS. I am trying to avoid using libtool here 
    22 pythonlib_PROGRAMS = opensync.so 
    23  
    24 #FIXME: the lines below don't work. check why (maybe use distutils?) 
    25 # only -Werror. pyrex generated code doesn't support -Wall 
    26 opensync_so_CFLAGS = -Werror -fPIC 
    27 opensync_so_CPPFLAGS = @PYTHON_CFLAGS@ -Werror 
    28 nodist_opensync_so_SOURCES = src/opensync.c 
    29 opensync_so_LDFLAGS = @PYTHON_LDFLAGS@ -shared 
    30 opensync_so_LDADD = $(OPENSYNC_LIBS) 
    3122 
    3223src/opensync.c: src/opensync.pyx 
    3324        pyrexc $< 
     25 
     26# opensync python module building rules 
     27# It uses setup.py and distutils to 
     28# make things easier 
     29opensync.so: src/opensync.c setup.py 
     30        CFLAGS="$$CFLAGS $(OSYNC_CFLAGS)" python setup.py build_ext 
     31 
     32all-local: opensync.so 
     33 
     34install-exec-local: 
     35        python setup.py install 
     36 
     37clean-local: 
     38        python setup.py clean 
  • plugins/python-module/configure.in

    r404 r487  
    6767                os_cv_python_sdk=yes])])]) 
    6868 
    69 OPENSYNC_PLUGINDIR=${libdir}/opensync/plugins 
     69PKG_CHECK_MODULES(OSYNC, [opensync-1.0]) 
     70 
     71OPENSYNC_CONFIGDIR=$(pkg-config --variable=configdir opensync-1.0) 
     72OPENSYNC_PLUGINDIR=$(pkg-config --variable=plugindir opensync-1.0) 
     73OPENSYNC_FORMATSDIR=$(pkg-config --variable=formatsdir opensync-1.0) 
     74OPENSYNC_HEADERDIR=$(pkg-config --variable=headerdir opensync-1.0) 
     75 
     76AC_SUBST(OPENSYNC_CONFIGDIR) 
    7077AC_SUBST(OPENSYNC_PLUGINDIR) 
     78AC_SUBST(OPENSYNC_FORMATSDIR) 
     79AC_SUBST(OPENSYNC_HEADERDIR) 
    7180 
     81#FIXME: Set the python plugin path using a better method, instead of just 
     82# using libdir 
    7283OPENSYNC_PYTHONPLG_DIR=${libdir}/opensync/python-plugins 
    7384AC_SUBST(OPENSYNC_PYTHONPLG_DIR) 
    7485 
    75 #FIXME: install opensync module on python library path, not on opensync directory 
    76 OPENSYNC_PYTHONLIB_DIR=${libdir}/opensync/python 
    77 AC_SUBST(OPENSYNC_PYTHONLIB_DIR) 
    78  
    7986AC_OUTPUT([Makefile]) 
  • plugins/python-module/src/opensync.pyx

    r108 r487  
    2929        OSyncObjFormat *osync_change_get_objformat(OSyncChange *change) 
    3030        void osync_change_set_member(OSyncChange *change, OSyncMember *member) 
     31        void osync_change_ref(OSyncChange *change) 
     32        void osync_change_decref(OSyncChange *change) 
    3133         
    3234        #objformat methods 
     
    3941 
    4042cdef extern from "pywrap.h": 
     43        ctypedef struct PythonPluginInfo 
     44 
     45        # pyrex doesn't handle typecasts gracefully, 
     46        # so the functions below are just macros that 
     47        # do typecasts 
    4148        OSyncMember *osync_member_from_void(void *m) 
    4249        OSyncContext *osync_context_from_void(void *c) 
     50        OSyncChange *osync_change_from_void(void *c) 
     51        PythonPluginInfo *osync_plginfo_from_void(void *v) 
     52 
     53 
     54        void pywrap_accept_objtype(PythonPluginInfo *info, char *objtype) 
     55        void pywrap_accept_objformat(PythonPluginInfo *info, char *objtype, char *objformat, char *extension) 
     56        void pywrap_set_name_and_version(PythonPluginInfo *info, char *name, int version) 
    4357 
    4458cdef class Member: 
     
    5973                self.memb = osync_member_from_void(v) 
    6074 
     75# forward declaration 
    6176cdef class Change 
    6277 
     
    6479        """opensync OSyncContext object""" 
    6580        cdef OSyncContext *ctx 
    66         cdef void *v 
    6781        def __new__(self, c): 
    6882                """Never call this method from python code. 
     
    7084                Objects of this class are created only internally by OpenSync 
    7185                """ 
    72                 # Another hack like the OSyncMember hack above, 
    73                 # to be able to convert a python object 
    74                 # to OSyncContext * 
    7586                cdef void *v 
    7687                v = PyCObject_AsVoidPtr(c) 
     
    7889 
    7990        def report_change(self, Change chg): 
     91                # call _ref() to avoid the change 
     92                # from being free()d after destroying 
     93                # the python opensync.Change object 
     94                osync_change_ref(chg.chg) 
     95                #FIXME: Are we supposed to call ref() here, 
     96                # or osync_context_report_change() implies 
     97                # on a _ref() call? 
     98 
    8099                osync_context_report_change(self.ctx, chg.chg) 
    81100 
     
    90109        cdef OSyncChange *chg 
    91110 
    92         def __new__(self, Member member): 
    93                 self.chg = osync_change_new() 
    94                 osync_change_set_member(self.chg, member.memb) 
     111        def __new__(self, Member member, chg = None): 
     112                """Creates a new change object 
     113 
     114                The chg parameter can be used only internally 
     115                by OpenSync. It is a CObject containing 
     116                an existing OSyncChange""" 
     117                cdef void *v 
     118                if chg is None: 
     119                        self.chg = osync_change_new() 
     120                        osync_change_set_member(self.chg, member.memb) 
     121                else: 
     122                        v = PyCObject_AsVoidPtr(chg) 
     123                        self.chg = osync_change_from_void(v) 
     124                        osync_change_ref(self.chg) 
    95125 
    96126        def __dealloc__(self): 
    97                 osync_change_free(self.chg) 
     127                osync_change_decref(self.chg) 
    98128 
    99129        def set_objformat(self, name): 
     
    106136                else: return None 
    107137 
     138cdef class PluginInfo: 
     139        """opensync OSyncPluginInfo object""" 
     140 
     141        # python_module.c should be able to tell, 
     142        # somehow, which functions should be registered, 
     143        # pyinfo is used for that 
     144        cdef PythonPluginInfo *info 
     145 
     146        def __new__(self, info): 
     147                """Never call this method from python code. 
     148                 
     149                Objects of this class are created only internally by OpenSync 
     150                """ 
     151                cdef void *v 
     152                v = PyCObject_AsVoidPtr(info) 
     153                self.info = osync_plginfo_from_void(v) 
     154 
     155        def accept_objtype(self, objtype): 
     156                pywrap_accept_objtype(self.info, objtype) 
     157         
     158        def accept_objformat(self, objtype, objformat, extension = None): 
     159                cdef char *ext 
     160                if extension is None: 
     161                        ext = NULL 
     162                else: 
     163                        ext = extension 
     164                pywrap_accept_objformat(self.info, objtype, objformat, ext) 
     165 
     166        def set_name(self, name, version): 
     167                pywrap_set_name_and_version(self.info, name, version); 
    108168 
    109169# vim:ft=python 
  • plugins/python-module/src/python_module.c

    r167 r487  
    1111#include <signal.h> 
    1212 
     13#include "pywrap.h" 
    1314 
    1415struct MemberData { 
     
    1920}; 
    2021 
    21 /*FIXME: Provide the opensync plugin API to python modules. 
    22  * Probably only a OSyncContext class implementation will suffice 
    23  * 
    24  * (report_change, report_error, report_success, etc.) 
    25  * 
    26  * Then, add parameters to the initialize, connect, etc. functions 
    27  * of the python module 
    28  */ 
    29  
    30 /** Insert plugin search path to sys.path 
     22/** Insert plugin search path in sys.path 
    3123 * 
    3224 * Isn't there an easier way of setting it? 
     25 * 
     26 *FIXME: It would be better if we just load modules from 
     27 * their filename, not setting the module search path 
    3328 */ 
    3429static int change_sys_path() 
    3530{ 
    3631        int rv = -1; 
    37         PyObject *sys, *path, *dir; 
     32        PyObject *sys = NULL; 
     33        PyObject *path = NULL; 
     34        PyObject *dir = NULL; 
    3835 
    3936        sys = PyImport_ImportModule("sys"); 
    40         if (!sys) goto error_sys; 
     37        if (!sys) goto error; 
    4138 
    4239        path = PyObject_GetAttrString(sys, "path"); 
    43         if (!path) goto error_path; 
     40        if (!path) goto error; 
    4441 
    4542        dir = PyString_FromString(OPENSYNC_PYTHONPLG_DIR); 
    46         if (!dir) goto error_dir; 
     43        if (!dir) goto error; 
    4744 
    4845        if (PyList_Insert(path, 0, dir) < 0) 
    49                 goto error_insert; 
    50  
    51         /*FIXME: temporary hack, while the opensync module is not 
    52          * installed on the python module path 
    53          */ 
    54         Py_DECREF(dir); 
    55         dir = PyString_FromString(OPENSYNC_PYTHONLIB_DIR); 
    56         if (!dir) goto error_dir; 
    57  
    58         if (PyList_Insert(path, 0, dir) < 0) 
    59                 goto error_insert; 
     46                goto error; 
    6047 
    6148        /* Success */ 
    6249        rv = 0; 
    6350 
    64 error_insert: 
    65         Py_DECREF(dir); 
    66 error_dir: 
    67         Py_DECREF(path); 
    68 error_path: 
    69         Py_DECREF(sys); 
    70 error_sys: 
     51error: 
     52        Py_XDECREF(dir); 
     53        Py_XDECREF(path); 
     54        Py_XDECREF(sys); 
    7155        return rv; 
    7256} 
     
    7458/** Calls the method initialize function 
    7559 * 
    76  * The initialize() function should return an object that 
     60 * The python initialize() function should return an object that 
    7761 * has the other plugin methods (get_changeinfo, commit, etc.) 
    7862 */ 
    79 static void *py_initialize(OSyncMember *member) 
    80 { 
     63static void *py_initialize(OSyncMember *member, OSyncError **error) 
     64{ 
     65        char *name; 
    8166        struct MemberData *data = malloc(sizeof(struct MemberData)); 
    8267        if (!data) 
     68                /*FIXME: set error info */ 
    8369                return NULL; 
    8470 
    85         /*FIXME: the code loading should be on the get_info() function, 
    86          * if we support registering many plugins by a single 
    87          * loadable module. 
    88          * 
    89          * Suggestion: on get_info(), use the main interpreter, to avoid 
    90          * creating interpreter objects only to get plugin information. 
    91          * Then on initialize(), load the plugin using another interpreter 
    92          * (or thread state?) to keep the members on separated interpreters (or 
    93          * threads) 
    94          */ 
     71        /* The plugin name was set on register_plugin() on plugin_data */ 
     72        name = osync_plugin_get_plugin_data(osync_member_get_plugin(member)); 
     73        if (!name) 
     74                /*FIXME: set error info */ 
     75                return NULL; 
     76 
    9577        data->interp_thread = Py_NewInterpreter(); 
    9678        if (0 && !data->interp_thread) { 
     
    10082        if (change_sys_path() < 0) { 
    10183                osync_debug("python", 1, "Exception when initializing python intepreter"); 
     84                PyErr_Print(); 
    10285                PyErr_Clear(); 
    10386                goto error_free_interp; 
     
    10689        if (!data->module) { 
    10790                osync_debug("python", 1, "Couldn't load OpenSync module"); 
     91                PyErr_Print(); 
    10892                PyErr_Clear(); 
    10993                goto error_free_interp; 
    11094        } 
    111         data->module = PyImport_ImportModule("testmodule"); 
     95        data->module = PyImport_ImportModule(name); 
    11296        if (!data->module) { 
    11397                osync_debug("python", 1, "Couldn't load testmodule"); 
     98                PyErr_Print(); 
    11499                PyErr_Clear(); 
    115100                goto error_unload_osync_mod; 
     
    120105        if (!data->object) { 
    121106                osync_debug("python", 1, "Error during initialize()"); 
     107                PyErr_Print(); 
    122108                PyErr_Clear(); 
    123109                goto error_unload_module; 
     
    141127 
    142128static void py_finalize(void *data) 
    143 { /*FIXME: Implement me */ 
     129{ 
    144130        struct MemberData *mydata = data; 
    145131        PyEval_AcquireThread(mydata->interp_thread); 
     
    148134                if (!ret) { 
    149135                        osync_debug("python", 1, "Error during finalize()"); 
     136                        PyErr_Print(); 
    150137                        PyErr_Clear(); 
    151138                } else 
     
    154141        Py_DECREF(mydata->object); 
    155142        Py_DECREF(mydata->module); 
     143        Py_DECREF(mydata->osync_module); 
     144        free(mydata); 
     145 
    156146        Py_EndInterpreter(mydata->interp_thread); 
    157147} 
    158148 
    159 static void call_module_method(OSyncContext *ctx, char *name) 
    160 { 
     149/** Create a new opensync.Change object for a given change */ 
     150static PyObject *new_pychange(struct MemberData *mydata, OSyncChange *chg) 
     151{ 
     152        PyObject *cobject = NULL; 
     153        PyObject *ret = NULL; 
     154 
     155        cobject = PyCObject_FromVoidPtr(chg, NULL); 
     156        if (!cobject) 
     157                goto error; 
     158 
     159        /* ret = opensync.Context(member=None, chg=cobject) */ 
     160        ret = PyObject_CallMethod(mydata->osync_module, "Change", "OO", Py_None, cobject); 
     161 
     162        /* Done, drop reference even on success (the opensync.Change object 
     163         * will hold a reference to the cobject) 
     164         */ 
     165        Py_XDECREF(cobject); 
     166 
     167        return ret; 
     168 
     169error: 
     170        Py_XDECREF(ret); 
     171        Py_XDECREF(cobject); 
     172        return NULL; 
     173} 
     174 
     175/** Create a new opensync.Context object for a given context */ 
     176static PyObject *new_pycontext(struct MemberData *mydata, OSyncContext *ctx) 
     177{ 
     178        PyObject *cobject = NULL; 
     179        PyObject *ret = NULL; 
     180 
     181        cobject = PyCObject_FromVoidPtr(ctx, NULL); 
     182        if (!cobject) 
     183                goto error; 
     184 
     185        /* ret = opensync.Context(cobject) */ 
     186        ret = PyObject_CallMethod(mydata->osync_module, "Context", "O", cobject); 
     187 
     188        /* Done, drop reference even on success (the opensync.Context object 
     189         * will hold a reference to the cobject) 
     190         */ 
     191        Py_XDECREF(cobject); 
     192 
     193        return ret; 
     194 
     195error: 
     196        Py_XDECREF(ret); 
     197        Py_XDECREF(cobject); 
     198        return NULL; 
     199} 
     200 
     201/** Call a python method 
     202 * 
     203 * Methods called using this function can 
     204 * have one of these formats: 
     205 * 
     206 * - function(context) 
     207 * - function(context, change) 
     208 */ 
     209static void call_module_method(OSyncContext *ctx, OSyncChange *chg, char *name) 
     210{ 
     211        PyObject *context = NULL; 
     212        PyObject *ret = NULL; 
     213        PyObject *change = NULL; 
     214 
    161215        struct MemberData *data = osync_context_get_plugin_data(ctx); 
    162216        PyEval_AcquireThread(data->interp_thread); 
    163         /*FIXME: send parameters */ 
    164         { 
    165                 PyObject *ret = PyObject_CallMethod(data->object, name, NULL); 
    166                 if (!ret) { 
    167                         osync_debug("python", 1, "Error during %s() method", name); 
     217 
     218        if (chg) { 
     219                change = new_pychange(data, chg); 
     220                if (!change) { 
     221                        osync_debug("python", 1, "Can't create a change object"); 
     222                        PyErr_Print(); 
     223                        osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't create a opensync.Change object"); 
    168224                        PyErr_Clear(); 
    169                 } else 
    170                         Py_DECREF(ret); 
    171         } 
     225                        goto out; 
     226                } 
     227        } 
     228 
     229        context = new_pycontext(data, ctx); 
     230        if (!context) { 
     231                osync_debug("python", 1, "Can't create a context object"); 
     232                PyErr_Print(); 
     233                osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't create a opensync.Context object"); 
     234                PyErr_Clear(); 
     235                goto out; 
     236        } 
     237 
     238        if (change) 
     239                ret = PyObject_CallMethod(data->object, name, "OO", context, change); 
     240        else 
     241                ret = PyObject_CallMethod(data->object, name, "O", context); 
     242 
     243        if (!ret) { 
     244                osync_debug("python", 1, "Error during %s() method", name); 
     245                PyErr_Print(); 
     246                osync_context_report_error(ctx, OSYNC_ERROR_GENERIC, "Couldn't call python %s() method", name); 
     247                PyErr_Clear(); 
     248                goto out; 
     249        } 
     250 
     251out: 
     252        Py_XDECREF(ret); 
     253        Py_XDECREF(change); 
     254        Py_XDECREF(context); 
    172255        PyEval_ReleaseThread(data->interp_thread); 
    173256} 
     
    175258static void py_connect(OSyncContext *ctx) 
    176259{ 
    177         call_module_method(ctx, "connect"); 
     260        call_module_method(ctx, NULL, "connect"); 
    178261} 
    179262 
     
    181264static void py_get_changeinfo(OSyncContext *ctx) 
    182265{ 
    183         call_module_method(ctx, "get_changeinfo"); 
     266        call_module_method(ctx, NULL, "get_changeinfo"); 
    184267} 
    185268 
    186269 
    187270static void py_get_data(OSyncContext *ctx, OSyncChange *change) 
    188 { /*FIXME: Implement me */ 
     271{ 
     272        call_module_method(ctx, change, "get_data"); 
    189273} 
    190274 
    191275static osync_bool py_access(OSyncContext *ctx, OSyncChange *change) 
    192 { /*FIXME: Implement me */ 
     276{ 
     277        call_module_method(ctx, change, "access"); 
     278        return TRUE; 
     279} 
     280 
     281static osync_bool py_commit_change(OSyncContext *ctx, OSyncChange *change) 
     282{ 
     283        call_module_method(ctx, change, "commit_change"); 
    193284        return 0; 
    194285} 
    195286 
    196 static osync_bool py_commit_change(OSyncContext *ctx, OSyncChange *change) 
    197 { /*FIXME: Implement me */ 
    198         return 0; 
    199 } 
    200  
    201287static void py_sync_done(OSyncContext *ctx) 
    202 { /*FIXME: Implement me */ 
     288{ 
     289        call_module_method(ctx, NULL, "sync_done"); 
    203290} 
    204291 
    205292static void py_disconnect(OSyncContext *ctx) 
    206 { /*FIXME: Implement me */ 
    207 } 
    208  
    209 void get_info(OSyncPluginInfo *info) 
    210 { 
    211         /* Python initialization */ 
    212         struct sigaction old_sigint; 
    213  
    214         /* Hack to make python not overwrite SIGINT */ 
    215         sigaction(SIGINT, NULL, &old_sigint);  /* Save old handler */ 
    216         Py_Initialize(); 
    217         sigaction(SIGINT, &old_sigint, NULL);  /* Restore it */ 
    218         PyEval_InitThreads(); 
    219         py_initialize(NULL); 
    220  
    221         info->name = "python_module"; 
    222         info->version = 1; 
    223         info->is_threadsafe = 1; 
    224          
     293{ 
     294        call_module_method(ctx, NULL, "disconnect"); 
     295} 
     296 
     297/** Register a new plugin from python module called name. 
     298 * 
     299 * @todo We need to avoid the loading of the python runtime 
     300 *       on get_info() somehow, but then we need to store all 
     301 *       plugin information on another place (including 
     302 *       accepted objtypes/formats info) 
     303 */ 
     304static int register_plugin(OSyncEnv *env, PyObject *osync_module, char *name) 
     305{ 
     306        PyObject *module = NULL; 
     307        PyObject *get_info_result = NULL; 
     308        PythonPluginInfo pyinfo; 
     309        PyObject *pyinfo_cobject = NULL; 
     310        PyObject *get_info_parm = NULL; 
     311        OSyncPluginInfo *info; 
     312        int ret = -1; 
     313 
     314        osync_trace(TRACE_ENTRY, "register_plugin"); 
     315 
     316        module = PyImport_ImportModule(name); 
     317        if (!module) { 
     318                osync_debug("python", 1, "Couldn't load testmodule"); 
     319                PyErr_Print(); 
     320                PyErr_Clear(); 
     321                goto out; 
     322        } 
     323 
     324        info = osync_plugin_new_info(env); 
     325        if (!info) { 
     326                osync_debug("python", 1, "Couldn't create a new plugin object"); 
     327                goto error; 
     328        } 
     329 
    225330        info->functions.initialize = py_initialize; 
    226331        info->functions.connect = py_connect; 
     
    231336        info->functions.get_data = py_get_data; 
    232337         
    233         /*FIXME: We can't know all accepted objtypes here. Only after 
    234          * loading the python module. This is possible only after loading 
    235          * the python module. 
    236          * 
    237          * There are two approaches to fix this: the first one is 
    238          * getting the python module name from the configuration 
    239          * data, loading the module and registering the formats on 
    240          * initialize() time. Then we need to support registering 
    241          * the accepted formats on initialize(). Even if we choose 
    242          * the other approach described below, maybe it would be a good thing 
    243          * make possible to register the accepted formats on initialize 
    244          * (but I am not sure, maybe it is a good thing to force the 
    245          * format information to be available after get_info() 
    246          * 
    247          * Another possible fix: make possible to 
    248          * a single loadable module report any number of plugin info, 
    249          * so the get_info() function here just 
    250          * scans the OSYNC_LIBDIR/python-plugins directory, and reports 
    251          * all plugins. I think this is more elegant, and the better approach. 
    252          * It has the disadvantage of loading the python interpreter at plugin-listing 
    253          * time, but if we have any python plugins available, listing them 
    254          * is expected, anyway 
     338        /*TODO: let the module get_info function fill the fields below: */ 
     339        info->is_threadsafe = 1; 
     340 
     341        /* The plugin data is just the plugin name, 
     342         * to be used on py_initialize() 
    255343         */ 
    256         osync_plugin_accept_objtype(info, "contact"); 
    257         osync_plugin_accept_objformat(info, "contact", "vcard30"); 
    258         osync_plugin_set_commit_objformat(info, "contact", "vcard30", py_commit_change); 
    259         osync_plugin_set_access_objformat(info, "contact", "vcard30", py_access); 
    260 } 
     344        info->plugin_data = strdup(name); 
     345         
     346        /** Build a PluginInfo object for use by get_info */ 
     347        pyinfo.commit_fn = py_commit_change; 
     348        pyinfo.access_fn = py_access; 
     349        pyinfo.osync_info = info;