Index: 2.2-parit-releases/AUTHORS
===================================================================
--- 2.2-parit-releases/AUTHORS	(revision 79)
+++ 2.2-parit-releases/AUTHORS	(revision 80)
@@ -153,6 +153,7 @@
 Dave Freese <DFreese@osc.uscg.mil> for leap-year fix
 Todd T. Fries <todd@flare.fries.net> OpenBSD fix
 John Goerzen <jgoerzen@complete.org> file i/o fix for 64-bit architectures
+Jeff Green <jeff@parit.ca> Python bindings, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
 Hans de Graaff <hans@degraaff.org> XML patches
 Daniel Hagerty <hag@linnaean.org> patch to balance sheet report
 Mitsuo Hamada <mhamada@redhat.com> messages Japanese translations
@@ -170,6 +171,7 @@
 Edward J. Huff <ejhuff@huff20may77.us> Date handling in reports, quarterly option
 Tomokazu Iwashita <iwashita@center.nitech.ac.jp> Japanese translation of xea
 David Jafferian <david.jafferian@east.sun.com> Delete account query code.
+Mark Jenkins <mark@parit.ca> Python bindings, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
 Miquel Jordana Vilamitjana <jjvmjv@mundomail.net> Spanish translation of manual
 Prakash Kailasa <PrakashK@bigfoot.com> for gnome build fixes
 Alexey Kakunin <small@arcadia.spb.ru> quickfill patch for Cyrillic
Index: 2.2-parit-releases/src/optional/Makefile.am
===================================================================
--- 2.2-parit-releases/src/optional/Makefile.am	(revision 79)
+++ 2.2-parit-releases/src/optional/Makefile.am	(revision 80)
@@ -1 +1,2 @@
-SUBDIRS = xsl
+SUBDIRS = xsl ${PYTHON_DIR}
+DIST_SUBDIRS = xsl python-bindings
Index: 2.2-parit-releases/src/optional/python-bindings/timespec.i
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/timespec.i	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/timespec.i	(revision 80)
@@ -0,0 +1,67 @@
+/*
+ * timespec.i -- SWIG interface file for type translation of Timespec types
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark@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@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);
+}
Index: 2.2-parit-releases/src/optional/python-bindings/function_class.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/function_class.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/function_class.py	(revision 80)
@@ -0,0 +1,176 @@
+# function_class.py -- Library for making python classes from a set
+#                      of functions. 
+#
+# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark@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
+
+def methods_return_instance(cls, function_dict):
+    """Iterates through a dictionary of function name strings and instance names
+    and sets the function to return the associated instance 
+    """
+    for func_name, instance_name in function_dict.iteritems():
+        setattr(cls, func_name, 
+            method_function_returns_instance( getattr(cls, func_name), instance_name))
+
Index: 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_test.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_test.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_test.py	(revision 80)
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# Creates a basic set of accounts and a couple of transactions
+
+import gnucash
+
+FILE_1 = "/tmp/example.xac"
+
+session = None
+session = gnucash.Session("file:%s" % FILE_1, True)
+
+book = session.book
+root_acct = gnucash.Account(book)
+expenses_acct = gnucash.Account(book)
+savings_acct = gnucash.Account(book)
+opening_acct = gnucash.Account(book)
+trans1 = gnucash.Transaction(book)
+trans2 = gnucash.Transaction(book)
+split1 = gnucash.Split(book)
+split3 = gnucash.Split(book)
+comm = gnucash.GncCommodity(book, "Canadian Dollars", "CURRENCY", "CAD", None, 100)
+num1 = gnucash.GncNumeric(4, 1)
+num2 = gnucash.GncNumeric(100, 1)
+
+#Set new root account
+book.set_root_account(root_acct)
+
+#Set up root account and add sub-accounts
+root_acct.SetName("Root")
+root_acct.SetType(13) #ACCT_TYPE_ROOT = 13
+root_acct.append_child(expenses_acct)
+root_acct.append_child(savings_acct)
+root_acct.append_child(opening_acct)
+
+#Set up Expenses account
+expenses_acct.SetCommodity(comm)
+expenses_acct.SetName("Expenses")
+expenses_acct.SetType(9) #ACCT_TYPE_EXPENSE = 9
+
+#Set up Savings account
+savings_acct.SetCommodity(comm)
+savings_acct.SetName("Savings")
+savings_acct.SetType(0) #ACCT_TYPE_BANK = 0
+
+#Set up Opening Balance account
+opening_acct.SetCommodity(comm)
+opening_acct.SetName("Opening Balance")
+opening_acct.SetType(10) #ACCT_TYPE_EQUITY = 10
+
+split1.SetValue(num1)
+split1.SetAccount(expenses_acct)
+split1.SetParent(trans1)
+
+split3.SetValue(num2)
+split3.SetAccount(savings_acct)
+split3.SetParent(trans2)
+
+trans1.SetCurrency(comm)
+trans1.SetDescription("Groceries")
+
+trans2.SetCurrency(comm)
+trans2.SetDescription("Opening Savings Balance")
+
+split2 = split1.GetOtherSplit()
+split2.SetAccount(savings_acct)
+
+split4 = split3.GetOtherSplit()
+split4.SetAccount(opening_acct)
+
+book.print_dirty()
+
+book.mark_saved()
+book.mark_closed()
+
+book.print_dirty()
+
+session.save()
+session.end()
+session.destroy()
Index: 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_session.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_session.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_session.py	(revision 80)
@@ -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()
+
+    
Index: 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_book.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_book.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/example_scripts/simple_book.py	(revision 80)
@@ -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()
Index: 2.2-parit-releases/src/optional/python-bindings/tests/test_book.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/test_book.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/test_book.py	(revision 80)
@@ -0,0 +1,14 @@
+from unittest import TestCase, main
+
+from gnucash import Book
+
+class BookSession( TestCase ):
+    def setUp(self):
+        self.book = Book()
+
+class TestBook( BookSession ):
+    def test_markclosed(self):
+        self.book.mark_closed()
+
+if __name__ == '__main__':
+    main()
Index: 2.2-parit-releases/src/optional/python-bindings/tests/test_split.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/test_split.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/test_split.py	(revision 80)
@@ -0,0 +1,35 @@
+from unittest import main
+
+from gnucash import Book, Account, Split, Transaction
+
+from test_book import BookSession
+
+class SplitSession( BookSession ):
+    def setUp(self):
+        BookSession.setUp(self)
+        self.split = Split(self.book)
+
+class TestSplit( SplitSession ):
+    def test_memo(self):
+        MEMO = "cookie monster"
+        self.assertEquals( '', self.split.GetMemo() )
+        self.split.SetMemo(MEMO)
+        self.assertEquals( MEMO, self.split.GetMemo() )
+
+    def test_account(self):
+        ACCT = Account(self.book)
+        self.split.SetAccount(ACCT)
+        self.assertTrue( ACCT.Equal(self.split.GetAccount(), True) )
+
+    def test_transaction(self):
+        TRANS = Transaction(self.book)
+        self.split.SetParent(TRANS)
+        TRANS.SetDescription("Foo")
+        self.assertEquals( TRANS.GetDescription(), self.split.GetParent().GetDescription() )
+
+    def test_equal(self):
+        COPY = self.split
+        self.assertTrue( self.split.Equal(COPY, True, False, False) )
+
+if __name__ == '__main__':
+    main()
Index: 2.2-parit-releases/src/optional/python-bindings/tests/test_account.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/test_account.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/test_account.py	(revision 80)
@@ -0,0 +1,26 @@
+from unittest import main
+
+from gnucash import Book, Account, Split
+
+from test_book import BookSession
+
+class AccountSession( BookSession ):
+    def setUp(self):
+        BookSession.setUp(self)
+        self.account = Account(self.book)
+
+class TestAccount( AccountSession ):
+    def test_name(self):
+        NAME = "Money"
+        self.assertEquals( '', self.account.GetName() )
+        self.account.SetName(NAME)
+        self.assertEquals( NAME, self.account.GetName() )
+
+    def test_split(self):
+        SPLIT = Split(self.book)
+        self.assertTrue(self.account.insert_split(SPLIT))
+        self.assertTrue(self.account.find_split(SPLIT))
+        self.assertTrue(self.account.remove_split(SPLIT))
+
+if __name__ == '__main__':
+    main()
Index: 2.2-parit-releases/src/optional/python-bindings/tests/Makefile.am
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/Makefile.am	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/Makefile.am	(revision 80)
@@ -0,0 +1,5 @@
+TESTS_ENVIRONMENT = PYTHONPATH=$(PYTHONPATH):$(pythondir) $(top_builddir)/src/bin/gnucash-env $(PYTHON)
+TESTS = runTests.py
+
+clean-local:
+	rm -f translog.*
Index: 2.2-parit-releases/src/optional/python-bindings/tests/test_transaction.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/test_transaction.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/test_transaction.py	(revision 80)
@@ -0,0 +1,97 @@
+from unittest import main
+
+from gnucash import Transaction, Book, Account, Split
+
+from test_book import BookSession
+
+class TransactionSession( BookSession ):
+    def setUp(self):
+        BookSession.setUp(self)
+        self.trans = Transaction(self.book)
+        #Evil bug means we must set a split for the transaction before making
+        #any other changes (is slightly useful for later tests)
+        self.split = Split(self.book)
+        self.split.SetParent(self.trans)
+        ############
+
+class TestTransaction( TransactionSession ):
+    def test_equal(self):
+        TRANS = self.trans
+        self.assertTrue( TRANS.Equal(self.trans, True, False, False, False) )
+
+    def test_clone(self):
+        TRANS = self.trans.Clone()
+        #Clone and original should have different GUIDs
+        self.assertFalse( TRANS.Equal(self.trans, True, False, False, False) )
+        #Clone and original should have the same balance
+        self.assertTrue( TRANS.Equal(self.trans, False, False, True, False) )
+
+    def test_edit(self):
+        self.assertFalse( self.trans.IsOpen() )
+        self.trans.BeginEdit()
+        self.assertTrue( self.trans.IsOpen() )
+        self.trans.CommitEdit()
+        self.assertFalse( self.trans.IsOpen() )
+
+    def test_rollback(self):
+        self.assertEquals( '', self.trans.GetDescription() )
+        self.trans.BeginEdit()
+        DESC = 'Food'
+        self.trans.SetDescription(DESC)
+        self.assertEquals( DESC, self.trans.GetDescription() )
+        self.trans.RollbackEdit() 
+        self.assertEquals( '', self.trans.GetDescription() )
+
+    def test_findsplit(self):
+        ACCT = Account(self.book)
+        self.split.SetAccount( ACCT )
+        SPLIT = self.trans.FindSplitByAccount( ACCT )
+        self.assertTrue( SPLIT.Equal(self.split, True, False, False) )
+    
+    def test_getsplit(self):
+        SPLIT = self.trans.GetSplit(0)
+        self.assertTrue( SPLIT.Equal(self.split, True, False, False) )
+        
+    def test_getsplitindex(self):
+        self.assertEquals( 0, self.trans.GetSplitIndex(self.split) )
+
+    def test_countsplits(self):
+        self.assertEquals( 1, self.trans.CountSplits() )
+
+    def test_readonly(self):
+        self.assertEquals( None, self.trans.GetReadOnly() )
+        REASON = 'none'
+        self.trans.SetReadOnly(REASON)
+        self.assertEquals( REASON, self.trans.GetReadOnly() )
+        self.trans.ClearReadOnly()
+        self.assertEquals( None, self.trans.GetReadOnly() )
+
+    def test_txntype(self):
+        self.assertEquals( '\x00', self.trans.GetTxnType() )
+        TYPE = 'I'
+        self.trans.SetTxnType(TYPE)
+        self.assertEquals( TYPE, self.trans.GetTxnType() )
+        TYPE = 'P'
+        self.trans.SetTxnType(TYPE)
+        self.assertEquals( TYPE, self.trans.GetTxnType() )
+
+    def test_num(self):
+        NUM = '5'
+        self.assertEquals( '', self.trans.GetNum() )
+        self.trans.SetNum(NUM)
+        self.assertEquals( NUM, self.trans.GetNum() )
+
+    def test_description(self):
+        DESCR = 'Groceries'
+        self.assertEquals( '', self.trans.GetDescription() )
+        self.trans.SetDescription(DESCR)
+        self.assertEquals( DESCR, self.trans.GetDescription() )
+
+    def test_notes(self):
+        NOTE = 'For dinner party'
+        self.assertEquals( None, self.trans.GetNotes() )
+        self.trans.SetNotes(NOTE)
+        self.assertEquals( NOTE, self.trans.GetNotes() )
+
+if __name__ == '__main__':
+    main()
Index: 2.2-parit-releases/src/optional/python-bindings/tests/runTests.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/tests/runTests.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/tests/runTests.py	(revision 80)
@@ -0,0 +1,14 @@
+import unittest
+
+from test import test_support
+
+from test_book import TestBook
+from test_account import TestAccount
+from test_split import TestSplit
+from test_transaction import TestTransaction
+
+def test_main():
+    test_support.run_unittest(TestBook, TestAccount, TestSplit, TestTransaction)
+
+if __name__ == '__main__':
+    test_main()
Index: 2.2-parit-releases/src/optional/python-bindings/glib.i
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/glib.i	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/glib.i	(revision 80)
@@ -0,0 +1,115 @@
+/*
+ * glib.i -- SWIG interface file for type translation of glib types
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark@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;        
+    }
+}
+
+%typemap(out) GList *, CommodityList *, SplitList *, AccountList *, LotList * {
+    guint i;
+    gpointer data;
+    PyObject *list = PyList_New(0);
+    for (i = 0; i < g_list_length($1); i++)
+    {
+        data = g_list_nth_data($1, i);        
+        if (GNC_IS_ACCOUNT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Account, 0)); 
+        else if (GNC_IS_SPLIT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Split, 0)); 
+        else if (GNC_IS_TRANSACTION(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Transaction, 0)); 
+        else if (GNC_IS_COMMODITY(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_gnc_commodity, 0)); 
+        else if (GNC_IS_LOT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_GNCLot, 0)); 
+        else
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_gpointer, 0)); 
+    }
+    $result = list;
+}
Index: 2.2-parit-releases/src/optional/python-bindings/__init__.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/__init__.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/__init__.py	(revision 80)
@@ -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 *
Index: 2.2-parit-releases/src/optional/python-bindings/gnucash_core.i
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/gnucash_core.i	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/gnucash_core.i	(revision 80)
@@ -0,0 +1,98 @@
+/*
+ * gnucash_core.i -- SWIG interface file for the core parts of GnuCash
+ *
+ * Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+ *
+ * @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca>
+ * @author Jeff Green, ParIT Worker Co-operative <jeff@parit.ca>
+ */
+
+%module(package="gnucash") gnucash_core_c
+
+%{
+#include "config.h"
+#include <datetime.h>
+#include "qofsession.h"
+#include "qofbook.h"
+#include "qofbackend.h"
+#include "qofid.h"
+#include "guid.h"
+#include "Transaction.h"
+#include "Split.h"
+#include "Account.h"
+#include "gnc-commodity.h"
+#include "gnc-lot.h"
+#include "gnc-numeric.h"
+#include "gncCustomer.h"
+#include "gncEmployee.h"
+#include "gncVendor.h"
+#include "gncAddress.h"
+#include "gncBillTerm.h"
+#include <guile/gh.h>
+%}
+
+%include <timespec.i>
+
+%include <base-typemaps.i>
+
+%include <engine-common.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 <qofid.h>
+
+/* SWIG doesn't like this macro, so redefine it to simply mean const */
+#define G_CONST_RETURN const
+%include <guid.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> */
+
+//business-core includes
+%include <gncCustomer.h>
+%include <gncEmployee.h>
+%include <gncVendor.h>
+%include <gncAddress.h>
+%include <gncBillTerm.h>
+
+%init %{
+
+g_type_init();
+scm_init_guile();
+gnc_module_load("gnucash/engine", 0);
+gnc_module_load("gnucash/business-core-file", 0);
+
+%}
Index: 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py	(revision 80)
@@ -0,0 +1,384 @@
+# gnucash_core.py -- High level python wrapper classes for the core parts
+#                    of GnuCash
+#
+# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca>
+# @author Jeff Green,   ParIT Worker Co-operative <jeff@parit.ca>
+
+import gnucash_core_c
+
+from function_class import \
+     ClassFromFunctions, extract_attributes_with_prefix, \
+     default_arguments_decorator, method_function_returns_instance, \
+     methods_return_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):
+    """A GnuCash book editing session
+
+    To commit changes to the session you may need to call save,
+    (this is allways the case with the file backend).
+    
+    When you're down with a session you may need to call end()
+
+    Every Session has a Book in the book attribute, which you'll definetely
+    be interested in, as every GnuCash entity (Transaction, Split, Vendor,
+    Invoice..) is associated with a particular book where it is stored.
+    """
+
+    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):
+    """A Book encapsulates all of the GnuCash data, it is the place where
+    all GnuCash entities (Transaction, Split, Vendor, Invoice...), are
+    stored. You'll notice that all of the constructors for those entities
+    need a book to be associated with.
+
+    The most common way to get a book is through the book property in the
+    Session class, that is, create a session that connects to some storage,
+    such as through 'my_session = Session('file:my_books.xac')', and access
+    the book via the book property, 'my_session.book'
+
+    If you would like to create a Book without any backing storage, call the
+    Book constructor wihout any parameters, 'Book()'. You can later merge
+    such a book into a book with actual store by using merge_init.
+
+    Methods of interest
+    get_root_account -- Returns the root level Account
+    get_table -- Returns a commodity lookup table, of type GncCommodityTable
+    """
+    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):
+    """A CommodityTable provides a way to store and lookup commoditys.
+    Commoditys are primarily currencies, but other tradable things such as
+    stocks, mutual funds, and material substances are posible.
+
+    Users of this library should not create thier own CommodityTable, instead
+    the get_table method from the Book class should be used.
+
+    This table is automatically populated with the GnuCash default commodity's
+    which includes most of the world's currencies.
+    """
+    pass
+
+class GncLot(GnuCashCoreClass):
+    def __init__(self, book, **kargs):
+        GnuCashCoreClass.__init__(self, book, **kargs)
+
+class Transaction(GnuCashCoreClass):
+    _new_instance = 'xaccMallocTransaction'
+    def GetNthSplit(self, n):
+        return self.GetSplitList().pop(n)
+
+class Split(GnuCashCoreClass):
+    _new_instance = 'xaccMallocSplit'
+
+class Account(GnuCashCoreClass):
+    """A GnuCash Account.
+
+    A fundamental entity in accounting, an Account provides representation
+    for a financial object, such as a BANK account, an ASSET (like a building),
+    a LIABILITY (such as a bank loan), a summary of some type of EXPENSE, or
+    a summary of some source of INCOME.
+
+    The words in upper case are the constants that GnuCash and this library uses
+    to describe account type. Here is the full list:
+    BANK, CASH, CREDIT, ASSET, LIABILITY, STOCK, MUTUAL
+    CURRENCY, INCOME, EXPENSE, EQUITY, RECEIVABLE, PAYABLE, 
+    CHECKING, SAVINGS, MONEYMRKT, CREDITLINE
+
+    These are not strings, they are attributes you can import from this
+    module
+    """
+    _new_instance = 'xaccMallocAccount'
+
+class GUID(GnuCashCoreClass):
+    _new_instance = 'guid_new_return'
+
+# 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')
+Book.add_method('gnc_book_set_root_account', 'set_root_account')
+Book.add_method('gnc_commodity_table_get_table', 'get_table')
+#Functions that return Account
+Book.get_root_account = method_function_returns_instance(
+    Book.get_root_account, Account )
+#Functions that return GncCommodityTable
+Book.get_table = method_function_returns_instance(
+    Book.get_table, GncCommodityTable ) 
+
+# GncNumeric
+GncNumeric.add_constructor_and_methods_with_prefix('gnc_numeric_', 'create')
+
+gncnumeric_dict =   { 
+                        'same' : GncNumeric, 
+                        'add' : GncNumeric, 
+                        'sub' : GncNumeric, 
+                        'mul' : GncNumeric, 
+                        'div' : GncNumeric, 
+                        'neg' : GncNumeric, 
+                        'abs' : GncNumeric, 
+                        'add_fixed' : GncNumeric, 
+                        'sub_fixed' : GncNumeric, 
+                        'add_with_error' : GncNumeric, 
+                        'sub_with_error' : GncNumeric, 
+                        'mul_with_error' : GncNumeric, 
+                        'div_with_error' : GncNumeric, 
+                        'convert' : GncNumeric, 
+                        'reduce' : GncNumeric 
+                    }
+methods_return_instance(GncNumeric, gncnumeric_dict)
+
+# 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_methods_with_prefix('gnc_commodity_table_')
+commoditytable_dict =   { 
+                            'lookup' : GncCommodity, 
+                            'lookup_unique' : GncCommodity, 
+                            'find_full' : GncCommodity, 
+                            'insert' : GncCommodity 
+                        }
+methods_return_instance(GncCommodityTable, commoditytable_dict)
+
+# GncLot
+GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new')
+
+gnclot_dict =   { 
+                    'get_account' : Account, 
+                    'get_book' : Book, 
+                    'get_earliest_split' : Split, 
+                    'get_latest_split' : Split, 
+                    'get_balance' : GncNumeric, 
+                    'lookup' : GncLot, 
+                    'make_default' : GncLot 
+                }
+methods_return_instance(GncLot, gnclot_dict)
+
+# Transaction
+Transaction.add_methods_with_prefix('xaccTrans')
+Transaction.add_method('gncTransGetGUID', 'GetGUID');
+
+trans_dict =    {
+                    'GetSplit': Split, 
+                    'FindSplitByAccount': Split, 
+                    'GetNthSplit': Split, 
+                    'Clone': Transaction, 
+                    'Reverse': Transaction, 
+                    'GetReversedBy': Transaction, 
+                    'GetImbalance': GncNumeric, 
+                    'GetAccountValue': GncNumeric, 
+                    'GetAccountAmount': GncNumeric, 
+                    'GetAccountConvRate': GncNumeric, 
+                    'GetAccountBalance': GncNumeric, 
+                    'GetCurrency': GncCommodity,
+                    'GetGUID': GUID
+                }
+methods_return_instance(Transaction, trans_dict)
+
+# Split
+Split.add_methods_with_prefix('xaccSplit')
+Split.add_method('gncSplitGetGUID', 'GetGUID');
+
+split_dict =    {
+                    'GetBook': Book, 
+                    'GetAccount': Account, 
+                    'GetParent': Transaction, 
+                    'Lookup': Split, 
+                    'GetOtherSplit': Split, 
+                    'GetAmount': GncNumeric, 
+                    'GetValue': GncNumeric, 
+                    'GetSharePrice': GncNumeric, 
+                    'ConvertAmount': GncNumeric, 
+                    'GetBaseValue': GncNumeric, 
+                    'GetBalance': GncNumeric, 
+                    'GetClearedBalance': GncNumeric, 
+                    'GetReconciledBalance': GncNumeric, 
+                    'VoidFormerAmount': GncNumeric, 
+                    'VoidFormerValue': GncNumeric,
+                    'GetGUID': GUID
+                }
+methods_return_instance(Split, split_dict)
+
+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_')
+Account.add_method('gncAccountGetGUID', 'GetGUID');
+
+account_dict =  {   
+                    'get_book' : Book, 
+                    'Lookup' : Account, 
+                    'get_parent' : Account, 
+                    'get_root' : Account, 
+                    'nth_child' : Account, 
+                    'lookup_by_name' : Account, 
+                    'lookup_by_full_name' : Account, 
+                    'FindTransByDesc' : Transaction, 
+                    'FindSplitByDesc' : Split, 
+                    'get_start_balance' : GncNumeric, 
+                    'get_start_cleared_balance' : GncNumeric, 
+                    'GetBalance' : GncNumeric, 
+                    'GetClearedBalance' : GncNumeric, 
+                    'GetReconciledBalance' : GncNumeric, 
+                    'GetPresentBalance' : GncNumeric, 
+                    'GetProjectedMinimumBalance' : GncNumeric, 
+                    'GetBalanceAsOfDate' : GncNumeric, 
+                    'ConvertBalanceToCurrency' : GncNumeric, 
+                    'ConvertBalanceToCurrencyAsOfDate' : GncNumeric, 
+                    'GetBalanceInCurrency' : GncNumeric, 
+                    'GetClearedBalanceInCurrency' : GncNumeric, 
+                    'GetReconciledBalanceInCurrency' : GncNumeric, 
+                    'GetPresentBalanceInCurrency' : GncNumeric, 
+                    'GetProjectedMinimumBalanceInCurrency' : GncNumeric, 
+                    'GetBalanceAsOfDateInCurrency' : GncNumeric, 
+                    'GetBalanceChangeForPeriod' : GncNumeric, 
+                    'GetCommodity' : GncCommodity,
+                    'GetGUID': GUID 
+                }
+methods_return_instance(Account, account_dict)
+
+Account.name = property( Account.GetName, Account.SetName )
+
+#GUID
+GUID.add_methods_with_prefix('guid_')
+GUID.add_method('xaccAccountLookup', 'AccountLookup')
+GUID.add_method('xaccTransLookup', 'TransLookup')
+GUID.add_method('xaccSplitLookup', 'SplitLookup')
+
+guid_dict = {
+                'copy' : GUID,
+                'TransLookup': Transaction,
+                'AccountLookup': Account,
+                'SplitLookup': Split
+            }
+methods_return_instance(GUID, guid_dict)
+                
Index: 2.2-parit-releases/src/optional/python-bindings/Makefile.am
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/Makefile.am	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/Makefile.am	(revision 80)
@@ -0,0 +1,30 @@
+SUBDIRS = . tests
+
+BUILT_SOURCES = gnucash_core.c
+SWIG_SOURCES = gnucash_core.i
+
+pkgpython_PYTHON = __init__.py function_class.py \
+gnucash_core.py gnucash_core_c.py gnucash_business.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 \
+							  -I$(top_srcdir)/src/business/business-core 
+
+# 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 \
+	${top_builddir}/src/business/business-core/libgncmod-business-core.la
+
+gnucash_core.c : $(SWIG_SOURCES)
+	swig $(SWIG_PYTHON_OPT) -Wall -Werror \
+        -I$(top_srcdir)/src -I$(top_srcdir)/src/engine \
+		-I$(top_srcdir)/src/business/business-core \
+		$(QOF_CFLAGS) -o $@ $<
Index: 2.2-parit-releases/src/optional/python-bindings/gnucash_business.py
===================================================================
--- 2.2-parit-releases/src/optional/python-bindings/gnucash_business.py	(revision 0)
+++ 2.2-parit-releases/src/optional/python-bindings/gnucash_business.py	(revision 80)
@@ -0,0 +1,92 @@
+# gnucash_business.py -- High level python wrapper classes for the business
+#                        parts of GnuCash
+#
+# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@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@gnu.org
+#
+# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca>
+# @author Jeff Green,   ParIT Worker Co-operative <jeff@parit.ca>
+
+import gnucash_core_c
+
+from function_class import \
+     ClassFromFunctions, extract_attributes_with_prefix, \
+     default_arguments_decorator, method_function_returns_instance, \
+     methods_return_instance
+
+from gnucash_core import \
+     GnuCashCoreClass, GncNumeric, GncCommodity, Transaction, \
+     Split, Book
+
+class Customer(GnuCashCoreClass): pass
+
+class Employee(GnuCashCoreClass): pass
+
+class Vendor(GnuCashCoreClass): pass
+
+class Address(GnuCashCoreClass): pass
+    
+class BillTerm(GnuCashCoreClass): pass
+
+# Customer
+Customer.add_constructor_and_methods_with_prefix('gncCustomer', 'Create')
+
+customer_dict = {
+                    'GetAddr' : Address,
+                    'GetShipAddr' : Address,
+                    'GetDiscount' : GncNumeric,
+                    'GetCredit' : GncNumeric,
+                    'GetTerms' : BillTerm,
+                    'GetCurrency' : GncCommodity
+                }
+methods_return_instance(Customer, customer_dict)
+
+# Employee
+Employee.add_constructor_and_methods_with_prefix('gncEmployee', 'Create')
+
+employee_dict = {
+                    'GetBook' : Book,
+                    'GetAddr' : Address,
+                    'GetWorkday' : GncNumeric,
+                    'GetRate' : GncNumeric,
+                    'GetCurrency' : GncCommodity
+                }
+methods_return_instance(Employee, employee_dict)
+
+# Vendor
+Vendor.add_constructor_and_methods_with_prefix('gncVendor', 'Create')
+
+vendor_dict =   {
+                    'GetAddr' : Address,
+                    'GetTerms' : BillTerm,
+                    'GetCurrency' : GncCommodity
+                }
+methods_return_instance(Vendor, vendor_dict)
+
+# Address
+Address.add_constructor_and_methods_with_prefix('gncAddress', 'Create')
+
+# BillTerm
+BillTerm.add_constructor_and_methods_with_prefix('gncBillTerm', 'Create')
+
+billterm_dict = {
+                    'LookupByName' : BillTerm,
+                    'GetDiscount' : GncNumeric,
+                    'GetParent' : BillTerm,
+                    'ReturnChild' : BillTerm
+                }
+methods_return_instance(BillTerm, billterm_dict)
Index: 2.2-parit-releases/src/base-typemaps.i
===================================================================
--- 2.2-parit-releases/src/base-typemaps.i	(revision 79)
+++ 2.2-parit-releases/src/base-typemaps.i	(revision 80)
@@ -1,30 +1,17 @@
-%typemap(in) gboolean "$1 = SCM_NFALSEP($input) ? TRUE : FALSE;"
-%typemap(out) gboolean "$result = $1 ? SCM_BOOL_T : SCM_BOOL_F;"
-
-%typemap(in) Timespec "$1 = gnc_timepair2timespec($input);"
-%typemap(out) Timespec "$result = gnc_timespec2timepair($1);"
-
-%typemap(in) GUID "$1 = gnc_scm2guid($input);"
-%typemap(out) GUID "$result = gnc_guid2scm($1);"
-%typemap(in) GUID * (GUID g) " g = gnc_scm2guid($input); $1 = &g; "
-%typemap(out) GUID * " $result = ($1) ? gnc_guid2scm(*($1)): SCM_UNDEFINED; "
-
-%typemap(in) gnc_numeric "$1 = gnc_scm_to_numeric($input);"
-%typemap(out) gnc_numeric "$result = gnc_numeric_to_scm($1);"
-
-%typemap(in) gint64 " $1 = gnc_scm_to_gint64($input); "
-%typemap(out) gint64 " $result = gnc_gint64_to_scm($1); "
-
 /* Not sure why SWIG doesn't figure this out. */
-typedef void * gpointer;
 typedef int gint;
 typedef int time_t;
 typedef unsigned int guint;
 typedef double gdouble;
+typedef float gfloat;
 typedef char * URLType;
+typedef void * gpointer;
+
+%typemap(newfree) gchar * "g_free($1);"
+
+#if defined(SWIGGUILE)
 typedef char gchar;
 
-%typemap(newfree) gchar * "g_free($1);"
 %typemap (out) char * {
   $result = scm_makfrom0str((const char *)$1);
   if (!SCM_NFALSEP($result)) {
@@ -34,7 +21,23 @@
 %typemap(in) GNCPrintAmountInfo "$1 = gnc_scm2printinfo($input);"
 %typemap(out) GNCPrintAmountInfo "$result = gnc_printinfo2scm($1);"
 
+%typemap(in) gboolean "$1 = SCM_NFALSEP($input) ? TRUE : FALSE;"
+%typemap(out) gboolean "$result = $1 ? SCM_BOOL_T : SCM_BOOL_F;"
 
+%typemap(in) Timespec "$1 = gnc_timepair2timespec($input);"
+%typemap(out) Timespec "$result = gnc_timespec2timepair($1);"
+
+%typemap(in) GUID "$1 = gnc_scm2guid($input);"
+%typemap(out) GUID "$result = gnc_guid2scm($1);"
+%typemap(in) GUID * (GUID g) " g = gnc_scm2guid($input); $1 = &g; "
+%typemap(out) GUID * " $result = ($1) ? gnc_guid2scm(*($1)): SCM_UNDEFINED; "
+
+%typemap(in) gnc_numeric "$1 = gnc_scm_to_numeric($input);"
+%typemap(out) gnc_numeric "$result = gnc_numeric_to_scm($1);"
+
+%typemap(in) gint64 " $1 = gnc_scm_to_gint64($input); "
+%typemap(out) gint64 " $result = gnc_gint64_to_scm($1); "
+
 %define GLIST_HELPER_INOUT(ListType, ElemSwigType)
 %typemap(in) ListType * {
   SCM list = $input;
@@ -66,5 +69,87 @@
   $result = scm_reverse(list);
 }
 %enddef
+#elif defined(SWIGPYTHON) /* Typemaps for Python */
+%typemap(in) gint8, gint16, gint32, gint64, gshort, glong {
+    $1 = ($1_type)PyInt_AsLong($input);
+}
 
+%typemap(out) gint8, gint16, gint32, gint64, gshort, glong {
+    $result = PyInt_FromLong($1);
+}
 
+%typemap(in) guint8, guint16, guint32, guint64, gushort, gulong {
+    $1 = ($1_type)PyLong_AsUnsignedLong($input);
+}
+
+%typemap(out) guint8, guint16, guint32, guint64, gushort, gulong {
+    $result = PyLong_FromUnsignedLong($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;        
+    }
+}
+
+%typemap(out) GList *, CommodityList *, SplitList *, AccountList *, LotList * {
+    guint i;
+    gpointer data;
+    PyObject *list = PyList_New(0);
+    for (i = 0; i < g_list_length($1); i++)
+    {
+        data = g_list_nth_data($1, i);        
+        if (GNC_IS_ACCOUNT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Account, 0)); 
+        else if (GNC_IS_SPLIT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Split, 0)); 
+        else if (GNC_IS_TRANSACTION(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_Transaction, 0)); 
+        else if (GNC_IS_COMMODITY(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_gnc_commodity, 0)); 
+        else if (GNC_IS_LOT(data))
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_GNCLot, 0)); 
+        else
+            PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_void, 0)); 
+    }
+    $result = list;
+}
+#endif
Index: 2.2-parit-releases/src/engine/engine.i
===================================================================
--- 2.2-parit-releases/src/engine/engine.i	(revision 79)
+++ 2.2-parit-releases/src/engine/engine.i	(revision 80)
@@ -23,7 +23,15 @@
 
 %import "base-typemaps.i"
 
+%include "engine-common.i"
 
+%inline %{
+static const GUID * gncPriceGetGUID(GNCPrice *x)
+{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
+static const GUID * gncBudgetGetGUID(GncBudget *x)
+{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
+%}
+
 GLIST_HELPER_INOUT(SplitList, SWIGTYPE_p_Split);
 GLIST_HELPER_INOUT(TransList, SWIGTYPE_p_Transaction);
 GLIST_HELPER_INOUT(LotList, SWIGTYPE_p_GNCLot);
@@ -32,27 +40,6 @@
 // TODO: free PriceList?
 GLIST_HELPER_INOUT(CommodityList, SWIGTYPE_p_gnc_commodity);
 
-
-%inline %{
-static const GUID * gncSplitGetGUID(Split *x)
-{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
-static const GUID * gncTransGetGUID(Transaction *x)
-{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
-static const GUID * gncAccountGetGUID(Account *x)
-{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
-static const GUID * gncPriceGetGUID(GNCPrice *x)
-{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
-static const GUID * gncBudgetGetGUID(GncBudget *x)
-{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
-%}
-
-%typemap(newfree) AccountList * "g_list_free($1);"
-%typemap(newfree) SplitList * "g_list_free($1);"
-%typemap(newfree) TransList * "g_list_free($1);"
-%typemap(newfree) PriceList * "g_list_free($1);"
-%typemap(newfree) LotList * "g_list_free($1);"
-%typemap(newfree) CommodityList * "g_list_free($1);"
-
 %typemap(newfree) gchar * "g_free($1);"
 
 /* NB: The object ownership annotations should already cover all the
@@ -83,18 +70,7 @@
   static QofIdType QOF_ID_BOOK_SCM (void) { return QOF_ID_BOOK; }
 }
 
-%include <Split.h>
 %include <engine-helpers.h>
-AccountList * gnc_account_get_children (const Account *account);
-AccountList * gnc_account_get_children_sorted (const Account *account);
-AccountList * gnc_account_get_descendants (const Account *account);
-AccountList * gnc_account_get_descendants_sorted (const Account *account);
-%ignore gnc_account_get_children;
-%ignore gnc_account_get_children_sorted;
-%ignore gnc_account_get_descendants;
-%ignore gnc_account_get_descendants_sorted;
-%include <Account.h>
-%include <Transaction.h>
 %include <gnc-pricedb.h>
 
 QofSession * qof_session_new (void);
@@ -193,7 +169,6 @@
 %ignore gnc_quote_source_set_fq_installed;
 %include <gnc-commodity.h>
 
-%include <gnc-lot.h>
 %include <gnc-session-scm.h>
 void gnc_hook_add_scm_dangler (const gchar *name, SCM proc);
 void gnc_hook_run (const gchar *name, gpointer data);
Index: 2.2-parit-releases/src/engine/engine-common.i
===================================================================
--- 2.2-parit-releases/src/engine/engine-common.i	(revision 0)
+++ 2.2-parit-releases/src/engine/engine-common.i	(revision 80)
@@ -0,0 +1,31 @@
+%inline %{
+static const GUID * gncSplitGetGUID(Split *x)
+{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
+static const GUID * gncTransGetGUID(Transaction *x)
+{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
+static const GUID * gncAccountGetGUID(Account *x)
+{ return qof_instance_get_guid(QOF_INSTANCE(x)); }
+%}
+
+%typemap(newfree) AccountList * "g_list_free($1);"
+%typemap(newfree) SplitList * "g_list_free($1);"
+%typemap(newfree) TransList * "g_list_free($1);"
+%typemap(newfree) PriceList * "g_list_free($1);"
+%typemap(newfree) LotList * "g_list_free($1);"
+%typemap(newfree) CommodityList * "g_list_free($1);"
+
+%include <Split.h>
+
+AccountList * gnc_account_get_children (const Account *account);
+AccountList * gnc_account_get_children_sorted (const Account *account);
+AccountList * gnc_account_get_descendants (const Account *account);
+AccountList * gnc_account_get_descendants_sorted (const Account *account);
+%ignore gnc_account_get_children;
+%ignore gnc_account_get_children_sorted;
+%ignore gnc_account_get_descendants;
+%ignore gnc_account_get_descendants_sorted;
+%include <Account.h>
+
+%include <Transaction.h>
+
+%include <gnc-lot.h>
Index: 2.2-parit-releases/macros/ac_python_devel.m4
===================================================================
--- 2.2-parit-releases/macros/ac_python_devel.m4	(revision 0)
+++ 2.2-parit-releases/macros/ac_python_devel.m4	(revision 80)
@@ -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@web.de>
+dnl @author Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+dnl @author Rafael Laboissiere <laboissiere@psy.mpg.de>
+dnl @author Andrew Collier <colliera@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)
+])
Index: 2.2-parit-releases/configure.in
===================================================================
--- 2.2-parit-releases/configure.in	(revision 79)
+++ 2.2-parit-releases/configure.in	(revision 80)
@@ -1341,6 +1341,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
 ###-------------------------------------------------------------------------
@@ -1533,6 +1555,8 @@
           src/import-export/hbci/schemas/Makefile
           src/import-export/hbci/test/Makefile
           src/optional/Makefile
+          src/optional/python-bindings/Makefile
+          src/optional/python-bindings/tests/Makefile
           src/optional/xsl/Makefile
           src/pixmaps/Makefile
           src/quotes/Makefile
@@ -1598,6 +1622,9 @@
 if test x${AQBANKING_DIR} != x; then
 components="$components $AQBANKING_DIR"
 fi
+if test x${PYTHON_DIR} != x; then
+components="$components python-bindings"
+fi
 
 AC_MSG_RESULT([
   Options detected/selected
