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