This commit is contained in:
QWork66 2025-02-08 11:29:59 -05:00 committed by GitHub
commit ff97b98c57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 123 additions and 12 deletions

View File

@ -65,7 +65,7 @@ def ya_add_method(_class, function, method_name=None, clsmethod=False, noinstanc
setattr(gnucash.gnucash_core_c,function.__name__,function) setattr(gnucash.gnucash_core_c,function.__name__,function)
if clsmethod: if clsmethod:
mf=_class.ya_add_classmethod(function.__name__,method_name) mf=_class.add_classmethod(function.__name__,method_name)
elif noinstance: elif noinstance:
mf=_class.add_method(function.__name__,method_name) mf=_class.add_method(function.__name__,method_name)
else: else:

View File

@ -87,7 +87,7 @@ class ClassFromFunctions(object):
@classmethod @classmethod
def add_method(cls, function_name, method_name): def add_method(cls, function_name, method_name):
"""! Add the function, method_name to this class as a method named name """! Add the function, function_name to this class as a method named method_name
arguments: arguments:
@param cls Class: class to add methods to @param cls Class: class to add methods to
@ -116,8 +116,8 @@ class ClassFromFunctions(object):
return method_function return method_function
@classmethod @classmethod
def ya_add_classmethod(cls, function_name, method_name): def add_classmethod(cls, function_name, method_name):
"""! Add the function, method_name to this class as a classmethod named name """! Add the function, function_name to this class as a classmethod named method_name
Taken from function_class and modified from add_method() to add classmethod Taken from function_class and modified from add_method() to add classmethod
instead of method and not to turn self argument to self.instance. instead of method and not to turn self argument to self.instance.
@ -129,17 +129,16 @@ class ClassFromFunctions(object):
function will be wrapped by method_function""" function will be wrapped by method_function"""
def method_function(self, *meth_func_args, **meth_func_kargs): def method_function(cls, *meth_func_args, **meth_func_kargs):
"""! wrapper method for function """! wrapper method for function
arguments: arguments:
@param self: FunctionClass instance. @param cls: FunctionClass.
@param *meth_func_args: arguments to be passed to function. All FunctionClass @param *meth_func_args: arguments to be passed to function. All FunctionClass
objects will be turned to their respective instances. objects will be turned to their respective instances.
@param **meth_func_kargs: keyword arguments to be passed to function. All @param **meth_func_kargs: keyword arguments to be passed to function. All
FunctionClass objects will be turned to their respective instances.""" FunctionClass objects will be turned to their respective instances."""
return getattr(self._module, function_name)( return getattr(cls._module, function_name)(
self,
*process_list_convert_to_instance(meth_func_args), *process_list_convert_to_instance(meth_func_args),
**process_dict_convert_to_instance(meth_func_kargs) **process_dict_convert_to_instance(meth_func_kargs)
) )
@ -240,6 +239,26 @@ def method_function_returns_instance(method_function, cls):
return new_function return new_function
def classmethod_function_returns_instance(method_function, cls):
"""A function decorator that is used to decorate classmethod 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 )
# Will get a class in arguement list,
# use static so we don't add another here.
@staticmethod
def new_function(*args, **kargs):
kargs_cls = { INSTANCE_ARGUMENT : method_function(*args, **kargs) }
if kargs_cls['instance'] == None:
return None
else:
return cls( **kargs_cls )
return new_function
def method_function_returns_instance_list(method_function, cls): def method_function_returns_instance_list(method_function, cls):
def new_function(*args, **kargs): def new_function(*args, **kargs):
return [ cls( **{INSTANCE_ARGUMENT: item} ) return [ cls( **{INSTANCE_ARGUMENT: item} )

View File

@ -169,7 +169,8 @@ from gnucash.function_class import \
ClassFromFunctions, extract_attributes_with_prefix, \ ClassFromFunctions, extract_attributes_with_prefix, \
default_arguments_decorator, method_function_returns_instance, \ default_arguments_decorator, method_function_returns_instance, \
methods_return_instance, process_list_convert_to_instance, \ methods_return_instance, process_list_convert_to_instance, \
method_function_returns_instance_list, methods_return_instance_lists method_function_returns_instance_list, methods_return_instance_lists, \
classmethod_function_returns_instance
from gnucash.gnucash_core_c import gncInvoiceLookup, gncInvoiceGetInvoiceFromTxn, \ from gnucash.gnucash_core_c import gncInvoiceLookup, gncInvoiceGetInvoiceFromTxn, \
gncInvoiceGetInvoiceFromLot, gncEntryLookup, gncInvoiceLookup, \ gncInvoiceGetInvoiceFromLot, gncEntryLookup, gncInvoiceLookup, \
@ -1007,6 +1008,8 @@ GncCommodityNamespace.get_commodity_list = \
# GncLot # GncLot
GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new') GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new')
# replace method for gnc_lot_make_default() to be a classmethod
GncLot.add_classmethod('gnc_lot_make_default', 'make_default')
gnclot_dict = { gnclot_dict = {
'get_account' : Account, 'get_account' : Account,
@ -1014,10 +1017,13 @@ gnclot_dict = {
'get_earliest_split' : Split, 'get_earliest_split' : Split,
'get_latest_split' : Split, 'get_latest_split' : Split,
'get_balance' : GncNumeric, 'get_balance' : GncNumeric,
'lookup' : GncLot, 'lookup' : GncLot
'make_default' : GncLot
} }
methods_return_instance(GncLot, gnclot_dict) methods_return_instance(GncLot, gnclot_dict)
GncLot.make_default = classmethod_function_returns_instance(GncLot.make_default, GncLot)
methods_return_instance_lists(
GncLot, { 'get_split_list': Split
})
# Transaction # Transaction
Transaction.add_methods_with_prefix('xaccTrans') Transaction.add_methods_with_prefix('xaccTrans')
@ -1057,6 +1063,7 @@ split_dict = {
'GetBook': Book, 'GetBook': Book,
'GetAccount': Account, 'GetAccount': Account,
'GetParent': Transaction, 'GetParent': Transaction,
'GetLot': GncLot,
'Lookup': Split, 'Lookup': Split,
'GetOtherSplit': Split, 'GetOtherSplit': Split,
'GetAmount': GncNumeric, 'GetAmount': GncNumeric,
@ -1069,7 +1076,8 @@ split_dict = {
'GetReconciledBalance': GncNumeric, 'GetReconciledBalance': GncNumeric,
'VoidFormerAmount': GncNumeric, 'VoidFormerAmount': GncNumeric,
'VoidFormerValue': GncNumeric, 'VoidFormerValue': GncNumeric,
'GetGUID': GUID 'GetGUID': GUID,
'AssignToLot': Split
} }
methods_return_instance(Split, split_dict) methods_return_instance(Split, split_dict)
@ -1114,6 +1122,7 @@ account_dict = {
methods_return_instance(Account, account_dict) methods_return_instance(Account, account_dict)
methods_return_instance_lists( methods_return_instance_lists(
Account, { 'GetSplitList': Split, Account, { 'GetSplitList': Split,
'GetLotList': GncLot,
'get_children': Account, 'get_children': Account,
'get_children_sorted': Account, 'get_children_sorted': Account,
'get_descendants': Account, 'get_descendants': Account,

View File

@ -16,6 +16,7 @@ from test_business import TestBusiness
from test_commodity import TestCommodity, TestCommodityNamespace from test_commodity import TestCommodity, TestCommodityNamespace
from test_numeric import TestGncNumeric from test_numeric import TestGncNumeric
from test_query import TestQuery from test_query import TestQuery
from test_lot import TestLot
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -41,6 +41,10 @@ def other_function(self, arg=None):
return self, arg return self, arg
def class_other_function(arg=None):
return arg
class TestClass(ClassFromFunctions): class TestClass(ClassFromFunctions):
_module = sys.modules[__name__] _module = sys.modules[__name__]
@ -80,6 +84,17 @@ class TestFunctionClass(TestCase):
obj, arg = self.t.other_method(arg=self.t) obj, arg = self.t.other_method(arg=self.t)
self.assertIsInstance(arg, Instance) self.assertIsInstance(arg, Instance)
def test_add_classmethod(self):
"""test if add_classmethod adds method and if in case of FunctionClass
Instance instances get returned instead of FunctionClass instances"""
TestClass.add_constructor_and_methods_with_prefix("prefix_", "new_function")
TestClass.add_classmethod("class_other_function", "other_method")
self.t = TestClass()
arg = TestClass.other_method(self.t)
self.assertIsInstance(arg, Instance)
arg = TestClass.other_method(arg=self.t)
self.assertIsInstance(arg, Instance)
def test_default_arguments_decorator(self): def test_default_arguments_decorator(self):
"""test default_arguments_decorator()""" """test default_arguments_decorator()"""
TestClass.backup_test_function_return_args = TestClass.test_function_return_args TestClass.backup_test_function_return_args = TestClass.test_function_return_args

View File

@ -0,0 +1,64 @@
from unittest import main
from gnucash import Book, Account, GncLot, Split, GncNumeric
from test_account import AccountSession
# from test_split import SplitSession
class LotSession(AccountSession):
def setUp(self):
AccountSession.setUp(self)
self.NUM = 10000
self.amount = GncNumeric(self.NUM, 100)
def setup_buysplit(self):
self.buysplit = Split(self.book)
self.buysplit.SetAccount(self.account)
self.buysplit.SetAmount(self.amount)
def setup_sellsplit(self):
self.sellsplit = Split(self.book)
self.sellsplit.SetAccount(self.account)
self.sellsplit.SetAmount(self.amount.neg())
class TestLot(LotSession):
def test_make_default(self):
self.lot = GncLot.make_default(self.account)
self.assertIsInstance(self.lot, GncLot)
def test_AssignToLot(self):
self.lot = GncLot.make_default(self.account)
self.setup_buysplit()
self.buysplit.AssignToLot(self.lot)
self.assertEqual(self.NUM, self.lot.get_balance().num())
self.assertTrue(not self.lot.is_closed())
self.setup_sellsplit()
self.sellsplit.AssignToLot(self.lot)
self.assertEqual(0, self.lot.get_balance().num())
self.assertTrue(self.lot.is_closed())
def test_Split_GetLot(self):
self.lot = GncLot.make_default(self.account)
self.setup_buysplit()
self.buysplit.AssignToLot(self.lot)
rtn_lot = self.buysplit.GetLot()
self.assertEqual(rtn_lot.get_title(), self.lot.get_title())
def test_get_split_list(self):
self.lot = GncLot.make_default(self.account)
self.setup_buysplit()
self.buysplit.AssignToLot(self.lot)
splits = self.lot.get_split_list()
self.assertEqual(self.NUM, splits[0].GetAmount().num())
self.assertEqual(self.account.name, splits[0].GetAccount().name)
def test_Account_GetLotList(self):
self.lot = GncLot.make_default(self.account)
self.setup_buysplit()
self.buysplit.AssignToLot(self.lot)
lots = self.account.GetLotList()
self.assertEqual(self.account.name, lots[0].get_account().name)
if __name__ == '__main__':
unittest.main()

View File

@ -331,5 +331,8 @@ typedef char gchar;
PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_void, 0)); PyList_Append(list, SWIG_NewPointerObj(data, SWIGTYPE_p_void, 0));
} }
$result = list; $result = list;
if ($1_descriptor == $descriptor(AccountList *) ||
$1_descriptor == $descriptor(LotList *))
g_list_free($1);
} }
#endif #endif