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