Changeset 487
- Timestamp:
- 04/11/05 17:41:43 (4 years ago)
- Location:
- plugins/python-module
- Files:
-
- 6 modified
- 3 copied
-
. (modified) (1 prop)
-
Makefile.am (modified) (2 diffs)
-
configure.in (modified) (1 diff)
-
setup.py (copied) (copied from branches/python-plugins/plugins/python-module/setup.py)
-
src/opensync.pyx (modified) (8 diffs)
-
src/python_module.c (modified) (12 diffs)
-
src/pywrap.c (copied) (copied from branches/python-plugins/plugins/python-module/src/pywrap.c)
-
src/pywrap.h (modified) (1 diff)
-
src/sample.py (copied) (copied from branches/python-plugins/plugins/python-module/src/sample.py)
Legend:
- Unmodified
- Added
- Removed
-
plugins/python-module
- Property svn:ignore
-
old new 13 13 missing 14 14 install-sh 15 config.log 16 config.h 17 .libs 18 .deps 19 Makefile 20 config.status 21 stamp-h1 22 libtool
-
- Property svn:ignore
-
plugins/python-module/Makefile.am
r108 r487 1 1 plugindir=@OPENSYNC_PLUGINDIR@ 2 2 pythonplgdir=@OPENSYNC_PYTHONPLG_DIR@ 3 pythonlibdir=@OPENSYNC_PYTHONLIB_DIR@4 3 5 4 OPENSYNC_LIBS=-lopensync … … 7 6 #FIXME: set EXTRA_DIST 8 7 8 EXTRA_DIST = \ 9 src/pywrap.c \ 10 src/opensync.pyx 9 11 10 12 plugin_LTLIBRARIES = python_module.la 11 13 12 14 python_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 15 python_module_la_LDFLAGS = @PYTHON_LDFLAGS@ -avoid-version -export-dynamic -module $(OSYNC_LIBS) 16 python_module_la_CFLAGS = -Wall -Werror $(OSYNC_CFLAGS) 15 17 python_module_la_CPPFLAGS = @PYTHON_CFLAGS@ \ 16 -DOPENSYNC_PYTHONPLG_DIR=\"$(pythonplgdir)\" \ 17 -DOPENSYNC_PYTHONLIB_DIR=\"$(pythonlibdir)\" 18 -DOPENSYNC_PYTHONPLG_DIR=\"$(pythonplgdir)\" 18 19 python_module_la_LIBADD = @PYTHON_LIBS@ 19 20 20 21 21 # yes, _PROGRAMS. I am trying to avoid using libtool here22 pythonlib_PROGRAMS = opensync.so23 24 #FIXME: the lines below don't work. check why (maybe use distutils?)25 # only -Werror. pyrex generated code doesn't support -Wall26 opensync_so_CFLAGS = -Werror -fPIC27 opensync_so_CPPFLAGS = @PYTHON_CFLAGS@ -Werror28 nodist_opensync_so_SOURCES = src/opensync.c29 opensync_so_LDFLAGS = @PYTHON_LDFLAGS@ -shared30 opensync_so_LDADD = $(OPENSYNC_LIBS)31 22 32 23 src/opensync.c: src/opensync.pyx 33 24 pyrexc $< 25 26 # opensync python module building rules 27 # It uses setup.py and distutils to 28 # make things easier 29 opensync.so: src/opensync.c setup.py 30 CFLAGS="$$CFLAGS $(OSYNC_CFLAGS)" python setup.py build_ext 31 32 all-local: opensync.so 33 34 install-exec-local: 35 python setup.py install 36 37 clean-local: 38 python setup.py clean -
plugins/python-module/configure.in
r404 r487 67 67 os_cv_python_sdk=yes])])]) 68 68 69 OPENSYNC_PLUGINDIR=${libdir}/opensync/plugins 69 PKG_CHECK_MODULES(OSYNC, [opensync-1.0]) 70 71 OPENSYNC_CONFIGDIR=$(pkg-config --variable=configdir opensync-1.0) 72 OPENSYNC_PLUGINDIR=$(pkg-config --variable=plugindir opensync-1.0) 73 OPENSYNC_FORMATSDIR=$(pkg-config --variable=formatsdir opensync-1.0) 74 OPENSYNC_HEADERDIR=$(pkg-config --variable=headerdir opensync-1.0) 75 76 AC_SUBST(OPENSYNC_CONFIGDIR) 70 77 AC_SUBST(OPENSYNC_PLUGINDIR) 78 AC_SUBST(OPENSYNC_FORMATSDIR) 79 AC_SUBST(OPENSYNC_HEADERDIR) 71 80 81 #FIXME: Set the python plugin path using a better method, instead of just 82 # using libdir 72 83 OPENSYNC_PYTHONPLG_DIR=${libdir}/opensync/python-plugins 73 84 AC_SUBST(OPENSYNC_PYTHONPLG_DIR) 74 85 75 #FIXME: install opensync module on python library path, not on opensync directory76 OPENSYNC_PYTHONLIB_DIR=${libdir}/opensync/python77 AC_SUBST(OPENSYNC_PYTHONLIB_DIR)78 79 86 AC_OUTPUT([Makefile]) -
plugins/python-module/src/opensync.pyx
r108 r487 29 29 OSyncObjFormat *osync_change_get_objformat(OSyncChange *change) 30 30 void osync_change_set_member(OSyncChange *change, OSyncMember *member) 31 void osync_change_ref(OSyncChange *change) 32 void osync_change_decref(OSyncChange *change) 31 33 32 34 #objformat methods … … 39 41 40 42 cdef 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 41 48 OSyncMember *osync_member_from_void(void *m) 42 49 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) 43 57 44 58 cdef class Member: … … 59 73 self.memb = osync_member_from_void(v) 60 74 75 # forward declaration 61 76 cdef class Change 62 77 … … 64 79 """opensync OSyncContext object""" 65 80 cdef OSyncContext *ctx 66 cdef void *v67 81 def __new__(self, c): 68 82 """Never call this method from python code. … … 70 84 Objects of this class are created only internally by OpenSync 71 85 """ 72 # Another hack like the OSyncMember hack above,73 # to be able to convert a python object74 # to OSyncContext *75 86 cdef void *v 76 87 v = PyCObject_AsVoidPtr(c) … … 78 89 79 90 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 80 99 osync_context_report_change(self.ctx, chg.chg) 81 100 … … 90 109 cdef OSyncChange *chg 91 110 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) 95 125 96 126 def __dealloc__(self): 97 osync_change_ free(self.chg)127 osync_change_decref(self.chg) 98 128 99 129 def set_objformat(self, name): … … 106 136 else: return None 107 137 138 cdef 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); 108 168 109 169 # vim:ft=python -
plugins/python-module/src/python_module.c
r167 r487 11 11 #include <signal.h> 12 12 13 #include "pywrap.h" 13 14 14 15 struct MemberData { … … 19 20 }; 20 21 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 31 23 * 32 24 * 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 33 28 */ 34 29 static int change_sys_path() 35 30 { 36 31 int rv = -1; 37 PyObject *sys, *path, *dir; 32 PyObject *sys = NULL; 33 PyObject *path = NULL; 34 PyObject *dir = NULL; 38 35 39 36 sys = PyImport_ImportModule("sys"); 40 if (!sys) goto error _sys;37 if (!sys) goto error; 41 38 42 39 path = PyObject_GetAttrString(sys, "path"); 43 if (!path) goto error _path;40 if (!path) goto error; 44 41 45 42 dir = PyString_FromString(OPENSYNC_PYTHONPLG_DIR); 46 if (!dir) goto error _dir;43 if (!dir) goto error; 47 44 48 45 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; 60 47 61 48 /* Success */ 62 49 rv = 0; 63 50 64 error_insert: 65 Py_DECREF(dir); 66 error_dir: 67 Py_DECREF(path); 68 error_path: 69 Py_DECREF(sys); 70 error_sys: 51 error: 52 Py_XDECREF(dir); 53 Py_XDECREF(path); 54 Py_XDECREF(sys); 71 55 return rv; 72 56 } … … 74 58 /** Calls the method initialize function 75 59 * 76 * The initialize() function should return an object that60 * The python initialize() function should return an object that 77 61 * has the other plugin methods (get_changeinfo, commit, etc.) 78 62 */ 79 static void *py_initialize(OSyncMember *member) 80 { 63 static void *py_initialize(OSyncMember *member, OSyncError **error) 64 { 65 char *name; 81 66 struct MemberData *data = malloc(sizeof(struct MemberData)); 82 67 if (!data) 68 /*FIXME: set error info */ 83 69 return NULL; 84 70 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 95 77 data->interp_thread = Py_NewInterpreter(); 96 78 if (0 && !data->interp_thread) { … … 100 82 if (change_sys_path() < 0) { 101 83 osync_debug("python", 1, "Exception when initializing python intepreter"); 84 PyErr_Print(); 102 85 PyErr_Clear(); 103 86 goto error_free_interp; … … 106 89 if (!data->module) { 107 90 osync_debug("python", 1, "Couldn't load OpenSync module"); 91 PyErr_Print(); 108 92 PyErr_Clear(); 109 93 goto error_free_interp; 110 94 } 111 data->module = PyImport_ImportModule( "testmodule");95 data->module = PyImport_ImportModule(name); 112 96 if (!data->module) { 113 97 osync_debug("python", 1, "Couldn't load testmodule"); 98 PyErr_Print(); 114 99 PyErr_Clear(); 115 100 goto error_unload_osync_mod; … … 120 105 if (!data->object) { 121 106 osync_debug("python", 1, "Error during initialize()"); 107 PyErr_Print(); 122 108 PyErr_Clear(); 123 109 goto error_unload_module; … … 141 127 142 128 static void py_finalize(void *data) 143 { /*FIXME: Implement me */129 { 144 130 struct MemberData *mydata = data; 145 131 PyEval_AcquireThread(mydata->interp_thread); … … 148 134 if (!ret) { 149 135 osync_debug("python", 1, "Error during finalize()"); 136 PyErr_Print(); 150 137 PyErr_Clear(); 151 138 } else … … 154 141 Py_DECREF(mydata->object); 155 142 Py_DECREF(mydata->module); 143 Py_DECREF(mydata->osync_module); 144 free(mydata); 145 156 146 Py_EndInterpreter(mydata->interp_thread); 157 147 } 158 148 159 static void call_module_method(OSyncContext *ctx, char *name) 160 { 149 /** Create a new opensync.Change object for a given change */ 150 static 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 169 error: 170 Py_XDECREF(ret); 171 Py_XDECREF(cobject); 172 return NULL; 173 } 174 175 /** Create a new opensync.Context object for a given context */ 176 static 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 195 error: 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 */ 209 static void call_module_method(OSyncContext *ctx, OSyncChange *chg, char *name) 210 { 211 PyObject *context = NULL; 212 PyObject *ret = NULL; 213 PyObject *change = NULL; 214 161 215 struct MemberData *data = osync_context_get_plugin_data(ctx); 162 216 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"); 168 224 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 251 out: 252 Py_XDECREF(ret); 253 Py_XDECREF(change); 254 Py_XDECREF(context); 172 255 PyEval_ReleaseThread(data->interp_thread); 173 256 } … … 175 258 static void py_connect(OSyncContext *ctx) 176 259 { 177 call_module_method(ctx, "connect");260 call_module_method(ctx, NULL, "connect"); 178 261 } 179 262 … … 181 264 static void py_get_changeinfo(OSyncContext *ctx) 182 265 { 183 call_module_method(ctx, "get_changeinfo");266 call_module_method(ctx, NULL, "get_changeinfo"); 184 267 } 185 268 186 269 187 270 static void py_get_data(OSyncContext *ctx, OSyncChange *change) 188 { /*FIXME: Implement me */ 271 { 272 call_module_method(ctx, change, "get_data"); 189 273 } 190 274 191 275 static 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 281 static osync_bool py_commit_change(OSyncContext *ctx, OSyncChange *change) 282 { 283 call_module_method(ctx, change, "commit_change"); 193 284 return 0; 194 285 } 195 286 196 static osync_bool py_commit_change(OSyncContext *ctx, OSyncChange *change)197 { /*FIXME: Implement me */198 return 0;199 }200 201 287 static void py_sync_done(OSyncContext *ctx) 202 { /*FIXME: Implement me */ 288 { 289 call_module_method(ctx, NULL, "sync_done"); 203 290 } 204 291 205 292 static 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 */ 304 static 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 225 330 info->functions.initialize = py_initialize; 226 331 info->functions.connect = py_connect; … … 231 336 info->functions.get_data = py_get_data; 232 337 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() 255 343 */ 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;
