Updated sample.py with recent opensync library python API changes
[opensync/python-module.git] / src / python_module.c
blob5d607eb424d281ac80c4695b6feddfa004682fa2
1 /* Python module for OpenSync
2 * Copyright (C) 2005 Eduardo Pereira Habkost <ehabkost@conectiva.com.br>
3 * Copyright (C) 2007 Andrew Baumann <andrewb@cse.unsw.edu.au>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * @author Eduardo Pereira Habkost <ehabkost@conectiva.com.br>
20 * @author Andrew Baumann <andrewb@cse.unsw.edu.au>
22 * Additional changes by Armin Bauer <armin.bauer@desscon.com>
25 #include <Python.h>
26 #include <opensync/opensync.h>
27 #include <opensync/opensync-plugin.h>
28 #include <opensync/opensync-client.h>
29 #include <signal.h>
30 #include <glib.h>
32 /* change this define for python exception output on stderr */
33 //#define PYERR_CLEAR() PyErr_Clear()
34 #define PYERR_CLEAR() PyErr_Print()
36 typedef struct MemberData {
37 PyObject *osync_module;
38 PyObject *module;
39 PyObject *init_return;
40 } MemberData;
42 static PyObject *pm_load_opensync(OSyncError **error)
44 PyObject *osync_module = PyImport_ImportModule("opensync1");
45 if (!osync_module) {
46 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load OpenSync1 module");
47 PYERR_CLEAR();
48 return NULL;
50 return osync_module;
53 static PyObject *pm_make_change(PyObject *osync_module, OSyncChange *change, OSyncError **error)
55 PyObject *pychg_cobject = PyCObject_FromVoidPtr(change, NULL);
56 if (!pychg_cobject) {
57 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pychg cobject");
58 PYERR_CLEAR();
59 return NULL;
62 PyObject *pychg = PyObject_CallMethod(osync_module, "Change", "O", pychg_cobject);
63 Py_DECREF(pychg_cobject);
64 if (!pychg) {
65 osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncChange");
66 PYERR_CLEAR();
67 return NULL;
69 return pychg;
72 static PyObject *pm_make_context(PyObject *osync_module, OSyncContext *ctx, OSyncError **error)
74 PyObject *pyctx_cobject = PyCObject_FromVoidPtr(ctx, NULL);
75 if (!pyctx_cobject) {
76 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyctx cobject");
77 PYERR_CLEAR();
78 return NULL;
81 PyObject *pyctx = PyObject_CallMethod(osync_module, "Context", "O", pyctx_cobject);
82 Py_DECREF(pyctx_cobject);
83 if (!pyctx) {
84 osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncContext");
85 PYERR_CLEAR();
86 return NULL;
88 return pyctx;
91 static PyObject *pm_make_info(PyObject *osync_module, OSyncPluginInfo *info, OSyncError **error)
93 PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL);
94 if (!pyinfo_cobject) {
95 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject");
96 PYERR_CLEAR();
97 return NULL;
100 PyObject *pyinfo = PyObject_CallMethod(osync_module, "PluginInfo", "O", pyinfo_cobject);
101 Py_DECREF(pyinfo_cobject);
102 if (!pyinfo) {
103 osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPluginInfo");
104 PYERR_CLEAR();
105 return NULL;
107 return pyinfo;
110 /* convert a python exception to an OSyncError containing the traceback of the exception */
111 static void pm_pyexcept_to_oserror(PyObject *pytype, PyObject *pyvalue, PyObject *pytraceback, OSyncError **error)
113 const char *errmsg = NULL;
114 PyObject *tracebackmod = NULL, *stringmod = NULL;
115 PyObject *pystrs = NULL, *pystr = NULL;
117 tracebackmod = PyImport_ImportModule("traceback");
118 if (!tracebackmod) {
119 errmsg = "import traceback";
120 goto error;
123 if (pytraceback == NULL) {
124 pystrs = PyObject_CallMethod(tracebackmod, "format_exception_only", "OO", pytype, pyvalue);
125 if (!pystrs) {
126 errmsg = "traceback.format_exception_only";
127 goto error;
129 } else {
130 pystrs = PyObject_CallMethod(tracebackmod, "format_exception", "OOO", pytype, pyvalue, pytraceback);
131 if (!pystrs) {
132 errmsg = "traceback.format_exception";
133 goto error;
137 stringmod = PyImport_ImportModule("string");
138 if (!stringmod) {
139 errmsg = "import string";
140 goto error;
143 pystr = PyObject_CallMethod(stringmod, "join", "Os", pystrs, "");
144 if (!pystr) {
145 errmsg = "string.join";
146 goto error;
149 osync_error_set(error, OSYNC_ERROR_GENERIC, "%s", PyString_AsString(pystr));
151 error:
152 Py_XDECREF(tracebackmod);
153 Py_XDECREF(stringmod);
154 Py_XDECREF(pystrs);
155 Py_XDECREF(pystr);
157 if (errmsg) {
158 PYERR_CLEAR();
159 osync_error_set(error, OSYNC_ERROR_GENERIC, "pm_pyexcept_to_oserror: failed to report error: exception in %s", errmsg);
163 /** Call a python method, report any exception it raises as an error, if no exception was raised report success
165 * Methods called using this function can
166 * have one of these formats:
168 * - function(info, context)
169 * - function(info, context, change)
171 static osync_bool pm_call_module_method(OSyncObjTypeSink *sink,
172 OSyncPluginInfo *info, OSyncContext *ctx, osync_bool *pslow_sync,
173 void *userdata, char *name, OSyncChange *chg)
175 osync_trace(TRACE_ENTRY, "%s(%s, %p, %p, %p)", __func__, name, info, ctx, chg);
176 PyObject *ret = NULL;
177 OSyncError *error = NULL;
178 osync_bool report_error = TRUE;
179 PyObject *osync_module = NULL;
181 PyGILState_STATE pystate = PyGILState_Ensure();
183 PyObject *sink_pyobject = userdata;
184 if (!sink_pyobject) {
185 osync_error_set(&error, OSYNC_ERROR_GENERIC, "%s: %s: sink has no callback object", __func__, name);
186 goto error;
189 if (!(osync_module = pm_load_opensync(&error)))
190 goto error;
192 PyObject *pyinfo = pm_make_info(osync_module, info, &error);
193 if (!pyinfo)
194 goto error;
196 PyObject *pycontext = pm_make_context(osync_module, ctx, &error);
197 if (!pycontext) {
198 Py_DECREF(pyinfo);
199 goto error;
202 if (chg) {
203 PyObject *pychange = pm_make_change(osync_module, chg, &error);
204 if (!pychange) {
205 Py_DECREF(pycontext);
206 Py_DECREF(pyinfo);
207 goto error;
210 ret = PyObject_CallMethod(sink_pyobject, name, "OOO", pyinfo, pycontext, pychange);
212 Py_DECREF(pychange);
214 else if (pslow_sync) {
215 // this is get_changes or connect_done, which passes a slow_sync flag
216 ret = PyObject_CallMethod(sink_pyobject, name, "OOO",
217 pyinfo, pycontext, (*pslow_sync ? Py_True : Py_False));
219 else {
220 ret = PyObject_CallMethod(sink_pyobject, name, "OO", pyinfo, pycontext);
223 Py_DECREF(pyinfo);
225 if (ret) {
226 Py_DECREF(pycontext);
227 Py_DECREF(ret);
228 Py_XDECREF(osync_module);
229 PyGILState_Release(pystate);
230 osync_context_report_success(ctx);
231 osync_trace(TRACE_EXIT, "%s", __func__);
232 return TRUE;
235 /* an exception occurred. get the python exception data */
236 PyObject *pytype, *pyvalue, *pytraceback;
237 PyErr_Fetch(&pytype, &pyvalue, &pytraceback);
239 PyObject *osyncerror = NULL;
240 osyncerror = PyObject_GetAttrString(osync_module, "Error");
241 if (!osyncerror) {
242 PYERR_CLEAR();
243 osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed to get OSyncError class object");
244 goto out;
247 if (PyErr_GivenExceptionMatches(pytype, osyncerror)) {
248 /* if it's an OSyncError, just report that up on the context object */
249 PyObject *obj = PyObject_CallMethod(pyvalue, "report", "O", pycontext);
250 if (!obj) {
251 PYERR_CLEAR();
252 osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting OSyncError");
253 goto out;
256 Py_DECREF(obj);
257 osync_error_set(&error, OSYNC_ERROR_GENERIC, "Reported OSyncError");
258 report_error = FALSE;
259 } else if (PyErr_GivenExceptionMatches(pytype, PyExc_IOError)
260 || PyErr_GivenExceptionMatches(pytype, PyExc_OSError)) {
261 /* for IOError or OSError, we just report the &error message */
262 PyObject *pystr = PyObject_Str(pyvalue);
263 if (!pystr) {
264 PYERR_CLEAR();
265 osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting IOError/OSError");
266 goto out;
269 osync_error_set(&error, OSYNC_ERROR_IO_ERROR, "%s", PyString_AsString(pystr));
270 Py_DECREF(pystr);
271 } else {
272 /* for other exceptions, we report a full traceback */
273 pm_pyexcept_to_oserror(pytype, pyvalue, pytraceback, &error);
276 out:
277 Py_DECREF(pycontext);
278 Py_XDECREF(pytype);
279 Py_XDECREF(pyvalue);
280 Py_XDECREF(pytraceback);
281 Py_XDECREF(osyncerror);
283 error:
284 Py_XDECREF(osync_module);
285 PyGILState_Release(pystate);
286 if (report_error)
287 osync_context_report_osyncerror(ctx, error);
288 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
289 return FALSE;
292 static void pm_connect(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
293 OSyncContext *ctx, void *userdata)
295 pm_call_module_method(sink, info, ctx, NULL, userdata, "connect", NULL);
298 static void pm_disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
299 OSyncContext *ctx, void *userdata)
301 pm_call_module_method(sink, info, ctx, NULL, userdata, "disconnect", NULL);
304 static void pm_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
305 OSyncContext *ctx, osync_bool slow_sync, void *userdata)
307 pm_call_module_method(sink, info, ctx, &slow_sync, userdata,
308 "get_changes", NULL);
311 static void pm_commit(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
312 OSyncContext *ctx, OSyncChange *change, void *userdata)
314 pm_call_module_method(sink, info, ctx, NULL, userdata, "commit", change);
317 static void pm_committed_all(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
318 OSyncContext *ctx, void *userdata)
320 pm_call_module_method(sink, info, ctx, NULL, userdata, "committed_all", NULL);
323 static void pm_read(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
324 OSyncContext *ctx, OSyncChange *change, void *userdata)
326 pm_call_module_method(sink, info, ctx, NULL, userdata, "read", change);
329 static void pm_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
330 OSyncContext *ctx, void *userdata)
332 pm_call_module_method(sink, info, ctx, NULL, userdata, "sync_done", NULL);
335 static void pm_connect_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info,
336 OSyncContext *ctx, osync_bool slow_sync, void *userdata)
338 pm_call_module_method(sink, info, ctx, &slow_sync, userdata, "connect_done", NULL);
342 /** Calls the method initialize function
344 * The python initialize() function register one or more sink objects
345 * that have the other plugin methods (get_changeinfo, commit, etc.)
347 static void *pm_initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error)
349 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
350 MemberData *data = g_malloc0(sizeof(MemberData));
351 char *modulename = NULL;
352 OSyncList *s, *sinks = NULL;
353 OSyncObjTypeSink *sink = NULL;
355 PyGILState_STATE pystate = PyGILState_Ensure();
357 if (!data) {
358 osync_error_set(error, OSYNC_ERROR_GENERIC, "Failed to allocate module data");
359 goto error;
362 // The modulename is set in the xml file, so we can use it
363 // here, but we only need it for initialize, so free it below
364 // once we're done with it. We set the plugin data to NULL here
365 // as well.
366 if (!(modulename = osync_plugin_get_data(plugin))) {
367 osync_error_set(error, OSYNC_ERROR_GENERIC, "Failed to retrieve module name");
368 goto error;
370 osync_plugin_set_data(plugin, NULL);
372 if (!(data->osync_module = pm_load_opensync(error)))
373 goto error;
375 if (!(data->module = PyImport_ImportModule(modulename))) {
376 PYERR_CLEAR();
377 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module %s", modulename);
378 free(modulename);
379 goto error;
381 free(modulename);
383 PyObject *pyinfo = pm_make_info(data->osync_module, info, error);
384 if (!pyinfo)
385 goto error;
387 PyObject *ret = PyObject_CallMethod(data->module, "initialize", "O", pyinfo);
388 Py_DECREF(pyinfo);
389 if (!ret) {
390 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize module");
391 PYERR_CLEAR();
392 goto error;
395 /* loop through all objtype sinks, set up function pointers */
396 sinks = osync_plugin_info_get_objtype_sinks(info);
397 for (s = sinks; s; s = s->next) {
398 sink = (OSyncObjTypeSink *)s->data;
400 osync_objtype_sink_set_connect_func(sink, pm_connect);
401 osync_objtype_sink_set_disconnect_func(sink, pm_disconnect);
402 osync_objtype_sink_set_get_changes_func(sink, pm_get_changes);
403 osync_objtype_sink_set_commit_func(sink, pm_commit);
404 osync_objtype_sink_set_committed_all_func(sink, pm_committed_all);
405 osync_objtype_sink_set_read_func(sink, pm_read);
406 osync_objtype_sink_set_sync_done_func(sink, pm_sync_done);
407 osync_objtype_sink_set_connect_done_func(sink, pm_connect_done);
409 osync_list_free(sinks);
411 /* set functions for the main sink, if one is provided */
412 sink = osync_plugin_info_get_main_sink(info);
413 if (sink) {
414 osync_objtype_sink_set_connect_func(sink, pm_connect);
415 osync_objtype_sink_set_disconnect_func(sink, pm_disconnect);
416 osync_objtype_sink_set_get_changes_func(sink, pm_get_changes);
417 osync_objtype_sink_set_commit_func(sink, pm_commit);
418 osync_objtype_sink_set_committed_all_func(sink, pm_committed_all);
419 osync_objtype_sink_set_read_func(sink, pm_read);
420 osync_objtype_sink_set_sync_done_func(sink, pm_sync_done);
421 osync_objtype_sink_set_connect_done_func(sink, pm_connect_done);
424 /* return value can be module data, if it's not None, store it */
425 if (ret == Py_None) {
426 Py_DECREF(ret); // decrement for the CallMethod above
427 ret = NULL;
429 data->init_return = ret; /* if it's an object, this takes our ref to it */
431 PyGILState_Release(pystate);
432 osync_trace(TRACE_EXIT, "%s", __func__);
433 return data;
435 error:
436 Py_XDECREF(data->module);
437 Py_XDECREF(data->osync_module);
438 PyGILState_Release(pystate);
439 if (data) free(data);
440 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
441 return NULL;
444 static osync_bool pm_discover(OSyncPluginInfo *info, void *data_in, OSyncError **error)
446 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data_in, info, error);
448 MemberData *data = data_in;
450 PyGILState_STATE pystate = PyGILState_Ensure();
452 PyObject *pyinfo = pm_make_info(data->osync_module, info, error);
453 if (!pyinfo)
454 goto error;
456 PyObject *ret = NULL;
457 if (data->init_return) {
458 ret = PyObject_CallMethod(data->module, "discover", "OO", pyinfo, data->init_return);
459 } else {
460 Py_INCREF(Py_None);
461 ret = PyObject_CallMethod(data->module, "discover", "OO", pyinfo, Py_None);
462 Py_DECREF(Py_None);
465 Py_DECREF(pyinfo);
466 if (!ret)
467 goto error;
469 Py_DECREF(ret);
470 PyGILState_Release(pystate);
471 osync_trace(TRACE_EXIT, "%s", __func__);
472 return TRUE;
474 error:
475 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't call discover method");
476 PYERR_CLEAR();
477 PyGILState_Release(pystate);
478 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
479 return FALSE;
482 static void pm_finalize(void *data)
484 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
485 MemberData *mydata = data;
486 OSyncError **error = NULL;
487 PyGILState_STATE pystate = PyGILState_Ensure();
488 PyObject *ret = NULL;
489 int has_finalize = 0;
491 /* we make finalize optional in the plugin */
492 if ((has_finalize = PyObject_HasAttrString(mydata->module, "finalize")) == 1) {
493 if (mydata->init_return) {
494 ret = PyObject_CallMethod(mydata->module, "finalize", "O", mydata->init_return);
495 } else {
496 Py_INCREF(Py_None);
497 ret = PyObject_CallMethod(mydata->module, "finalize", "O", Py_None);
498 Py_DECREF(Py_None);
502 if (mydata->init_return) Py_DECREF(mydata->init_return);
503 Py_DECREF(mydata->module);
504 Py_DECREF(mydata->osync_module);
505 free(mydata);
507 if ((has_finalize == 1) && (!ret))
508 goto error;
510 Py_XDECREF(ret);
511 PyGILState_Release(pystate);
512 osync_trace(TRACE_EXIT, "%s", __func__);
513 return;
515 error:
516 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't call finalize method");
517 PYERR_CLEAR();
518 PyGILState_Release(pystate);
519 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
520 return;
523 /* set python search path to look in our module directory first */
524 static osync_bool set_search_path(OSyncError **error)
526 PyObject *sys_module = PyImport_ImportModule("sys");
527 if (!sys_module) {
528 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't import sys module");
529 PYERR_CLEAR();
530 return FALSE;
533 PyObject *path = PyObject_GetAttrString(sys_module, "path");
534 if (!path) {
535 osync_error_set(error, OSYNC_ERROR_GENERIC, "sys module has no path attribute?");
536 PYERR_CLEAR();
537 Py_DECREF(sys_module);
538 return FALSE;
541 if (!PyList_Check(path)) {
542 osync_error_set(error, OSYNC_ERROR_GENERIC, "sys.path is not a list?");
543 Py_DECREF(path);
544 Py_DECREF(sys_module);
545 return FALSE;
548 PyObject *plugindir = Py_BuildValue("s", OPENSYNC_PYTHONPLG_DIR);
549 if (!plugindir) {
550 osync_error_set(error, OSYNC_ERROR_GENERIC, "Error constructing plugindir string for sys.path");
551 PYERR_CLEAR();
552 Py_DECREF(path);
553 Py_DECREF(sys_module);
554 return FALSE;
557 int r = PySequence_Contains(path, plugindir);
558 if (r < 0) {
559 osync_error_set(error, OSYNC_ERROR_GENERIC, "Error checking for 'plugindir in sys.path'");
560 PYERR_CLEAR();
561 Py_DECREF(plugindir);
562 Py_DECREF(path);
563 Py_DECREF(sys_module);
564 return FALSE;
567 if (r == 0 && PyList_Insert(path, 0, plugindir) != 0) {
568 osync_error_set(error, OSYNC_ERROR_GENERIC, "Error inserting plugin directory into sys.path");
569 PYERR_CLEAR();
570 Py_DECREF(plugindir);
571 Py_DECREF(path);
572 Py_DECREF(sys_module);
573 return FALSE;
576 Py_DECREF(plugindir);
577 Py_DECREF(path);
578 Py_DECREF(sys_module);
580 return TRUE;
583 int main(int argc, char *argv[])
585 osync_trace(TRACE_ENTRY, "%s(%d, %p)", __func__, argc, argv);
587 const char *modulename = NULL;
588 const char *pipe_path = NULL;
589 int arg = 1;
590 OSyncError *error = NULL;
591 OSyncClient *client = NULL;
592 OSyncPlugin *plugin = NULL;
595 // command line args
598 // check for plugin modulename
599 if (argc >= (arg+1)) {
600 modulename = argv[arg];
601 arg++;
603 else {
604 fprintf(stderr, "module name is missing!\n");
605 return 1;
608 // check for pipe path
609 if (argc >= (arg+1)) {
610 pipe_path = argv[arg];
611 arg++;
613 else {
614 fprintf(stderr, "pipe path is missing!\n");
615 return 2;
620 // load python library
623 /* Because OpenSync likes to call this function multiple times in
624 * different threads, and because we may be sharing the python
625 * interpreter with other code, we have to:
626 * * init python only once
627 * * acquire the Python lock before making any API calls
630 if (!Py_IsInitialized()) {
631 /* We're the first user of python in this process. Initialise
632 * it, enable threading, and release the lock that will be
633 * re-acquired by the PyGILState_Ensure() call below. */
634 Py_InitializeEx(0);
635 PyEval_InitThreads();
636 PyThreadState *pts = PyGILState_GetThisThreadState();
637 PyEval_ReleaseThread(pts);
638 } else if (!PyEval_ThreadsInitialized()) {
639 /* Python has been initialised, but threads are not. */
640 osync_error_set(&error, OSYNC_ERROR_GENERIC, "The Python interpreter in this process has been initialised without threading support.");
641 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
642 return FALSE;
645 PyGILState_STATE pystate = PyGILState_Ensure();
647 if (!set_search_path(&error))
648 goto state_error;
650 // import opensync module
651 PyObject *osync_module = pm_load_opensync(&error);
652 if (!osync_module)
653 goto state_error;
655 PyGILState_Release(pystate);
659 // create plugin
661 plugin = osync_plugin_new(&error);
662 if (!plugin)
663 goto error;
665 osync_plugin_set_initialize_func(plugin, pm_initialize);
666 osync_plugin_set_finalize_func(plugin, pm_finalize);
667 osync_plugin_set_discover_func(plugin, pm_discover);
668 osync_plugin_set_data(plugin, g_strdup(modulename));
672 // create client
674 client = osync_client_new(&error);
675 if (!client)
676 goto error;
678 osync_client_set_pipe_path(client, pipe_path);
679 osync_client_set_plugin(client, plugin);
680 osync_plugin_unref(plugin);
683 printf("[python_module %s]: %s OSyncPlugin:%p OSyncClient:%p\n",
684 modulename, __func__, plugin, client);
685 printf("[python_module %s]: Starting (blocking) OSyncClient ...\n",
686 modulename);
688 if (!osync_client_run_and_block(client, &error))
689 goto error;
691 printf("[python_module %s]: OSyncClient completed.\n", modulename);
693 osync_client_unref(client);
695 osync_trace(TRACE_EXIT, "%s", __func__);
696 return 0;
698 state_error:
699 PyGILState_Release(pystate);
701 error:
702 fprintf(stderr, "[python_module %s] Error: %s\n",
703 modulename, osync_error_print(&error));
704 osync_error_unref(&error);
705 osync_trace(TRACE_EXIT_ERROR, "%s", __func__);
706 return 1;