Use builtin SWIG conversions for glib types

Where possible in the Python SWIG code use the builtin SWIG conversion
code over custom code. This ensures appropriate overflow/type checking.
With this I have enabled GncNumeric from longs and tested for correct
overflow handling.

Note: This could be extended to GUILE but I am not familiar enought to
safely enable this.
This commit is contained in:
Guy Taylor
2017-06-03 17:51:54 +01:00
committed by John Ralls
parent c9c5876431
commit 744cdac5a4
6 changed files with 122 additions and 36 deletions

View File

@@ -294,7 +294,7 @@ class GncNumeric(GnuCashCoreClass):
return gnc_numeric_zero()
elif len(args) == 1:
arg = args[0]
if type(arg) == int:
if type(arg) in (int, long):
return gnc_numeric_create(arg ,1)
elif type(arg) == float:
return double_to_gnc_numeric(arg, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER)

View File

@@ -13,6 +13,7 @@ SET(test_python_bindings_DATA
test_book.py
test_business.py
test_commodity.py
test_numeric.py
test_split.py
test_transaction.py)

View File

@@ -48,6 +48,8 @@ EXTRA_DIST = \
test_account.py \
test_book.py \
test_split.py \
test_commodity.py \
test_numeric.py \
test_transaction.py \
test_business.py \
CMakeLists.txt

View File

@@ -0,0 +1,106 @@
from unittest import TestCase, main
from gnucash import GncNumeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED, \
GNC_HOW_RND_NEVER, GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL
class TestGncNumeric( TestCase ):
def test_defaut(self):
num = GncNumeric()
self.assertEqual(str(num), "0/1")
self.assertEqual(num.num(), 0)
self.assertEqual(num.denom(), 1)
def test_from_num_denom(self):
num = GncNumeric(1, 2)
self.assertEqual(str(num), "1/2")
self.assertEqual(num.num(), 1)
self.assertEqual(num.denom(), 2)
def test_from_int(self):
num = GncNumeric(3)
self.assertEqual(str(num), "3/1")
self.assertEqual(num.num(), 3)
self.assertEqual(num.denom(), 1)
def test_from_long(self):
num = GncNumeric(3L)
self.assertEqual(str(num), "3/1")
self.assertEqual(num.num(), 3)
self.assertEqual(num.denom(), 1)
#One might think this would be an overflow error, but SWIG type-checks
#it first and discovers that it's too big to be an int64_t.
with self.assertRaises(TypeError):
GncNumeric((2**64)+1)
def test_from_float(self):
num = GncNumeric(3.1, 20, GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER)
self.assertEqual(str(num), "62/20")
self.assertEqual(num.num(), 62)
self.assertEqual(num.denom(), 20)
num = GncNumeric(1/3.0, 10000000000, GNC_HOW_RND_FLOOR)
self.assertEqual(str(num), "3333333333/10000000000")
self.assertEqual(num.num(), 3333333333)
self.assertEqual(num.denom(), 10000000000)
num = GncNumeric(1/3.0, 10000000000, GNC_HOW_RND_CEIL)
self.assertEqual(str(num), "3333333334/10000000000")
self.assertEqual(num.num(), 3333333334)
self.assertEqual(num.denom(), 10000000000)
def test_from_float_auto(self):
num = GncNumeric(3.1)
self.assertEqual(str(num), "31/10")
self.assertEqual(num.num(), 31)
self.assertEqual(num.denom(), 10)
def test_from_instance(self):
orig = GncNumeric(3)
num = GncNumeric(instance=orig.instance)
self.assertEqual(str(num), "3/1")
self.assertEqual(num.num(), 3)
self.assertEqual(num.denom(), 1)
def test_from_str(self):
num = GncNumeric("3.1")
self.assertEqual(str(num), "31/10")
self.assertEqual(num.num(), 31)
self.assertEqual(num.denom(), 10)
num = GncNumeric("1/3")
self.assertEqual(str(num), "1/3")
self.assertEqual(num.num(), 1)
self.assertEqual(num.denom(), 3)
def test_to_str(self):
num = GncNumeric("1000/3")
self.assertEqual(str(num), "1000/3")
num = GncNumeric(1, 0)
self.assertEqual(str(num), "1/0")
def test_to_double(self):
for test_num in [0.0, 1.1, -1.1, 1/3.0]:
self.assertEqual(GncNumeric(test_num).to_double(), test_num)
def test_to_fraction(self):
fraction = GncNumeric("1000/3").to_fraction()
self.assertEqual(fraction.numerator, 1000)
self.assertEqual(fraction.denominator, 3)
def test_incorect_args(self):
with self.assertRaises(TypeError):
GncNumeric(1, 2, 3)
with self.assertRaises(TypeError):
GncNumeric("1", 2)
with self.assertRaises(TypeError):
GncNumeric(1.1, "round")
with self.assertRaises(TypeError):
GncNumeric(complex(1, 1))
if __name__ == '__main__':
main()