Hello community,
here is the log from the commit of package libopensync-plugin-python-module
checked in at Fri Aug 3 15:12:32 CEST 2007.
--------
--- libopensync-plugin-python-module/libopensync-plugin-python-module.changes 2007-07-18 16:52:06.000000000 +0200
+++ /mounts/work_src_done/STABLE/libopensync-plugin-python-module/libopensync-plugin-python-module.changes 2007-08-02 15:50:35.000000000 +0200
@@ -1,0 +2,6 @@
+Thu Aug 2 15:50:22 CEST 2007 - cstender@suse.de
+
+- updated to version 0.32
+ o ported to the new OpenSync 0.3x API
+
+-------------------------------------------------------------------
Old:
----
libopensync-plugin-python-module-0.22.tar.bz2
New:
----
libopensync-plugin-python-module-0.32.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ libopensync-plugin-python-module.spec ++++++
--- /var/tmp/diff_new_pack.rc8448/_old 2007-08-03 15:11:56.000000000 +0200
+++ /var/tmp/diff_new_pack.rc8448/_new 2007-08-03 15:11:56.000000000 +0200
@@ -1,5 +1,5 @@
#
-# spec file for package libopensync-plugin-python-module (Version 0.22)
+# spec file for package libopensync-plugin-python-module (Version 0.32)
#
# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany.
# This file and all modifications and additions to the pristine
@@ -13,15 +13,15 @@
Name: libopensync-plugin-python-module
BuildRequires: libopensync-devel
URL: http://www.opensync.org
-Version: 0.22
-Release: 30
+Version: 0.32
+Release: 1
Summary: OpenSync module for Python plugins
License: LGPL v2 or later
Group: Productivity/Other
Autoreqprov: on
Source: %{name}-%{version}.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build
-Requires: python-opensync
+Requires: python-opensync libopensync1 >= 0.32
%py_requires
@@ -48,10 +48,9 @@
%install
make DESTDIR=$RPM_BUILD_ROOT install
+mkdir -p $RPM_BUILD_ROOT/%{_libdir}/opensync/python-plugins
# only needed for development
rm $RPM_BUILD_ROOT/%{_libdir}/opensync/plugins/python_module.la
-# only an example plugin
-rm $RPM_BUILD_ROOT/%{_libdir}/opensync/python-plugins/sample.py
%clean
rm -rf $RPM_BUILD_ROOT
@@ -62,11 +61,14 @@
%files
%defattr(-, root, root)
+%doc AUTHORS COPYING INSTALL
%{_libdir}/opensync/plugins/python_module.so
-%{_libdir}/opensync/python-plugins/
-%doc AUTHORS COPYING INSTALL ChangeLog NEWS README
+%{_libdir}/opensync/python-plugins
%changelog
+* Thu Aug 02 2007 - cstender@suse.de
+- updated to version 0.32
+ o ported to the new OpenSync 0.3x API
* Wed Jul 18 2007 - dmueller@suse.de
- use py_requires macro for more accurate requires
* Thu Mar 29 2007 - cstender@suse.de
++++++ libopensync-plugin-python-module-0.22.tar.bz2 -> libopensync-plugin-python-module-0.32.tar.bz2 ++++++
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/AUTHORS new/libopensync-plugin-python-module-0.32/AUTHORS
--- old/libopensync-plugin-python-module-0.22/AUTHORS 2005-01-02 14:56:30.000000000 +0100
+++ new/libopensync-plugin-python-module-0.32/AUTHORS 2007-02-20 11:50:36.000000000 +0100
@@ -1 +1,2 @@
Eduardo Pereira Habkost
+Andrew Baumann
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/configure.in new/libopensync-plugin-python-module-0.32/configure.in
--- old/libopensync-plugin-python-module-0.22/configure.in 2007-03-25 18:17:30.000000000 +0200
+++ new/libopensync-plugin-python-module-0.32/configure.in 2007-08-02 12:21:29.000000000 +0200
@@ -1,7 +1,7 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.58)
-AC_INIT([OpenSync Python Module], 0.22, [], [libopensync-plugin-python])
+AC_INIT([OpenSync Python Module], 0.32, [], [libopensync-plugin-python])
AM_INIT_AUTOMAKE(foreign)
AC_CONFIG_SRCDIR(src/python_module.c)
AM_CONFIG_HEADER(config.h)
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/Makefile.am new/libopensync-plugin-python-module-0.32/Makefile.am
--- old/libopensync-plugin-python-module-0.22/Makefile.am 2006-09-08 16:32:11.000000000 +0200
+++ new/libopensync-plugin-python-module-0.32/Makefile.am 2007-02-19 09:48:32.000000000 +0100
@@ -3,8 +3,6 @@
INCLUDES = -I$(top_srcdir)
-EXTRA_DIST = src/sample.py
-
OPENSYNC_LIBS=-lopensync
plugin_LTLIBRARIES = python_module.la
@@ -15,4 +13,6 @@
python_module_la_CPPFLAGS = @PYTHON_CFLAGS@ -DOPENSYNC_PYTHONPLG_DIR=\"$(pythonplgdir)\"
python_module_la_LIBADD = @PYTHON_LIBS@
-pythonplg_DATA = src/sample.py
+# uncomment these to install the sample plugin
+#EXTRA_DIST = src/sample.py
+#pythonplg_DATA = src/sample.py
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/setup.py new/libopensync-plugin-python-module-0.32/setup.py
--- old/libopensync-plugin-python-module-0.22/setup.py 2005-04-11 17:41:43.000000000 +0200
+++ new/libopensync-plugin-python-module-0.32/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-from distutils.core import setup, Extension
-setup(name='opensync',
- version='0.3',
- ext_modules=[
- Extension(
- 'opensync', ['src/opensync.c', 'src/pywrap.c'],
- libraries=['opensync']
- )
- ],
-)
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/src/python_module.c new/libopensync-plugin-python-module-0.32/src/python_module.c
--- old/libopensync-plugin-python-module-0.22/src/python_module.c 2006-08-25 13:57:03.000000000 +0200
+++ new/libopensync-plugin-python-module-0.32/src/python_module.c 2007-07-03 09:39:37.000000000 +0200
@@ -1,5 +1,6 @@
/* Python module for OpenSync
* Copyright (C) 2005 Eduardo Pereira Habkost
+ * Copyright (C) 2007 Andrew Baumann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,22 +17,27 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @author Eduardo Pereira Habkost
+ * @author Andrew Baumann
*
* Additional changes by Armin Bauer
*/
#include
-
#include
+#include
+#include
#include
#include
#include "config.h"
+/* change this define for python exception output on stderr */
+//#define PYERR_CLEAR() PyErr_Clear()
+#define PYERR_CLEAR() PyErr_Print()
+
typedef struct MemberData {
- PyThreadState *interp_thread;
PyObject *osync_module;
PyObject *module;
- PyObject *object;
+ GSList *sinks;
} MemberData;
static PyObject *pm_load_opensync(OSyncError **error)
@@ -39,49 +45,26 @@
PyObject *osync_module = PyImport_ImportModule("opensync");
if (!osync_module) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load OpenSync module");
- //PyErr_Print();
+ PYERR_CLEAR();
return NULL;
}
return osync_module;
}
-static PyObject *pm_load_script(const char *filename, OSyncError **error)
-{
- FILE *fp = fopen(filename, "r");
- if (!fp) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to open file %s", filename);
- return NULL;
- }
-
- if (PyRun_SimpleFile(fp, filename) == -1) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't run module from file %s", filename);
- PyErr_Print();
- return NULL;
- }
-
- PyObject *module = PyImport_AddModule("__main__");
- if (!module) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module from file %s", filename);
- PyErr_Print();
- return NULL;
- }
- return module;
-}
-
static PyObject *pm_make_change(PyObject *osync_module, OSyncChange *change, OSyncError **error)
{
PyObject *pychg_cobject = PyCObject_FromVoidPtr(change, NULL);
if (!pychg_cobject) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pychg cobject");
- PyErr_Print();
+ PYERR_CLEAR();
return NULL;
}
- PyObject *pychg = PyObject_CallMethod(osync_module, "OSyncChange", "O", pychg_cobject);
+ PyObject *pychg = PyObject_CallMethod(osync_module, "Change", "O", pychg_cobject);
+ Py_DECREF(pychg_cobject);
if (!pychg) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncChange");
- PyErr_Print();
- Py_XDECREF(pychg_cobject);
+ PYERR_CLEAR();
return NULL;
}
return pychg;
@@ -92,216 +75,359 @@
PyObject *pyctx_cobject = PyCObject_FromVoidPtr(ctx, NULL);
if (!pyctx_cobject) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyctx cobject");
- PyErr_Print();
+ PYERR_CLEAR();
return NULL;
}
- PyObject *pyctx = PyObject_CallMethod(osync_module, "OSyncContext", "O", pyctx_cobject);
+ PyObject *pyctx = PyObject_CallMethod(osync_module, "Context", "O", pyctx_cobject);
+ Py_DECREF(pyctx_cobject);
if (!pyctx) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncContext");
- PyErr_Print();
- Py_XDECREF(pyctx_cobject);
+ PYERR_CLEAR();
return NULL;
}
return pyctx;
}
-static PyObject *pm_make_member(PyObject *osync_module, OSyncMember *member, OSyncError **error)
+static PyObject *pm_make_info(PyObject *osync_module, OSyncPluginInfo *info, OSyncError **error)
{
- PyObject *pymember_cobject = PyCObject_FromVoidPtr(member, NULL);
- if (!pymember_cobject) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pymember cobject");
- PyErr_Print();
+ PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL);
+ if (!pyinfo_cobject) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject");
+ PYERR_CLEAR();
return NULL;
}
- PyObject *pymember = PyObject_CallMethod(osync_module, "OSyncMember", "O", pymember_cobject);
- if (!pymember) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncMember");
- PyErr_Print();
- Py_XDECREF(pymember_cobject);
+ PyObject *pyinfo = PyObject_CallMethod(osync_module, "PluginInfo", "O", pyinfo_cobject);
+ Py_DECREF(pyinfo_cobject);
+ if (!pyinfo) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPluginInfo");
+ PYERR_CLEAR();
return NULL;
}
- return pymember;
+ return pyinfo;
}
-/** Calls the method initialize function
- *
- * The python initialize() function should return an object that
- * has the other plugin methods (get_changeinfo, commit, etc.)
- */
-static void *pm_initialize(OSyncMember *member, OSyncError **error)
+/* convert a python exception to an OSyncError containing the traceback of the exception */
+static void pm_pyexcept_to_oserror(PyObject *pytype, PyObject *pyvalue, PyObject *pytraceback, OSyncError **error)
{
- const char *name = osync_member_get_plugindata(member);
- if (!name) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "No script name was set");
- return NULL;
+ const char *errmsg = NULL;
+ PyObject *tracebackmod = NULL, *stringmod = NULL;
+ PyObject *pystrs = NULL, *pystr = NULL;
+
+ tracebackmod = PyImport_ImportModule("traceback");
+ if (!tracebackmod) {
+ errmsg = "import traceback";
+ goto error;
}
- MemberData *data = g_malloc(sizeof(MemberData));
-
- data->interp_thread = Py_NewInterpreter();
- if (!data->interp_thread) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize python sub interpreter");
- goto error_free_data;
+ pystrs = PyObject_CallMethod(tracebackmod, "format_exception", "OOO", pytype, pyvalue, pytraceback);
+ if (!pystrs) {
+ errmsg = "traceback.format_exception";
+ goto error;
}
-
- if (!(data->osync_module = pm_load_opensync(error)))
- goto error_free_interp;
-
- if (!(data->module = pm_load_script(name, error)))
- goto error_free_interp;
-
- PyObject *pymember = pm_make_member(data->osync_module, member, error);
- if (!pymember)
- goto error_unload_module;
-
- data->object = PyObject_CallMethod(data->module, "initialize", "O", pymember);
- if (!data->object) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize module");
- PyErr_Print();
- goto error_unload_module;
+
+ stringmod = PyImport_ImportModule("string");
+ if (!stringmod) {
+ errmsg = "import string";
+ goto error;
}
- PyEval_ReleaseThread(data->interp_thread);
+ pystr = PyObject_CallMethod(stringmod, "join", "Os", pystrs, "");
+ if (!pystr) {
+ errmsg = "string.join";
+ goto error;
+ }
- return data;
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "%s", PyString_AsString(pystr));
-error_unload_module:
- Py_DECREF(data->module);
-error_free_interp:
- Py_EndInterpreter(data->interp_thread);
-error_free_data:
- free(data);
- return NULL;
-}
+error:
+ Py_XDECREF(tracebackmod);
+ Py_XDECREF(stringmod);
+ Py_XDECREF(pystrs);
+ Py_XDECREF(pystr);
-static void pm_finalize(void *data)
-{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
- MemberData *mydata = data;
- PyEval_AcquireThread(mydata->interp_thread);
- {
- PyObject *ret = PyObject_CallMethod(mydata->object, "finalize", NULL);
- if (!ret) {
- osync_trace(TRACE_INTERNAL, "Error during finalize()");
- PyErr_Print();
- } else
- Py_DECREF(ret);
+ if (errmsg) {
+ PYERR_CLEAR();
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "pm_pyexcept_to_oserror: failed to report error: exception in %s", errmsg);
}
- Py_DECREF(mydata->object);
- Py_DECREF(mydata->module);
- Py_EndInterpreter(mydata->interp_thread);
-
- free(mydata);
- osync_trace(TRACE_EXIT, "%s", __func__);
}
-/** Call a python method
+/** Call a python method, report any exception it raises as an error, if no exception was raised report success
*
* Methods called using this function can
* have one of these formats:
*
- * - function(context)
- * - function(context, change)
+ * - function(info, context)
+ * - function(info, context, change)
*/
-static osync_bool pm_call_module_method(OSyncContext *ctx, OSyncChange *chg, char *name, OSyncError **error)
+static osync_bool pm_call_module_method(MemberData *data, char *name, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *chg)
{
- osync_trace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, ctx, chg, name, error);
- PyObject *pycontext = NULL;
+ osync_trace(TRACE_ENTRY, "%s(%s, %p, %p, %p)", __func__, name, info, ctx, chg);
PyObject *ret = NULL;
+ OSyncError *error = NULL;
+ osync_bool report_error = TRUE;
+
+ PyGILState_STATE pystate = PyGILState_Ensure();
- MemberData *data = osync_context_get_plugin_data(ctx);
- PyEval_AcquireThread(data->interp_thread);
+ PyObject *pyinfo = pm_make_info(data->osync_module, info, &error);
+ if (!pyinfo)
+ goto error;
- pycontext = pm_make_context(data->osync_module, ctx, error);
+ PyObject *pycontext = pm_make_context(data->osync_module, ctx, &error);
if (!pycontext) {
- PyEval_ReleaseThread(data->interp_thread);
- osync_context_report_osyncerror(ctx, error);
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+ Py_DECREF(pyinfo);
+ goto error;
}
+ OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
+ PyObject *sink_pyobject = osync_objtype_sink_get_userdata(sink);
+
if (chg) {
- PyObject *pychange = pm_make_change(data->osync_module, chg, error);
+ PyObject *pychange = pm_make_change(data->osync_module, chg, &error);
if (!pychange) {
- PyEval_ReleaseThread(data->interp_thread);
- osync_context_report_osyncerror(ctx, error);
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+ Py_DECREF(pyinfo);
+ Py_DECREF(pycontext);
+ goto error;
}
- ret = PyObject_CallMethod(data->object, name, "OO", pycontext, pychange);
+ ret = PyObject_CallMethod(sink_pyobject, name, "OOO", pyinfo, pycontext, pychange);
- Py_XDECREF(pychange);
+ Py_DECREF(pychange);
} else {
- ret = PyObject_CallMethod(data->object, name, "O", pycontext);
+ ret = PyObject_CallMethod(sink_pyobject, name, "OO", pyinfo, pycontext);
}
-
- if (!ret) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Error during %s() method", name);
- PyErr_Print();
- PyEval_ReleaseThread(data->interp_thread);
- osync_context_report_osyncerror(ctx, error);
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+
+ Py_DECREF(pyinfo);
+
+ if (ret) {
+ Py_DECREF(pycontext);
+ Py_DECREF(ret);
+ PyGILState_Release(pystate);
+ osync_context_report_success(ctx);
+ osync_trace(TRACE_EXIT, "%s", __func__);
+ return TRUE;
+ }
+
+ /* an exception occurred. get the python exception data */
+ PyObject *pytype, *pyvalue, *pytraceback;
+ PyErr_Fetch(&pytype, &pyvalue, &pytraceback);
+
+ PyObject *osyncerror = NULL;
+ osyncerror = PyObject_GetAttrString(data->osync_module, "Error");
+ if (!osyncerror) {
+ PYERR_CLEAR();
+ osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed to get OSyncError class object");
+ goto out;
+ }
+
+ if (PyErr_GivenExceptionMatches(pytype, osyncerror)) {
+ /* if it's an OSyncError, just report that up on the context object */
+ PyObject *obj = PyObject_CallMethod(pyvalue, "report", "O", pycontext);
+ if (!obj) {
+ PYERR_CLEAR();
+ osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting OSyncError");
+ goto out;
+ }
+
+ Py_DECREF(obj);
+ osync_error_set(&error, OSYNC_ERROR_GENERIC, "Reported OSyncError");
+ report_error = FALSE;
+ } else if (PyErr_GivenExceptionMatches(pytype, PyExc_IOError)
+ || PyErr_GivenExceptionMatches(pytype, PyExc_OSError)) {
+ /* for IOError or OSError, we just report the &error message */
+ PyObject *pystr = PyObject_Str(pyvalue);
+ if (!pystr) {
+ PYERR_CLEAR();
+ osync_error_set(&error, OSYNC_ERROR_GENERIC, "Failed reporting IOError/OSError");
+ goto out;
+ }
+
+ osync_error_set(&error, OSYNC_ERROR_IO_ERROR, "%s", PyString_AsString(pystr));
+ Py_DECREF(pystr);
+ } else {
+ /* for other exceptions, we report a full traceback */
+ pm_pyexcept_to_oserror(pytype, pyvalue, pytraceback, &error);
}
- Py_XDECREF(ret);
- PyEval_ReleaseThread(data->interp_thread);
-
- osync_trace(TRACE_EXIT, "%s", __func__);
- return TRUE;
+out:
+ Py_DECREF(pycontext);
+ Py_XDECREF(pytype);
+ Py_XDECREF(pyvalue);
+ Py_XDECREF(pytraceback);
+ Py_XDECREF(osyncerror);
+
+error:
+ PyGILState_Release(pystate);
+ if (report_error)
+ osync_context_report_osyncerror(ctx, error);
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
+ return FALSE;
}
-static void pm_connect(OSyncContext *ctx)
+static void pm_connect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, NULL, "connect", &error);
- osync_trace(TRACE_EXIT, "%s", __func__);
+ pm_call_module_method(data, "connect", info, ctx, NULL);
}
+static void pm_disconnect(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
+{
+ pm_call_module_method(data, "disconnect", info, ctx, NULL);
+}
-static void pm_get_changeinfo(OSyncContext *ctx)
+static void pm_get_changes(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, NULL, "get_changeinfo", &error);
- osync_trace(TRACE_EXIT, "%s", __func__);
+ pm_call_module_method(data, "get_changes", info, ctx, NULL);
}
-static osync_bool pm_access(OSyncContext *ctx, OSyncChange *change)
+static void pm_commit(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change)
{
- osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, change, "access", &error);
- osync_trace(TRACE_EXIT, "%s", __func__);
- return TRUE;
+ pm_call_module_method(data, "commit", info, ctx, change);
}
-static osync_bool pm_commit_change(OSyncContext *ctx, OSyncChange *change)
+static void pm_committed_all(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
{
- osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, change, "commit_change", &error);
+ pm_call_module_method(data, "committed_all", info, ctx, NULL);
+}
+
+static osync_bool pm_write(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change)
+{
+ return pm_call_module_method(data, "write", info, ctx, change);
+}
+
+static osync_bool pm_read(void *data, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change)
+{
+ return pm_call_module_method(data, "read", info, ctx, change);
+}
+
+static void pm_sync_done(void *data, OSyncPluginInfo *info, OSyncContext *ctx)
+{
+ pm_call_module_method(data, "sync_done", info, ctx, NULL);
+}
+
+static OSyncObjTypeSinkFunctions pm_sink_functions = {
+ .connect = pm_connect,
+ .disconnect = pm_disconnect,
+ .get_changes = pm_get_changes,
+ .commit = pm_commit,
+ .write = pm_write,
+ .committed_all = pm_committed_all,
+ .read = pm_read,
+ .batch_commit = NULL, /* not (yet) supported for python plugins */
+ .sync_done = pm_sync_done
+};
+
+/** Calls the method initialize function
+ *
+ * The python initialize() function register one or more sink objects
+ * that have the other plugin methods (get_changeinfo, commit, etc.)
+ */
+static void *pm_initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error)
+{
+ osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, plugin, info, error);
+ MemberData *data = g_malloc0(sizeof(MemberData));
+ char *modulename;
+
+ if (!(modulename = osync_plugin_get_data(plugin)))
+ return NULL;
+ osync_plugin_set_data(plugin, NULL);
+
+ PyGILState_STATE pystate = PyGILState_Ensure();
+
+ if (!(data->module = PyImport_ImportModule(modulename))) {
+ PYERR_CLEAR();
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module %s", modulename);
+ free(modulename);
+ goto error;
+ }
+
+ free(modulename);
+
+ if (!(data->osync_module = pm_load_opensync(error)))
+ goto error;
+
+ PyObject *pyinfo = pm_make_info(data->osync_module, info, error);
+ if (!pyinfo)
+ goto error;
+
+ PyObject *ret = PyObject_CallMethod(data->module, "initialize", "O", pyinfo);
+ Py_DECREF(pyinfo);
+ if (!ret) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize module");
+ PYERR_CLEAR();
+ goto error;
+ }
+ Py_DECREF(ret);
+
+ /* loop through all objtype sinks, set up function pointers */
+ int n, max = osync_plugin_info_num_objtypes(info);
+ for (n = 0; n < max; n++) {
+ OSyncObjTypeSink *sink = osync_plugin_info_nth_objtype(info, n);
+ PyObject *sinkobj = osync_objtype_sink_get_userdata(sink);
+ osync_objtype_sink_set_functions(sink, pm_sink_functions, sinkobj);
+ Py_INCREF(sinkobj);
+ data->sinks = g_slist_prepend(data->sinks, sinkobj);
+ }
+
+ PyGILState_Release(pystate);
osync_trace(TRACE_EXIT, "%s", __func__);
- return TRUE;
+ return data;
+
+error:
+ Py_XDECREF(data->module);
+ Py_XDECREF(data->osync_module);
+ PyGILState_Release(pystate);
+ free(data);
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ return NULL;
}
-static void pm_sync_done(OSyncContext *ctx)
+static osync_bool pm_discover(void *data_in, OSyncPluginInfo *info, OSyncError **error)
{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, NULL, "sync_done", &error);
+ osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data_in, info, error);
+
+ MemberData *data = data_in;
+
+ PyGILState_STATE pystate = PyGILState_Ensure();
+
+ PyObject *pyinfo = pm_make_info(data->osync_module, info, error);
+ if (!pyinfo)
+ goto error;
+
+ PyObject *ret = PyObject_CallMethod(data->module, "discover", "O", pyinfo);
+ Py_DECREF(pyinfo);
+ if (!ret)
+ goto error;
+
+ Py_DECREF(ret);
+ PyGILState_Release(pystate);
osync_trace(TRACE_EXIT, "%s", __func__);
+ return TRUE;
+
+error:
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't call discover method");
+ PYERR_CLEAR();
+ PyGILState_Release(pystate);
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ return FALSE;
}
-static void pm_disconnect(OSyncContext *ctx)
+static void pm_finalize(void *data)
{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
- OSyncError *error = NULL;
- pm_call_module_method(ctx, NULL, "disconnect", &error);
+ osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data);
+ MemberData *mydata = data;
+ PyGILState_STATE pystate = PyGILState_Ensure();
+
+ /* free all sink objects */
+ while (mydata->sinks) {
+ Py_DECREF((PyObject *)mydata->sinks->data);
+ mydata->sinks = g_slist_delete_link(mydata->sinks, mydata->sinks);
+ }
+
+ Py_DECREF(mydata->module);
+ Py_DECREF(mydata->osync_module);
+ free(mydata);
+ PyGILState_Release(pystate);
osync_trace(TRACE_EXIT, "%s", __func__);
}
@@ -312,84 +438,86 @@
* plugin information on another place (including
* accepted objtypes/formats info)
*/
-static osync_bool register_plugin(OSyncEnv *env, PyObject *osync_module,
- const char *filename, OSyncError **error)
+static osync_bool register_plugin(OSyncPluginEnv *env, PyObject *osync_module,
+ char *modulename, OSyncError **error)
{
- osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, filename, error);
+ osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, env, modulename, error);
- PyObject *module = pm_load_script(filename, error);
- if (!module) {
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ PyObject *module = NULL, *pyplugin_cobj = NULL, *pyplugin = NULL;
+
+ OSyncPlugin *plugin = osync_plugin_new(error);
+ if (!plugin)
return FALSE;
+
+ module = PyImport_ImportModule(modulename);
+ if (!module) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't load module %s", modulename);
+ goto error;
}
-
- OSyncPluginInfo *info = osync_plugin_new_info(env);
- info->functions.initialize = pm_initialize;
- info->functions.connect = pm_connect;
- info->functions.get_changeinfo = pm_get_changeinfo;
- info->functions.sync_done = pm_sync_done;
- info->functions.disconnect = pm_disconnect;
- info->functions.finalize = pm_finalize;
-
- info->plugin_data = g_strdup(filename);
-
- PyObject *pyinfo_cobject = PyCObject_FromVoidPtr(info, NULL);
- if (!pyinfo_cobject) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyinfo cobject");
- PyErr_Print();
- PyErr_Clear();
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+
+ pyplugin_cobj = PyCObject_FromVoidPtr(plugin, NULL);
+ if (!pyplugin_cobj) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldnt make pyplugin cobject");
+ goto error;
}
- PyObject *pyinfo = PyObject_CallMethod(osync_module, "OSyncPluginInfo", "O", pyinfo_cobject);
- if (!pyinfo) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPluginInfo");
- PyErr_Print();
- PyErr_Clear();
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+ pyplugin = PyObject_CallMethod(osync_module, "Plugin", "O", pyplugin_cobj);
+ if (!pyplugin) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot create Python OSyncPlugin");
+ goto error;
}
- if (!PyObject_CallMethod(module, "get_info", "O", pyinfo)) {
- osync_error_set(error, OSYNC_ERROR_GENERIC, "Error calling get_info");
- PyErr_Print();
- PyErr_Clear();
- osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
- return FALSE;
+ PyObject *pyret = PyObject_CallMethod(module, "get_sync_info", "O", pyplugin);
+ if (!pyret) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Error calling get_sync_info");
+ goto error;
}
-
- if (!info->name) {
- osync_debug("python", 1, "The plugin didn't set its name!");
+ Py_DECREF(pyret);
+
+ if (!osync_plugin_get_name(plugin)) {
+ osync_trace(TRACE_INTERNAL, "%s: the plugin %s didn't set its name", __func__, modulename);
}
- osync_plugin_set_access_objformat(info, NULL, NULL, pm_access);
- osync_plugin_set_commit_objformat(info, NULL, NULL, pm_commit_change);
+ osync_plugin_set_initialize(plugin, pm_initialize);
+ osync_plugin_set_discover(plugin, pm_discover);
+ osync_plugin_set_finalize(plugin, pm_finalize);
+ osync_plugin_set_data(plugin, g_strdup(modulename));
+ osync_plugin_env_register_plugin(env, plugin);
+ osync_plugin_unref(plugin);
osync_trace(TRACE_EXIT, "%s", __func__);
return TRUE;
+
+error:
+ PYERR_CLEAR();
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ Py_XDECREF(module);
+ Py_XDECREF(pyplugin_cobj);
+ Py_XDECREF(pyplugin);
+ return FALSE;
}
-static osync_bool scan_for_plugins(OSyncEnv *env, PyObject *osync_module)
+static osync_bool scan_for_plugins(OSyncPluginEnv *env, PyObject *osync_module, OSyncError **error)
{
- osync_trace(TRACE_ENTRY, "%s(%p)", __func__, env);
+ osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, env, osync_module);
- char *path = OPENSYNC_PYTHONPLG_DIR;
GError *gerror = NULL;
- GDir *dir = g_dir_open(path, 0, &gerror);
+ GDir *dir = g_dir_open(OPENSYNC_PYTHONPLG_DIR, 0, &gerror);
if (!dir) {
- osync_trace(TRACE_EXIT_ERROR, "%s: Unable to open directory %s: %s", __func__, path, gerror ? gerror->message : "None");
+ osync_trace(TRACE_EXIT_ERROR, "%s: Unable to open directory %s: %s", __func__, OPENSYNC_PYTHONPLG_DIR, gerror ? gerror->message : "None");
return FALSE;
}
- const char *de = NULL;
- while ((de = g_dir_read_name(dir))) {
- char *filename = g_build_filename(path, de, NULL);
- OSyncError *error = NULL;
- if (!register_plugin(env, osync_module, filename, &error))
- osync_debug("python", 1, "Couldn't register plugin \"%s\": %s", filename, osync_error_print(&error));
-
- g_free(filename);
+ /* scan through the plugin directory looking for python modules (*.py)
+ * for each matching file, drop the .py extension to get the module name, and try to register its plugin */
+ const char *filename = NULL;
+ while ((filename = g_dir_read_name(dir))) {
+ if (g_str_has_suffix(filename, ".py")) {
+ char *modulename = g_strndup(filename, strlen(filename) - 3);
+ if (!register_plugin(env, osync_module, modulename, error))
+ osync_trace(TRACE_INTERNAL, "Couldn't register python plugin \"%s\": %s", filename, osync_error_print(error));
+ g_free(modulename);
+ }
}
g_dir_close(dir);
@@ -397,21 +525,113 @@
return TRUE;
}
-void get_info(OSyncEnv *env)
+/* set python search path to look in our module directory first */
+static osync_bool set_search_path(OSyncError **error)
+{
+ PyObject *sys_module = PyImport_ImportModule("sys");
+ if (!sys_module) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't import sys module");
+ PYERR_CLEAR();
+ return FALSE;
+ }
+
+ PyObject *path = PyObject_GetAttrString(sys_module, "path");
+ if (!path) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "sys module has no path attribute?");
+ PYERR_CLEAR();
+ Py_DECREF(sys_module);
+ return FALSE;
+ }
+
+ if (!PyList_Check(path)) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "sys.path is not a list?");
+ Py_DECREF(sys_module);
+ Py_DECREF(path);
+ return FALSE;
+ }
+
+ PyObject *plugindir = Py_BuildValue("s", OPENSYNC_PYTHONPLG_DIR);
+ if (!plugindir) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Error constructing plugindir string for sys.path");
+ PYERR_CLEAR();
+ Py_DECREF(sys_module);
+ Py_DECREF(path);
+ return FALSE;
+ }
+
+ int r = PySequence_Contains(path, plugindir);
+ if (r < 0) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Error checking for 'plugindir in sys.path'");
+ PYERR_CLEAR();
+ Py_DECREF(sys_module);
+ Py_DECREF(path);
+ Py_DECREF(plugindir);
+ return FALSE;
+ }
+
+ if (r == 0 && PyList_Insert(path, 0, plugindir) != 0) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Error inserting plugin directory into sys.path");
+ PYERR_CLEAR();
+ Py_DECREF(sys_module);
+ Py_DECREF(path);
+ Py_DECREF(plugindir);
+ return FALSE;
+ }
+
+ Py_DECREF(sys_module);
+ Py_DECREF(path);
+ Py_DECREF(plugindir);
+
+ return TRUE;
+}
+
+osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
{
- /* Python initialization */
- struct sigaction old_sigint;
+ osync_trace(TRACE_ENTRY, "%s(%p)", __func__, env);
+
+ /* Because OpenSync likes to call this function multiple times in
+ * different threads, and because we may be sharing the python
+ * interpreter with other code, we have to:
+ * * init python only once
+ * * acquire the Python lock before making any API calls
+ */
+
+ if (!Py_IsInitialized()) {
+ /* We're the first user of python in this process. Initialise
+ * it, enable threading, and release the lock that will be
+ * re-acquired by the PyGILState_Ensure() call below. */
+ Py_InitializeEx(0);
+ PyEval_InitThreads();
+ PyEval_ReleaseLock();
+ } else if (!PyEval_ThreadsInitialized()) {
+ /* Python has been initialised, but threads are not. */
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "The Python interpreter in this process has been initialised without threading support.");
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ return FALSE;
+ }
- /* Hack to make python not overwrite SIGINT */
- sigaction(SIGINT, NULL, &old_sigint); /* Save old handler */
- Py_Initialize();
- sigaction(SIGINT, &old_sigint, NULL); /* Restore it */
- PyEval_InitThreads();
+ PyGILState_STATE pystate = PyGILState_Ensure();
+ osync_bool ret = FALSE;
- OSyncError *error = NULL;
- PyObject *osync_module = pm_load_opensync(&error);
+ if (!set_search_path(error))
+ goto out;
+
+ /* import opensync module */
+ PyObject *osync_module = pm_load_opensync(error);
if (!osync_module)
- return;
+ goto out;
+
+ ret = scan_for_plugins(env, osync_module, error);
+ Py_DECREF(osync_module);
+
+out:
+ PyGILState_Release(pystate);
- scan_for_plugins(env, osync_module);
+ osync_trace(ret ? TRACE_EXIT : TRACE_EXIT_ERROR, "%s", __func__);
+ return ret;
+}
+
+int get_version(void)
+{
+ return 1; /* opensync plugin API version we expect */
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/libopensync-plugin-python-module-0.22/src/sample.py new/libopensync-plugin-python-module-0.32/src/sample.py
--- old/libopensync-plugin-python-module-0.22/src/sample.py 2006-08-25 13:57:03.000000000 +0200
+++ new/libopensync-plugin-python-module-0.32/src/sample.py 2007-02-21 01:05:21.000000000 +0100
@@ -1,47 +1,65 @@
import opensync
-class SyncClass:
- def __init__(self, member):
- self.__member = member
-
- def connect(self, ctx):
- print "Connect called!!"
- ctx.report_success()
-
- def get_changeinfo(self, ctx):
- print "get_changeinfo called!!"
- if self.__member.get_slow_sync("data"):
- print "Slow-sync requested"
- change = opensync.OSyncChange()
- change.uid = "testuid"
- change.data = "testdata"
- change.format = "plain"
- change.objtype = "data"
- change.changetype = opensync.CHANGE_ADDED
- change.report(ctx)
- ctx.report_success()
- print "done with get_changeinfo"
+class DummySink(opensync.ObjTypeSinkCallbacks):
+ def __init__(self, objtype):
+ opensync.ObjTypeSinkCallbacks.__init__(self, objtype)
+ self.sink.add_objformat("file")
+
+ def connect(self, info, ctx):
+ print "Connect called!"
+
+ def get_changes(self, info, ctx):
+ print "get_changes called!"
+ #if self.__member.get_slow_sync("data"):
+ #print "Slow-sync requested"
+ #change = opensync.Change()
+ #change.uid = "testuid"
+ #change.data = "testdata"
+ #change.format = "plain"
+ #change.objtype = "data"
+ #change.changetype = opensync.CHANGE_ADDED
+ #change.report(ctx)
+ #print "done with get_changeinfo"
- def commit_change(self, ctx, chg):
- print "commit called!!"
- print "Opensync wants me to write data with size " + len(chg.data)
- ctx.report_success()
+ def commit(self, info, ctx, chg):
+ print "commit called!"
+ print "Opensync wants me to write data for UID", chg.uid
+
+ def committed_all(self, info, ctx):
+ print "committed_all called!"
+
+ def read(self, info, ctx, chg):
+ print "read called!"
+ print "OpenSync wants me to read the data for UID", chg.uid
+
+ def write(self, info, ctx, chg):
+ print "write called!"
+ print "Opensync wants me to write data for UID", chg.uid
- def disconnect(self, ctx):
+ def disconnect(self, info, ctx):
print "disconnect called!"
- ctx.report_success()
- def sync_done(self, ctx):
+ def sync_done(self, info, ctx):
print "sync_done called!"
- ctx.report_success()
-
- def finalize(self):
- print "finalize called!"
-
-def initialize(member):
- return SyncClass(member)
-def get_info(info):
- info.accept_objtype("data")
- info.accept_objformat("data", "plain")
- info.name = "testmodule"
\ No newline at end of file
+def initialize(info):
+ print "initialize called!"
+ print "My config is:", info.config
+ print "Adding new sink"
+ info.add_objtype(DummySink("data").sink)
+ print "Done"
+
+def discover(info):
+ print "discover called!"
+ for sink in info.objtypes:
+ print "setting sink available:", sink
+ sink.available = True
+ info.version = opensync.Version()
+ info.version.plugin = "python-sample"
+ print "done"
+
+def get_sync_info(plugin):
+ plugin.name = "python-sample"
+ plugin.longname = "Sample sync plugin for the Python module"
+ plugin.description = "This plugin only shows what must be implemented."
+ plugin.config_type = opensync.PLUGIN_NO_CONFIGURATION
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...
---------------------------------------------------------------------
To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org
For additional commands, e-mail: opensuse-commit+help@opensuse.org