r17263 - gnucash/trunk - Python bindings for the gnucash API.

Christian Stimming cstim at cvs.gnucash.org
Mon Jul 7 15:18:26 EDT 2008


Author: cstim
Date: 2008-07-07 15:18:26 -0400 (Mon, 07 Jul 2008)
New Revision: 17263
Trac: http://svn.gnucash.org/trac/changeset/17263

Added:
   gnucash/trunk/macros/ac_python_devel.m4
   gnucash/trunk/src/optional/python-bindings/
   gnucash/trunk/src/optional/python-bindings/Makefile.am
   gnucash/trunk/src/optional/python-bindings/__init__.py
   gnucash/trunk/src/optional/python-bindings/example_scripts/
   gnucash/trunk/src/optional/python-bindings/example_scripts/simple_book.py
   gnucash/trunk/src/optional/python-bindings/example_scripts/simple_session.py
   gnucash/trunk/src/optional/python-bindings/example_scripts/simple_test.py
   gnucash/trunk/src/optional/python-bindings/function_class.py
   gnucash/trunk/src/optional/python-bindings/glib.i
   gnucash/trunk/src/optional/python-bindings/gnucash_core.i
   gnucash/trunk/src/optional/python-bindings/gnucash_core.py
   gnucash/trunk/src/optional/python-bindings/timespec.i
Modified:
   gnucash/trunk/AUTHORS
   gnucash/trunk/configure.in
   gnucash/trunk/src/optional/Makefile.am
Log:
Python bindings for the gnucash API.

Submitted by Mark Jenkins on 2008-03-25 to gnucash-devel.


Modified: gnucash/trunk/AUTHORS
===================================================================
--- gnucash/trunk/AUTHORS	2008-07-07 19:18:14 UTC (rev 17262)
+++ gnucash/trunk/AUTHORS	2008-07-07 19:18:26 UTC (rev 17263)
@@ -150,6 +150,7 @@
 Dave Freese <DFreese at osc.uscg.mil> for leap-year fix
 Todd T. Fries <todd at flare.fries.net> OpenBSD fix
 John Goerzen <jgoerzen at complete.org> file i/o fix for 64-bit architectures
+Jeff Green <jmgreen7 at gmail.com> Python bindings
 Hans de Graaff <hans at degraaff.org> XML patches
 Daniel Hagerty <hag at linnaean.org> patch to balance sheet report
 Mitsuo Hamada <mhamada at redhat.com> messages Japanese translations
@@ -166,6 +167,7 @@
 Edward J. Huff <ejhuff at huff20may77.us> Date handling in reports, quarterly option
 Tomokazu Iwashita <iwashita at center.nitech.ac.jp> Japanese translation of xea
 David Jafferian <david.jafferian at east.sun.com> Delete account query code.
+Mark Jenkins <mark at parit.ca> Python bindings
 Miquel Jordana Vilamitjana <jjvmjv at mundomail.net> Spanish translation of manual
 Prakash Kailasa <PrakashK at bigfoot.com> for gnome build fixes
 Alexey Kakunin <small at arcadia.spb.ru> quickfill patch for Cyrillic

Modified: gnucash/trunk/configure.in
===================================================================
--- gnucash/trunk/configure.in	2008-07-07 19:18:14 UTC (rev 17262)
+++ gnucash/trunk/configure.in	2008-07-07 19:18:26 UTC (rev 17263)
@@ -1329,6 +1329,28 @@
 fi
 AC_SUBST(LC_MESSAGES_ENUM)
 
+###--------------------------------------------------------
+### Make Python bindings optional
+###--------------------------------------------------------
+enable_python=false
+
+AC_ARG_ENABLE(python-bindings,
+  [  --enable-python-bindings     enable python bindings],
+  [case "${enableval}" in
+        yes) enable_python=true ;;
+        no) enable_python=false ;;
+        *) enable_python=true ;;
+        esac]
+  )
+if test x${enable_python} = "xtrue"
+then
+  PYTHON_DIR=python-bindings
+  AM_PATH_PYTHON(2.4)
+  AC_PYTHON_DEVEL(>= '2.4')
+  SWIG_PYTHON
+fi
+AC_SUBST(PYTHON_DIR)
+
 ###-------------------------------------------------------------------------
 ### Additional compiler warnings (or not) if we're running GCC
 ###-------------------------------------------------------------------------
@@ -1520,6 +1542,7 @@
           src/import-export/hbci/schemas/Makefile
           src/import-export/hbci/test/Makefile
           src/optional/Makefile
+          src/optional/python-bindings/Makefile
           src/optional/xsl/Makefile
           src/pixmaps/Makefile
           src/quotes/Makefile
@@ -1585,6 +1608,9 @@
 if test x${HBCI_DIR} != x; then
 components="$components hbci"
 fi
+if test x${PYTHON_DIR} != x; then
+components="$components python-bindings"
+fi
 
 AC_MSG_RESULT([
   Options detected/selected

Added: gnucash/trunk/macros/ac_python_devel.m4
===================================================================
--- gnucash/trunk/macros/ac_python_devel.m4	                        (rev 0)
+++ gnucash/trunk/macros/ac_python_devel.m4	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,64 @@
+dnl @synopsis AC_PYTHON_DEVEL
+dnl
+dnl Checks for Python and tries to get the include path to 'Python.h'.
+dnl It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) output
+dnl variable.
+dnl
+dnl @category InstalledPackages
+dnl @author Sebastian Huber <sebastian-huber at web.de>
+dnl @author Alan W. Irwin <irwin at beluga.phys.uvic.ca>
+dnl @author Rafael Laboissiere <laboissiere at psy.mpg.de>
+dnl @author Andrew Collier <colliera at nu.ac.za>
+dnl @version 2004-07-14
+dnl @license GPLWithACException
+
+AC_DEFUN([AC_PYTHON_DEVEL],[
+	#
+	# should allow for checking of python version here...
+	#
+	AC_REQUIRE([AM_PATH_PYTHON])
+
+	# Check for Python include path
+	AC_MSG_CHECKING([for Python include path])
+	python_path=`echo $PYTHON | sed "s,/bin.*$,,"`
+	for i in "$python_path/include/python$PYTHON_VERSION/" "$python_path/include/python/" "$python_path/" ; do
+		python_path=`find $i -type f -name Python.h -print | sed "1q"`
+		if test -n "$python_path" ; then
+			break
+		fi
+	done
+	python_path=`echo $python_path | sed "s,/Python.h$,,"`
+	AC_MSG_RESULT([$python_path])
+	if test -z "$python_path" ; then
+		AC_MSG_ERROR([cannot find Python include path])
+	fi
+	AC_SUBST([PYTHON_CPPFLAGS],[-I$python_path])
+
+	# Check for Python library path
+	AC_MSG_CHECKING([for Python library path])
+	python_path=`echo $PYTHON | sed "s,/bin.*$,,"`
+	for i in "$python_path/lib/python$PYTHON_VERSION/config/" "$python_path/lib/python$PYTHON_VERSION/" "$python_path/lib/python/config/" "$python_path/lib/python/" "$python_path/" ; do
+		python_path=`find $i -type f -name libpython$PYTHON_VERSION.* -print | sed "1q"`
+		if test -n "$python_path" ; then
+			break
+		fi
+	done
+	python_path=`echo $python_path | sed "s,/libpython.*$,,"`
+	AC_MSG_RESULT([$python_path])
+	if test -z "$python_path" ; then
+		AC_MSG_ERROR([cannot find Python library path])
+	fi
+	AC_SUBST([PYTHON_LDFLAGS],["-L$python_path -lpython$PYTHON_VERSION"])
+	#
+	python_site=`echo $python_path | sed "s/config/site-packages/"`
+	AC_SUBST([PYTHON_SITE_PKG],[$python_site])
+	#
+	# libraries which must be linked in when embedding
+	#
+	AC_MSG_CHECKING(python extra libraries)
+	PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+                conf = distutils.sysconfig.get_config_var; \
+                print conf('LOCALMODLIBS')+' '+conf('LIBS')"
+	AC_MSG_RESULT($PYTHON_EXTRA_LIBS)`
+	AC_SUBST(PYTHON_EXTRA_LIBS)
+])

Modified: gnucash/trunk/src/optional/Makefile.am
===================================================================
--- gnucash/trunk/src/optional/Makefile.am	2008-07-07 19:18:14 UTC (rev 17262)
+++ gnucash/trunk/src/optional/Makefile.am	2008-07-07 19:18:26 UTC (rev 17263)
@@ -1 +1,2 @@
-SUBDIRS = xsl
+SUBDIRS = xsl ${PYTHON_DIR}
+DIST_SUBDIRS = xsl python-bindings

Added: gnucash/trunk/src/optional/python-bindings/Makefile.am
===================================================================
--- gnucash/trunk/src/optional/python-bindings/Makefile.am	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/Makefile.am	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,25 @@
+BUILT_SOURCES = gnucash_core.c
+SWIG_SOURCES = gnucash_core.i
+
+pkgpython_PYTHON = __init__.py function_class.py \
+gnucash_core.py gnucash_core_c.py
+
+pkgpyexec_LTLIBRARIES = _gnucash_core_c.la
+_gnucash_core_c_la_SOURCES = $(BUILT_SOURCES) $(SWIG_SOURCES)
+_gnucash_core_c_la_CPPFLAGS = $(PYTHON_CPPFLAGS) \
+                              -I$(top_srcdir)/src $(QOF_CFLAGS) \
+                              $(GLIB_CFLAGS) $(GUILE_INCS) \
+							  -I$(top_srcdir)/src/engine 
+
+# Suppress all warnings for now, but we really only need to -Wno-implicit
+AM_CFLAGS = -w
+
+_gnucash_core_c_la_LDFLAGS = -avoid-version -module
+_gnucash_core_c_la_LIBADD = ${QOF_LIBS} ${GUILE_LIBS} ${GLIB_LIBS} \
+    ${top_builddir}/src/gnc-module/libgnc-module.la \
+	${top_builddir}/src/engine/libgncmod-engine.la
+
+gnucash_core.c : $(SWIG_SOURCES)
+	$(SWIG) $(SWIG_PYTHON_OPT) -Wall -Werror \
+        -I$(top_srcdir)/src -I$(top_srcdir)/src/engine \
+		$(QOF_CFLAGS) -o $@ $<

Added: gnucash/trunk/src/optional/python-bindings/__init__.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/__init__.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/__init__.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,6 @@
+# import all the symbols from gnucash_core, so basic gnucash stuff can be
+# loaded with:
+# >>> from gnucash import thingy
+# instead of
+# >>> from gnucash.gnucash_core import thingy
+from gnucash_core import *

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/simple_book.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/simple_book.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/simple_book.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+from gnucash import Book
+
+book = Book()
+
+#Call some methods that produce output to show that Book works
+print "New book:"
+book.print_dirty()
+book.mark_saved()
+print "\nBook marked saved:"
+book.print_dirty()
+
+book.destroy()

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/simple_session.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/simple_session.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/simple_session.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+from gnucash import \
+     Session, GnuCashBackendException, \
+     ERR_BACKEND_LOCKED, ERR_FILEIO_FILE_NOT_FOUND
+
+FILE_1 = "/tmp/not_there.xac"
+FILE_2 = "/tmp/example_file.xac"
+
+# open a file that isn't there, detect the error
+session = None
+try:
+    session = Session("file:%s" % FILE_1)
+except GnuCashBackendException, backend_exception:
+    assert( ERR_FILEIO_FILE_NOT_FOUND in backend_exception.errors)
+
+
+# create a new file
+session = Session("file:%s" % FILE_2, True)
+session.save()
+session.end()
+session.destroy()
+
+# open the new file, try to open it a second time, detect the lock
+session = Session("file:%s" % FILE_2)
+try:
+    session_2 = Session("file:%s" % FILE_2)
+except GnuCashBackendException, backend_exception:
+    assert( ERR_BACKEND_LOCKED in backend_exception.errors )
+session.end()
+session.destroy()
+
+    

Added: gnucash/trunk/src/optional/python-bindings/example_scripts/simple_test.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/example_scripts/simple_test.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/example_scripts/simple_test.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+from gnucash import \
+     Session, Account, Transaction, Split, GncCommodity, GncNumeric
+     
+
+FILE_1 = "/tmp/example.xac"
+
+session = None
+session = Session("file:%s" % FILE_1, True)
+
+book = session.book
+root_account = book.get_root_account()
+acct1 = Account(book)
+acct2 = Account(book)
+trans = Transaction(book)
+split1 = Split(book)
+split2 = Split(book)
+comm = GncCommodity(book, "Canadian Dollars", "CURRENCY", "CAD", None, 100)
+debit_num = GncNumeric(4, 1)
+credit_num = debit_num.neg()
+
+acct1.SetCommodity(comm)
+acct1.SetName("Savings")
+root_account.append_child(acct1)
+
+acct2.SetCommodity(comm)
+acct2.SetName("Food expenses")
+root_account.append_child(acct2)
+
+split1.SetValue(credit_num)
+split1.SetAccount(acct1)
+split1.SetParent(trans)
+
+split2.SetValue(debit_num)
+split2.SetAccount(acct2)
+split2.SetParent(trans)
+
+trans.SetCurrency(comm)
+trans.SetDescription("Groceries")
+
+book.print_dirty()
+
+book.mark_saved()
+book.mark_closed()
+
+session.save()
+session.end()
+session.destroy()

Added: gnucash/trunk/src/optional/python-bindings/function_class.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/function_class.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/function_class.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,167 @@
+# function_class.py -- Library for making python classes from a set
+#                      of functions. 
+#
+# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo at parit.ca>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, contact:
+# Free Software Foundation           Voice:  +1-617-542-5942
+# 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+# Boston, MA  02110-1301,  USA       gnu at gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+
+INSTANCE_ARGUMENT = "instance"
+
+class ClassFromFunctions(object):
+    """Inherit this class to give yourself a python class that wraps a set of
+    functions that together consitute the methods of the class.
+
+    The method functions must all have as a first argument an object
+    holding the instance data. There must also be a function that
+    returns a new instance of the class, the constructor.
+
+    Your subclass must define
+    _module - The module where the method functions, including the
+    constructor can be found
+    _new_instance - The name of a function that serves as a constructor,
+    returning the instance data.
+
+    To access the instance data, use the read-only property instance.
+
+    To add some functions from _module as methods, call classmethods like
+    add_method and add_methods_with_prefix.
+    """
+    def __new__(cls, *args, **kargs):
+        # why reimpliment __new__? Because later on we're going to
+        # use new to avoid creating new instances when existing instances
+        # already exist with the same __instance value, or equivlent __instance
+        # values, where this is desirable...
+        return super(ClassFromFunctions, cls).__new__(cls, *args, **kargs)
+    
+    def __init__(self, *args, **kargs):
+        """Construct a new instance, using either the function
+        self._module[self._new_instance] or using existing instance
+        data. (specified with the keyword argument, instance)
+
+        Pass the arguments that should be passed on to
+        self._module[self._new_instance] . Any arguments of that
+        are instances of ClassFromFunctions will be switched with the instance
+        data. (by calling the .instance property)
+        """
+        if INSTANCE_ARGUMENT in kargs:
+            self.__instance = kargs[INSTANCE_ARGUMENT]
+        else:
+            self.__instance = getattr(self._module, self._new_instance)(
+                *process_list_convert_to_instance(args) )
+
+    def get_instance(self):
+        """Get the instance data.
+
+        You can also call the instance property
+        """
+        return self.__instance
+
+    instance = property(get_instance)
+
+    # CLASS METHODS
+
+    @classmethod
+    def add_method(cls, function_name, method_name):
+        """Add the function, method_name to this class as a method named name
+        """
+        def method_function(self, *meth_func_args):
+            return getattr(self._module, function_name)(
+                self.instance,
+                *process_list_convert_to_instance(meth_func_args) )
+        
+        setattr(cls, method_name, method_function)
+        setattr(method_function, "__name__", method_name)
+        return method_function
+    
+    @classmethod
+    def add_methods_with_prefix(cls, prefix):
+        """Add a group of functions with the same prefix 
+        """
+        for function_name, function_value, after_prefix in \
+            extract_attributes_with_prefix(cls._module, prefix):
+                cls.add_method(function_name, after_prefix)
+    
+    @classmethod
+    def add_constructor_and_methods_with_prefix(cls, prefix, constructor):
+        """Add a group of functions with the same prefix, and set the
+        _new_instance attribute to prefix + constructor
+        """
+        cls.add_methods_with_prefix(prefix)
+        cls._new_instance = prefix + constructor
+
+    @classmethod
+    def decorate_functions(cls, decorator, *args):
+        for function_name in args:
+            setattr( cls, function_name,
+                     decorator( getattr(cls, function_name) ) )
+
+def method_function_returns_instance(method_function, cls):
+    """A function decorator that is used to decorates method functions that
+    return instance data, to return instances instead.
+
+    You can't use this decorator with @, because this function has a second
+    argument.
+    """
+    assert( 'instance' == INSTANCE_ARGUMENT )
+    def new_function(*args):
+        kargs = { INSTANCE_ARGUMENT : method_function(*args) }
+        return cls( **kargs )
+    
+    return new_function
+
+def default_arguments_decorator(function, *args):
+    """Decorates a function to give it default, positional arguments
+
+    You can't use this decorator with @, because this function has more
+    than one argument.
+    """
+    def new_function(*function_args):
+        new_argset = list(function_args)
+        new_argset.extend( args[ len(function_args): ] )
+        return function( *new_argset )
+    return new_function
+    
+def return_instance_if_value_has_it(value):
+    """Return value.instance if value is an instance of ClassFromFunctions,
+    else return value
+    """
+    if isinstance(value, ClassFromFunctions):
+        return value.instance
+    else:
+        return value
+
+def process_list_convert_to_instance( value_list ):
+    """Return a list built from value_list, where if a value is in an instance
+    of ClassFromFunctions, we put value.instance in the list instead.
+
+    Things that are not instances of ClassFromFunctions are returned to
+    the new list unchanged.
+    """
+    return [ return_instance_if_value_has_it(value)
+             for value in value_list ]
+
+def extract_attributes_with_prefix(obj, prefix):
+    """Generator that iterates through the attributes of an object and
+    for any attribute that matches a prefix, this yields
+    the attribute name, the attribute value, and the text that appears
+    after the prefix in the name
+    """
+    for attr_name, attr_value in obj.__dict__.iteritems():
+        if attr_name.startswith(prefix):
+            after_prefix = attr_name[ len(prefix): ]
+            yield attr_name, attr_value, after_prefix

Added: gnucash/trunk/src/optional/python-bindings/glib.i
===================================================================
--- gnucash/trunk/src/optional/python-bindings/glib.i	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/glib.i	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,92 @@
+/*
+ * glib.i -- SWIG interface file for type translation of glib types
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo at parit.ca>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+ */
+
+%typemap(in) gint8, gint16, gint32, gint64, gint, gshort, glong {
+    $1 = ($1_type)PyInt_AsLong($input);
+}
+
+%typemap(out) gint8, gint16, gint32, gint64, gint, gshort, glong {
+    $result = PyInt_FromLong($1);
+}
+
+%typemap(in) guint8, guint16, guint32, guint64, guint, gushort, gulong {
+    $1 = ($1_type)PyLong_AsUnsignedLong($input);
+}
+
+%typemap(out) guint8, guint16, guint32, guint64, guint, gushort, gulong {
+    $result = PyLong_FromUnsignedLong($1);
+}
+
+%typemap(in) gfloat, gdouble {
+    $1 = ($1_type)PyFloat_AsDouble($input);
+}
+
+%typemap(out) gfloat, gdouble {
+    $result = PyFloat_FromDouble($1);
+}
+
+%typemap(in) gchar * {
+    $1 = ($1_type)PyString_AsString($input);
+}
+
+%typemap(out) gchar * {
+    $result = PyString_FromString($1);
+}
+
+%typemap(in) gboolean {
+    if ($input == Py_True)
+        $1 = TRUE;
+    else if ($input == Py_False)
+        $1 = FALSE;
+    else
+    {
+        PyErr_SetString(
+            PyExc_ValueError,
+            "Python object passed to a gboolean argument was not True "
+            "or False" );
+        return NULL;
+    }
+}
+
+%typemap(out) gboolean {
+    if ($1 == TRUE)
+    {
+        Py_INCREF(Py_True);
+        $result = Py_True;
+    }
+    else if ($1 == FALSE)
+    {
+        Py_INCREF(Py_False);
+        $result = Py_False;
+    }
+    else
+    {
+        PyErr_SetString(
+            PyExc_ValueError,
+            "function returning gboolean returned a value that wasn't "
+            "TRUE or FALSE.");
+        return NULL;        
+    }
+}

Added: gnucash/trunk/src/optional/python-bindings/gnucash_core.i
===================================================================
--- gnucash/trunk/src/optional/python-bindings/gnucash_core.i	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/gnucash_core.i	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,76 @@
+/*
+ * gnucash_core.i -- SWIG interface file for the core parts of GnuCash
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo at parit.ca>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+ * @author Jeff Green, ParIT Worker Co-operative <jeff at parit.ca>
+ */
+
+%module(package="gnucash") gnucash_core_c
+
+%{
+#include "config.h"
+#include <datetime.h>
+#include "qofsession.h"
+#include "qofbook.h"
+#include "qofbackend.h"
+#include "gnc-commodity.h"
+#include "gnc-lot.h"
+#include "gnc-numeric.h"
+#include "Transaction.h"
+#include "Split.h"
+#include "Account.h"
+#include <guile/gh.h>
+%}
+
+%include <timespec.i>
+
+%include <glib.i>
+
+%include <qofbackend.h>
+
+// this function is defined in qofsession.h, but isnt found in the libraries,
+// ignoroed because SWIG attempts to link against (to create language bindings)
+%ignore qof_session_not_saved;
+%include <qofsession.h>
+
+%include <qofbook.h>
+
+%include <Transaction.h>
+%include <Split.h>
+%include <Account.h>
+
+//Ignored because it is unimplemented
+%ignore gnc_numeric_convert_with_error;
+%include <gnc-numeric.h>
+
+%include <gnc-commodity.h>
+
+%include <gnc-lot.h>
+
+%init %{
+
+g_type_init();
+scm_init_guile();
+gnc_module_load("gnucash/engine", 0);
+gnc_module_load("gnucash/business-core-file", 0);
+
+%}

Added: gnucash/trunk/src/optional/python-bindings/gnucash_core.py
===================================================================
--- gnucash/trunk/src/optional/python-bindings/gnucash_core.py	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/gnucash_core.py	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,374 @@
+# gnucash_core.py -- High level python wrapper classes for the core parts
+#                    of GnuCash
+#
+# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo at parit.ca>
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, contact:
+# Free Software Foundation           Voice:  +1-617-542-5942
+# 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+# Boston, MA  02110-1301,  USA       gnu at gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+# @author Jeff Green,   ParIT Worker Co-operative <jeff at parit.ca>
+
+import gnucash_core_c
+
+from function_class import \
+     ClassFromFunctions, extract_attributes_with_prefix, \
+     default_arguments_decorator, method_function_returns_instance
+
+class GnuCashCoreClass(ClassFromFunctions):
+    _module = gnucash_core_c
+
+class GnuCashBackendException(Exception):
+    def __init__(self, msg, errors):
+        Exception.__init__(self, msg)
+        self.errors = errors
+
+class Session(GnuCashCoreClass):
+    def __init__(self, book_uri=None, is_new=False):
+        """A convienent contructor that allows you to specify a book URI,
+        begin the session, and load the book.
+
+        This can give you the power of calling
+        qof_session_new, qof_session_begin, and qof_session_load all in one!
+
+        book_uri can be None to skip the calls to qof_session_begin and
+        qof_session_load, or it can be a string like "file:/test.xac"
+
+        qof_session_load is only called if is_new is set to False
+
+        is_new is passed to qof_session_begin as the
+        argument create_if_nonexistent
+
+        This function can raise a GnuCashBackendException. If it does,
+        you don't need to cleanup and call end() and destroy(), that is handled
+        for you, and the exception is raised.
+        """
+        GnuCashCoreClass.__init__(self)
+        if book_uri is not None:
+            try:
+                self.begin(book_uri, False, is_new)
+                if not is_new:
+                    self.load()
+            except GnuCashBackendException, backend_exception:
+                self.end()
+                self.destroy()
+                raise
+
+    def raise_backend_errors(self, called_function="qof_session function"):
+        """Raises a GnuCashBackendException if there are outstanding
+        QOF_BACKEND errors.
+
+        set called_function to name the function that was last called
+        """
+        errors = self.pop_all_errors()
+        if errors != ():
+            raise GnuCashBackendException(
+                "call to %s resulted in the "
+                "following errors, %s" % (called_function, errors),
+                errors )        
+
+    def generate_errors(self):
+        """A generator that yeilds any outstanding QofBackend errors 
+        """
+        while self.get_error() is not ERR_BACKEND_NO_ERR:
+            error = self.pop_error()
+            yield error
+
+    def pop_all_errors(self):
+        """Returns any accumulated qof backend errors as a tuple
+        """
+        return tuple( self.generate_errors() )
+
+    # STATIC METHODS
+    @staticmethod
+    def raise_backend_errors_after_call(function):
+        """A function decorator that results in a call to  
+        raise_backend_errors after execution.
+        """
+        def new_function(self, *args):
+            return_value = function(self, *args)
+            self.raise_backend_errors(function.__name__)
+            return return_value
+        return new_function
+
+class Book(GnuCashCoreClass): pass
+
+class GncNumeric(GnuCashCoreClass):
+    def __init__(self, num=0, denom=0, **kargs):
+        GnuCashCoreClass.__init__(self, num, denom, **kargs)
+        #if INSTANCE_ARG in kargs:
+        #    GnuCashCoreClass.__init__(**kargs)
+        #else:
+        #    self.set_denom(denom) # currently undefined
+        #    self.set_num(num)     # currently undefined
+
+class GncCommodity(GnuCashCoreClass):
+    def __init__(self, book, name=None, namespace=None, mnemonic=None, cusip=None, fraction=1, **kargs):
+        GnuCashCoreClass.__init__(self, book, name, namespace, mnemonic, cusip, fraction, **kargs)
+
+class GncCommodityTable(GnuCashCoreClass):
+    def __init__(self, book, **kargs):
+        GnuCashCoreClass.__init__(self, book, **kargs)
+
+class GncLot(GnuCashCoreClass):
+    def __init__(self, book, **kargs):
+        GnuCashCoreClass.__init__(self, book, **kargs)
+
+class Transaction(GnuCashCoreClass):
+    _new_instance = 'xaccMallocTransaction'
+
+class Split(GnuCashCoreClass):
+    _new_instance = 'xaccMallocSplit'
+
+class Account(GnuCashCoreClass):
+    _new_instance = 'xaccMallocAccount'
+
+# Session
+Session.add_constructor_and_methods_with_prefix('qof_session_', 'new')
+
+def one_arg_default_none(function):
+    return default_arguments_decorator(function, None, None)
+Session.decorate_functions(one_arg_default_none, "load", "save")
+
+Session.decorate_functions( Session.raise_backend_errors_after_call,
+                            "begin", "load", "save", "end")
+Session.get_book = method_function_returns_instance(
+    Session.get_book, Book )
+
+Session.book = property( Session.get_book )
+
+# import all of the session backend error codes into this module
+this_module_dict = globals()
+for error_name, error_value, error_name_after_prefix in \
+    extract_attributes_with_prefix(gnucash_core_c, 'ERR_'):
+    this_module_dict[ error_name ] = error_value
+
+#Book
+Book.add_constructor_and_methods_with_prefix('qof_book_', 'new')
+Book.add_method('gnc_book_get_root_account', 'get_root_account')
+#Functions that return Account
+Book.get_root_account = method_function_returns_instance(
+    Book.get_root_account, Account )
+
+# GncNumeric
+GncNumeric.add_constructor_and_methods_with_prefix('gnc_numeric_', 'create')
+#Functions that return GncNumeric
+GncNumeric.same = method_function_returns_instance(
+    GncNumeric.same, GncNumeric )
+GncNumeric.add = method_function_returns_instance(
+    GncNumeric.add, GncNumeric )
+GncNumeric.sub = method_function_returns_instance(
+    GncNumeric.sub, GncNumeric )
+GncNumeric.mul = method_function_returns_instance(
+    GncNumeric.mul, GncNumeric )
+GncNumeric.div = method_function_returns_instance(
+    GncNumeric.div, GncNumeric )
+GncNumeric.neg = method_function_returns_instance(
+    GncNumeric.neg, GncNumeric )
+GncNumeric.abs = method_function_returns_instance(
+    GncNumeric.abs, GncNumeric )
+GncNumeric.add_fixed = method_function_returns_instance(
+    GncNumeric.add_fixed, GncNumeric )
+GncNumeric.sub_fixed = method_function_returns_instance(
+    GncNumeric.sub_fixed, GncNumeric )
+GncNumeric.add_with_error = method_function_returns_instance(
+    GncNumeric.add_with_error, GncNumeric )
+GncNumeric.sub_with_error = method_function_returns_instance(
+    GncNumeric.sub_with_error, GncNumeric )
+GncNumeric.mul_with_error = method_function_returns_instance(
+    GncNumeric.mul_with_error, GncNumeric )
+GncNumeric.div_with_error = method_function_returns_instance(
+    GncNumeric.div_with_error, GncNumeric )
+GncNumeric.convert = method_function_returns_instance(
+    GncNumeric.convert, GncNumeric )
+GncNumeric.reduce = method_function_returns_instance(
+    GncNumeric.reduce, GncNumeric )
+
+# GncCommodity
+GncCommodity.add_constructor_and_methods_with_prefix('gnc_commodity_', 'new')
+#Functions that return GncCommodity
+GncCommodity.clone = method_function_returns_instance(
+    GncCommodity.clone, GncCommodity )
+
+# GncCommodityTable
+GncCommodityTable.add_constructor_and_methods_with_prefix('gnc_commodity_table_', 'get_table')
+#Functions that return GncCommodity
+GncCommodityTable.lookup = method_function_returns_instance(
+    GncCommodityTable.lookup, GncCommodity )
+GncCommodityTable.lookup_unique = method_function_returns_instance(
+    GncCommodityTable.lookup_unique, GncCommodity )
+GncCommodityTable.find_full = method_function_returns_instance(
+    GncCommodityTable.find_full, GncCommodity )
+GncCommodityTable.insert = method_function_returns_instance(
+    GncCommodityTable.insert, GncCommodity )
+
+# GncLot
+GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new')
+#Functions that return Account
+GncLot.get_account = method_function_returns_instance(
+    GncLot.get_account, Account )
+#Functions that return Book
+GncLot.get_book = method_function_returns_instance(
+    GncLot.get_book, Book )
+#Functions that return Split
+GncLot.get_earliest_split = method_function_returns_instance(
+    GncLot.get_earliest_split, Split )
+GncLot.get_latest_split = method_function_returns_instance(
+    GncLot.get_latest_split, Split )
+#Functions that return GncNumeric
+GncLot.get_balance = method_function_returns_instance(
+    GncLot.get_balance, GncNumeric )
+#Functions that return GncLot
+GncLot.lookup = method_function_returns_instance(
+    GncLot.lookup, GncLot )
+GncLot.make_default = method_function_returns_instance(
+    GncLot.make_default, GncLot )
+
+
+# Transaction
+Transaction.add_methods_with_prefix('xaccTrans')
+#Functions that return Split
+Transaction.GetSplit = method_function_returns_instance(
+    Transaction.GetSplit, Split )
+Transaction.FindSplitByAccount = method_function_returns_instance(
+    Transaction.FindSplitByAccount, Split )
+#Functions that return Transaction
+Transaction.Clone = method_function_returns_instance(
+    Transaction.Clone, Transaction )
+Transaction.Reverse = method_function_returns_instance(
+    Transaction.Reverse, Transaction )
+Transaction.GetReversedBy = method_function_returns_instance(
+    Transaction.GetReversedBy, Transaction )
+#Functions that return GncCommodity
+Transaction.GetCurrency = method_function_returns_instance(
+    Transaction.GetCurrency, GncCommodity )
+#Functions that return GncNumeric
+Transaction.GetImbalance = method_function_returns_instance(
+    Transaction.GetImbalance, GncNumeric )
+Transaction.GetAccountValue = method_function_returns_instance(
+    Transaction.GetAccountValue, GncNumeric )
+Transaction.GetAccountAmount = method_function_returns_instance(
+    Transaction.GetAccountAmount, GncNumeric )
+Transaction.GetAccountConvRate = method_function_returns_instance(
+    Transaction.GetAccountConvRate, GncNumeric )
+Transaction.GetAccountBalance = method_function_returns_instance(
+    Transaction.GetAccountBalance, GncNumeric )
+
+# Split
+Split.add_methods_with_prefix('xaccSplit')
+#Functions that return Book
+Split.GetBook = method_function_returns_instance(
+    Split.GetBook, Book )
+#Functions that return Account
+Split.GetAccount = method_function_returns_instance(
+    Split.GetAccount, Account )
+#Functions that return Transaction
+Split.GetParent = method_function_returns_instance(
+    Split.GetParent, Transaction )
+#Functions that return Split
+Split.Lookup = method_function_returns_instance(
+    Split.Lookup, Split )
+Split.GetOtherSplit = method_function_returns_instance(
+    Split.GetOtherSplit, Split )
+#Functions that return GncNumeric
+Split.GetAmount = method_function_returns_instance(
+    Split.GetAmount, GncNumeric )
+Split.GetValue = method_function_returns_instance(
+    Split.GetValue, GncNumeric )
+Split.GetSharePrice = method_function_returns_instance(
+    Split.GetSharePrice, GncNumeric )
+Split.ConvertAmount = method_function_returns_instance(
+    Split.ConvertAmount, GncNumeric )
+Split.GetBaseValue = method_function_returns_instance(
+    Split.GetBaseValue, GncNumeric )
+Split.GetBalance = method_function_returns_instance(
+    Split.GetBalance, GncNumeric )
+Split.GetClearedBalance = method_function_returns_instance(
+    Split.GetClearedBalance, GncNumeric )
+Split.GetReconciledBalance = method_function_returns_instance(
+    Split.GetReconciledBalance, GncNumeric )
+Split.VoidFormerAmount = method_function_returns_instance(
+    Split.VoidFormerAmount, GncNumeric )
+Split.VoidFormerValue = method_function_returns_instance(
+    Split.VoidFormerValue, GncNumeric )
+
+Split.account = property( Split.GetAccount, Split.SetAccount )
+Split.parent = property( Split.GetParent, Split.SetParent )
+
+# Account
+Account.add_methods_with_prefix('xaccAccount')
+Account.add_methods_with_prefix('gnc_account_')
+#Functions that return Book
+Account.get_book = method_function_returns_instance(
+    Account.get_book, Book )
+#Functions that return Account
+Account.Lookup = method_function_returns_instance(
+    Account.Lookup, Account )
+Account.get_parent = method_function_returns_instance(
+    Account.get_parent, Account )
+Account.get_root = method_function_returns_instance(
+    Account.get_root, Account )
+Account.nth_child = method_function_returns_instance(
+    Account.nth_child, Account )
+Account.lookup_by_name = method_function_returns_instance(
+    Account.lookup_by_name, Account )
+Account.lookup_by_full_name = method_function_returns_instance(
+    Account.lookup_by_full_name, Account )
+#Functions that return Transaction
+Account.FindTransByDesc = method_function_returns_instance(
+    Account.FindTransByDesc, Transaction )
+#Functions that return Split
+Account.FindSplitByDesc = method_function_returns_instance(
+    Account.FindSplitByDesc, Split )
+#Functions that return GncNumeric
+Account.get_start_balance = method_function_returns_instance(
+    Account.get_start_balance, GncNumeric )
+Account.get_start_cleared_balance = method_function_returns_instance(
+    Account.get_start_cleared_balance, GncNumeric )
+Account.GetBalance = method_function_returns_instance(
+    Account.GetBalance, GncNumeric )
+Account.GetClearedBalance = method_function_returns_instance(
+    Account.GetClearedBalance, GncNumeric )
+Account.GetReconciledBalance = method_function_returns_instance(
+    Account.GetReconciledBalance, GncNumeric )
+Account.GetPresentBalance = method_function_returns_instance(
+    Account.GetPresentBalance, GncNumeric )
+Account.GetProjectedMinimumBalance = method_function_returns_instance(
+    Account.GetProjectedMinimumBalance, GncNumeric )
+Account.GetBalanceAsOfDate = method_function_returns_instance(
+    Account.GetBalanceAsOfDate, GncNumeric )
+Account.ConvertBalanceToCurrency = method_function_returns_instance(
+    Account.ConvertBalanceToCurrency, GncNumeric )
+Account.ConvertBalanceToCurrencyAsOfDate = method_function_returns_instance(
+    Account.ConvertBalanceToCurrencyAsOfDate, GncNumeric )
+Account.GetBalanceInCurrency = method_function_returns_instance(
+    Account.GetBalanceInCurrency, GncNumeric )
+Account.GetClearedBalanceInCurrency = method_function_returns_instance(
+    Account.GetClearedBalanceInCurrency, GncNumeric )
+Account.GetReconciledBalanceInCurrency = method_function_returns_instance(
+    Account.GetReconciledBalanceInCurrency, GncNumeric )
+Account.GetPresentBalanceInCurrency = method_function_returns_instance(
+    Account.GetPresentBalanceInCurrency, GncNumeric )
+Account.GetProjectedMinimumBalanceInCurrency = method_function_returns_instance(
+    Account.GetProjectedMinimumBalanceInCurrency, GncNumeric )
+Account.GetBalanceAsOfDateInCurrency = method_function_returns_instance(
+    Account.GetBalanceInCurrency, GncNumeric )
+Account.GetBalanceChangeForPeriod = method_function_returns_instance(
+    Account.GetBalanceChangeForPeriod, GncNumeric )
+#Functions that return GncCommodity
+Account.GetCommodity = method_function_returns_instance(
+    Account.GetCommodity, GncCommodity )
+
+Account.name = property( Account.GetName, Account.SetName )

Added: gnucash/trunk/src/optional/python-bindings/timespec.i
===================================================================
--- gnucash/trunk/src/optional/python-bindings/timespec.i	                        (rev 0)
+++ gnucash/trunk/src/optional/python-bindings/timespec.i	2008-07-07 19:18:26 UTC (rev 17263)
@@ -0,0 +1,67 @@
+/*
+ * timespec.i -- SWIG interface file for type translation of Timespec types
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo at parit.ca>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark at parit.ca>
+ */
+
+// A typemap for converting python dates to Timespec in functions that
+// require Timespec as an argument
+%typemap(in) Timespec {
+    PyDateTime_IMPORT;
+    $1 = gnc_dmy2timespec(PyDateTime_GET_DAY($input),
+                          PyDateTime_GET_MONTH($input),
+                          PyDateTime_GET_YEAR($input) );
+}
+
+// A typemap for converting python dates to Timespec *, for fuctions that
+// requires a Timespec * as an argument. BIG ASSUMPTION, the function
+// recieving this pointer is going to make a copy of the data. After the
+// function call, the memory for the Timespec used to perform this conversion
+// is going to be lost, so make damn sure that the recipiant of this pointer
+// is NOT going dereference it sometime after this function call takes place.
+//
+// As far as I know, the xaccTransSetDate[Posted|Entered|Due]TS functions
+// from Transaction.h are the only functions with Timespec * that we re
+// actually using. I have personaly verifyed in the source that the pointer
+// being produced by this typemap is being deferenced, and the data copied
+// in all three functions.
+// 
+// The memory for the Timespec used for this conversion is allocated on the
+// stack. (SWIG will name the variables ts1, ts2, ts3...)
+//
+// Mark Jenkins <mark at parit.ca>
+%typemap(in) Timespec * (Timespec ts) {
+    PyDateTime_IMPORT;
+    ts = gnc_dmy2timespec(PyDateTime_GET_DAY($input),
+                          PyDateTime_GET_MONTH($input),
+                          PyDateTime_GET_YEAR($input) );
+    $1 = &ts;
+}
+
+// A typemap for converting Timespec values returned from functions to
+// python dates.
+%typemap(out) Timespec {
+    int year, month, day;
+    gnc_timespec2dmy($1, &day, &month, &year);
+    PyDateTime_IMPORT;
+    $result = PyDate_FromDate(year, month, day);
+}



More information about the gnucash-changes mailing list