python bindings on ubuntu/debian

Ian Smith-Heisters i at idiosyncra.tc
Mon Nov 24 12:27:06 EST 2008


On Mon, Nov 24, 2008 at 9:05 AM, Mark Jenkins <mark at parit.ca> wrote:
>> ok, then. Last question: is there any kind of schedule/idea of when
>> the bindings will make it to a stable release
>
> Well, I've attached our 2.2 patch (in two forms, one big -p0 style
> patch, python-bindings-all-2.2-2008-11-24 and 4 smaller -p1 style
> patches, python-bindings-???), if the GnuCash developers want to commit
> these they could.
>
> But, I'm not really submitting these with the request that they go into
> branches/2.2 yet, as I know we have a few api changes we're planning on
> submitting (hello Jeff?) and I don't really feel it is sensible to make
> api changes within a stable release series. Right now, you have to use
> the C-ish level python api in a few places in order for some things to
> work (go back and see the work workarounds and inconsistent style I had
> to use in the ugly script I wrote for Rolf..). That's really awkward. We
> really feel that everything should be do-able via the object oriented
> python api; and a patch to fix the current deficiencies is on the way.
> (again, hello Jeff?). After that I will consider our python-bindings to
> be 2.2 worthy.
>
> So, to answer your question, maybe in the next month....
>
> And hmmm, with the same rational in mind (no api changes within a stable
> release series), Jeff and I better get these changes done before a 2.4
> branch is created from trunk.
>
>
>> and from thence to
>> distro packages?
>
> With the attached patches you could keep up with changes your distro
> makes and still get the benefit of the python bindings
>
> $ apt-get src install gnucash
> $ cd gnucash-?.?.?
> ( change debian/rules to include --enable-python-bindings)
> $ patch -p0 < python-bindings-all-2.2-2008-11-24
> $ debchange -i
> (add a debian/changelog entry)
> $ dpkg-buildpackage -rfakeroot -uc -us
>
> This is helpful if ParIT goes away (not jinxing myself here!) and we no
> longer build packages that work with your distro. Also helpful if you
> don't want to use ParIT's binary builds, which include our own splash
> screen (Total, shameless advertising!) The debian package building stuff
> is nice, it checks that all build dependencies are already installed. As
> you mention, it enforces run time dependencies too.

cool, good to know that will work; I give it a try. You've addressed
my main concern in adopting ParIT's repo, ie. that it will go away or
get me addicted to a feature that never makes it upstream, not that it
will include shameless advertising ;)

>
> Another step to this being enabled in Debian is that the packaging must
> be updated to separate out the python bindings into a distinct
> architecture independent package (e.g. gnucash-python-bindings,
> python-gnucash, gnucash-python...). This kind of thing is Debian policy
> if I recall correctly.
>
> I don't know if Thomas (debian package maintainer) is going to do that
> or if he'll be busy and if someone else will have to volunteer. (I'll do
> it eventually...)
>
>
>
> Mark
>
> cc Thomas Bushnell
> cc Jeff
>
> Index: AUTHORS
> ===================================================================
> --- AUTHORS     (revision 168)
> +++ AUTHORS     (working copy)
> @@ -153,6 +153,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 <jeff at parit.ca> Python bindings, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
>  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
> @@ -170,6 +171,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, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
>  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
> Index: src/optional/Makefile.am
> ===================================================================
> --- src/optional/Makefile.am    (revision 168)
> +++ src/optional/Makefile.am    (working copy)
> @@ -1 +1,2 @@
> -SUBDIRS = xsl
> +SUBDIRS = xsl ${PYTHON_DIR}
> +DIST_SUBDIRS = xsl python-bindings
> Index: src/optional/python-bindings/timespec.i
> ===================================================================
> --- src/optional/python-bindings/timespec.i     (revision 0)
> +++ src/optional/python-bindings/timespec.i     (revision 0)
> @@ -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);
> +}
> Index: src/optional/python-bindings/function_class.py
> ===================================================================
> --- src/optional/python-bindings/function_class.py      (revision 0)
> +++ src/optional/python-bindings/function_class.py      (revision 0)
> @@ -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 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
> +
> +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: src/optional/python-bindings/example_scripts/simple_test.py
> ===================================================================
> --- src/optional/python-bindings/example_scripts/simple_test.py (revision 0)
> +++ src/optional/python-bindings/example_scripts/simple_test.py (revision 0)
> @@ -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: src/optional/python-bindings/example_scripts/simple_session.py
> ===================================================================
> --- src/optional/python-bindings/example_scripts/simple_session.py      (revision 0)
> +++ src/optional/python-bindings/example_scripts/simple_session.py      (revision 0)
> @@ -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: src/optional/python-bindings/example_scripts/simple_book.py
> ===================================================================
> --- src/optional/python-bindings/example_scripts/simple_book.py (revision 0)
> +++ src/optional/python-bindings/example_scripts/simple_book.py (revision 0)
> @@ -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: src/optional/python-bindings/tests/test_book.py
> ===================================================================
> --- src/optional/python-bindings/tests/test_book.py     (revision 0)
> +++ src/optional/python-bindings/tests/test_book.py     (revision 0)
> @@ -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: src/optional/python-bindings/tests/test_split.py
> ===================================================================
> --- src/optional/python-bindings/tests/test_split.py    (revision 0)
> +++ src/optional/python-bindings/tests/test_split.py    (revision 0)
> @@ -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: src/optional/python-bindings/tests/test_account.py
> ===================================================================
> --- src/optional/python-bindings/tests/test_account.py  (revision 0)
> +++ src/optional/python-bindings/tests/test_account.py  (revision 0)
> @@ -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: src/optional/python-bindings/tests/Makefile.am
> ===================================================================
> --- src/optional/python-bindings/tests/Makefile.am      (revision 0)
> +++ src/optional/python-bindings/tests/Makefile.am      (revision 0)
> @@ -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: src/optional/python-bindings/tests/test_transaction.py
> ===================================================================
> --- src/optional/python-bindings/tests/test_transaction.py      (revision 0)
> +++ src/optional/python-bindings/tests/test_transaction.py      (revision 0)
> @@ -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: src/optional/python-bindings/tests/runTests.py
> ===================================================================
> --- src/optional/python-bindings/tests/runTests.py      (revision 0)
> +++ src/optional/python-bindings/tests/runTests.py      (revision 0)
> @@ -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: src/optional/python-bindings/glib.i
> ===================================================================
> --- src/optional/python-bindings/glib.i (revision 0)
> +++ src/optional/python-bindings/glib.i (revision 0)
> @@ -0,0 +1,115 @@
> +/*
> + * 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;
> +    }
> +}
> +
> +%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: src/optional/python-bindings/__init__.py
> ===================================================================
> --- src/optional/python-bindings/__init__.py    (revision 0)
> +++ src/optional/python-bindings/__init__.py    (revision 0)
> @@ -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: src/optional/python-bindings/gnucash_core.i
> ===================================================================
> --- src/optional/python-bindings/gnucash_core.i (revision 0)
> +++ src/optional/python-bindings/gnucash_core.i (revision 0)
> @@ -0,0 +1,98 @@
> +/*
> + * 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 "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: src/optional/python-bindings/gnucash_core.py
> ===================================================================
> --- src/optional/python-bindings/gnucash_core.py        (revision 0)
> +++ src/optional/python-bindings/gnucash_core.py        (revision 0)
> @@ -0,0 +1,308 @@
> +# 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, \
> +     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):
> +    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): 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):
> +    _new_instance = 'xaccMallocAccount'
> +    def GetNthChild(self, n):
> +        return self.get_children().pop(n)
> +
> +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')
> +#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')
> +
> +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_constructor_and_methods_with_prefix('gnc_commodity_table_', 'get_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,
> +                }
> +methods_return_instance(GncLot, gnclot_dict)
> +
> +# Transaction
> +Transaction.add_methods_with_prefix('xaccTrans')
> +
> +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
> +                }
> +methods_return_instance(Transaction, trans_dict)
> +
> +# Split
> +Split.add_methods_with_prefix('xaccSplit')
> +
> +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
> +                }
> +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_dict =  {
> +                    'get_book' : Book,
> +                    'Lookup' : Account,
> +                    'get_parent' : Account,
> +                    'get_root' : Account,
> +                    'nth_child' : Account,
> +                    'lookup_by_name' : Account,
> +                    'lookup_by_full_name' : Account,
> +                    'GetNthChild' : 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
> +                }
> +methods_return_instance(Account, account_dict)
> +
> +Account.name = property( Account.GetName, Account.SetName )
> +
> +#GUID
> +GUID.add_methods_with_prefix('guid_')
> +
> Index: src/optional/python-bindings/Makefile.am
> ===================================================================
> --- src/optional/python-bindings/Makefile.am    (revision 0)
> +++ src/optional/python-bindings/Makefile.am    (revision 0)
> @@ -0,0 +1,32 @@
> +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 $@ $<
> +
> +gnucash_core_c.py: gnucash_core.c
> Index: src/optional/python-bindings/gnucash_business.py
> ===================================================================
> --- src/optional/python-bindings/gnucash_business.py    (revision 0)
> +++ src/optional/python-bindings/gnucash_business.py    (revision 0)
> @@ -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 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, \
> +     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: src/base-typemaps.i
> ===================================================================
> --- src/base-typemaps.i (revision 168)
> +++ src/base-typemaps.i (working copy)
> @@ -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: src/engine/engine.i
> ===================================================================
> --- src/engine/engine.i (revision 168)
> +++ src/engine/engine.i (working copy)
> @@ -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: configure.in
> ===================================================================
> --- configure.in        (revision 169)
> +++ configure.in        (working copy)
> @@ -1347,6 +1347,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
>  ###-------------------------------------------------------------------------
> @@ -1540,6 +1562,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
> @@ -1605,6 +1629,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
>
> 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 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 <jeff at parit.ca> Python bindings, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
>  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
> @@ -170,6 +171,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, with grant funding from Assiniboine Credit Union (http://assiniboine.mb.ca/)
>  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
> 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 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);
> +}
> 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 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
> +
> +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 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;
> +    }
> +}
> +
> +%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 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 "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 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, \
> +     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 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, \
> +     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 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)
> +])
> 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
>
> Index: 2.2-parit-releases/src/optional/python-bindings/Makefile.am
> ===================================================================
> --- 2.2-parit-releases/src/optional/python-bindings/Makefile.am (revision 105)
> +++ 2.2-parit-releases/src/optional/python-bindings/Makefile.am (revision 106)
> @@ -28,3 +28,5 @@
>         -I$(top_srcdir)/src -I$(top_srcdir)/src/engine \
>                -I$(top_srcdir)/src/business/business-core \
>                $(QOF_CFLAGS) -o $@ $<
> +
> +gnucash_core_c.py: gnucash_core.c
>
> Index: 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py
> ===================================================================
> --- 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py     (revision 122)
> +++ 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py     (revision 123)
> @@ -277,7 +277,6 @@
>                     'get_latest_split' : Split,
>                     'get_balance' : GncNumeric,
>                     'lookup' : GncLot,
> -                    'make_default' : GncLot
>                 }
>  methods_return_instance(GncLot, gnclot_dict)
>
>
> Index: 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py
> ===================================================================
> --- 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py     (revision 139)
> +++ 2.2-parit-releases/src/optional/python-bindings/gnucash_core.py     (revision 140)
> @@ -37,18 +37,6 @@
>         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.
> @@ -116,27 +104,8 @@
>             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.
> +class Book(GnuCashCoreClass): pass
>
> -    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)
> @@ -150,19 +119,8 @@
>     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.
> +class GncCommodityTable(GnuCashCoreClass): pass
>
> -    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)
> @@ -176,23 +134,9 @@
>     _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'
> +    def GetNthChild(self, n):
> +        return self.get_children().pop(n)
>
>  class GUID(GnuCashCoreClass):
>     _new_instance = 'guid_new_return'
> @@ -221,13 +165,9 @@
>  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')
> @@ -258,7 +198,8 @@
>     GncCommodity.clone, GncCommodity )
>
>  # GncCommodityTable
> -GncCommodityTable.add_methods_with_prefix('gnc_commodity_table_')
> +GncCommodityTable.add_constructor_and_methods_with_prefix('gnc_commodity_table_', 'get_table')
> +
>  commoditytable_dict =   {
>                             'lookup' : GncCommodity,
>                             'lookup_unique' : GncCommodity,
> @@ -282,7 +223,6 @@
>
>  # Transaction
>  Transaction.add_methods_with_prefix('xaccTrans')
> -Transaction.add_method('gncTransGetGUID', 'GetGUID');
>
>  trans_dict =    {
>                     'GetSplit': Split,
> @@ -296,14 +236,12 @@
>                     'GetAccountAmount': GncNumeric,
>                     'GetAccountConvRate': GncNumeric,
>                     'GetAccountBalance': GncNumeric,
> -                    'GetCurrency': GncCommodity,
> -                    'GetGUID': GUID
> +                    'GetCurrency': GncCommodity
>                 }
>  methods_return_instance(Transaction, trans_dict)
>
>  # Split
>  Split.add_methods_with_prefix('xaccSplit')
> -Split.add_method('gncSplitGetGUID', 'GetGUID');
>
>  split_dict =    {
>                     'GetBook': Book,
> @@ -320,8 +258,7 @@
>                     'GetClearedBalance': GncNumeric,
>                     'GetReconciledBalance': GncNumeric,
>                     'VoidFormerAmount': GncNumeric,
> -                    'VoidFormerValue': GncNumeric,
> -                    'GetGUID': GUID
> +                    'VoidFormerValue': GncNumeric
>                 }
>  methods_return_instance(Split, split_dict)
>
> @@ -331,7 +268,6 @@
>  # Account
>  Account.add_methods_with_prefix('xaccAccount')
>  Account.add_methods_with_prefix('gnc_account_')
> -Account.add_method('gncAccountGetGUID', 'GetGUID');
>
>  account_dict =  {
>                     'get_book' : Book,
> @@ -341,6 +277,7 @@
>                     'nth_child' : Account,
>                     'lookup_by_name' : Account,
>                     'lookup_by_full_name' : Account,
> +                    'GetNthChild' : Account,
>                     'FindTransByDesc' : Transaction,
>                     'FindSplitByDesc' : Split,
>                     'get_start_balance' : GncNumeric,
> @@ -360,8 +297,7 @@
>                     'GetProjectedMinimumBalanceInCurrency' : GncNumeric,
>                     'GetBalanceAsOfDateInCurrency' : GncNumeric,
>                     'GetBalanceChangeForPeriod' : GncNumeric,
> -                    'GetCommodity' : GncCommodity,
> -                    'GetGUID': GUID
> +                    'GetCommodity' : GncCommodity
>                 }
>  methods_return_instance(Account, account_dict)
>
> @@ -369,15 +305,4 @@
>
>  #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)
> -
>
>


More information about the gnucash-devel mailing list