Author: cito
Date: Mon Nov 23 19:42:03 2015
New Revision: 616

Log:
Allow money values to be returned as string

If set_decimal_point() is called with None or '', then money values
will not be returned as Decimal, but as strings from the database,
including the currency and other formatting. Maybe this should one
day become the default setting.

Modified:
   trunk/module/TEST_PyGreSQL_classic_connection.py
   trunk/module/pgmodule.c

Modified: trunk/module/TEST_PyGreSQL_classic_connection.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_classic_connection.py    Mon Nov 23 16:15:38 
2015        (r615)
+++ trunk/module/TEST_PyGreSQL_classic_connection.py    Mon Nov 23 19:42:03 
2015        (r616)
@@ -1139,8 +1139,10 @@
 
     def testGetDecimalPoint(self):
         point = pg.get_decimal_point()
+        # error if a parameter is passed
+        self.assertRaises(TypeError, pg.get_decimal_point, point)
         self.assertIsInstance(point, str)
-        self.assertEqual(point, '.')
+        self.assertEqual(point, '.')  # the default setting
         pg.set_decimal_point(',')
         try:
             r = pg.get_decimal_point()
@@ -1152,12 +1154,28 @@
     def testSetDecimalPoint(self):
         d = pg.Decimal
         point = pg.get_decimal_point()
+        self.assertRaises(TypeError, pg.set_decimal_point)
+        # error if decimal point is not a string
+        self.assertRaises(TypeError, pg.set_decimal_point, 0)
+        # error if more than one decimal point passed
+        self.assertRaises(TypeError, pg.set_decimal_point, '.', ',')
+        self.assertRaises(TypeError, pg.set_decimal_point, '.,')
+        # error if decimal point is not a punctuation character
+        self.assertRaises(TypeError, pg.set_decimal_point, '0')
         query = self.c.query
-        # check that money values can be interpreted correctly
-        # if and only if the decimal point is set appropriately
-        # for the current lc_monetary setting
+        # check that money values are interpreted as decimal values
+        # only if decimal_point is set, and that the result is correct
+        # only if it is set suitable for the current lc_monetary setting
+        select_money = "select '34.25'::money"
+        proper_money = d(34.25)
+        bad_money = d(3425)
+        en_locales = 'en', 'en_US', 'en_US.utf8', 'en_US.UTF-8'
+        en_money = '$34.25', '$ 34.25', '34.25$', '34.25 $', '34.25 Dollar'
+        de_locales = 'de', 'de_DE', 'de_DE.utf8', 'de_DE.UTF-8'
+        de_money = ('34,25€', '34,25 €', '€34,25' '€ 34,25',
+            '34,25 EUR', '34,25 Euro', '34,25 DM')
         # first try with English localization (using the point)
-        for lc in 'en', 'en_US', 'en_US.utf8', 'en_US.UTF-8':
+        for lc in en_locales:
             try:
                 query("set lc_monetary='%s'" % lc)
             except pg.ProgrammingError:
@@ -1167,27 +1185,44 @@
         else:
             self.SkipTest("cannot set English money locale")
         try:
-            r = query("select '34.25'::money")
+            r = query(select_money)
         except pg.ProgrammingError:
             # this can happen if the currency signs cannot be
             # converted using the encoding of the test database
             self.skipTest('database does not support money')
+        pg.set_decimal_point(None)
+        try:
+            r = r.getresult()[0][0]
+        finally:
+            pg.set_decimal_point(point)
+        self.assertIsInstance(r, str)
+        self.assertIn(r, en_money)
+        r = query(select_money)
         pg.set_decimal_point('.')
         try:
             r = r.getresult()[0][0]
         finally:
             pg.set_decimal_point(point)
         self.assertIsInstance(r, d)
-        self.assertEqual(r, d('34.25'))
-        r = query("select '34.25'::money")
+        self.assertEqual(r, proper_money)
+        r = query(select_money)
         pg.set_decimal_point(',')
         try:
             r = r.getresult()[0][0]
         finally:
             pg.set_decimal_point(point)
-        self.assertNotEqual(r, d('34.25'))
+        self.assertIsInstance(r, d)
+        self.assertEqual(r, bad_money)
+        r = query(select_money)
+        pg.set_decimal_point('!')
+        try:
+            r = r.getresult()[0][0]
+        finally:
+            pg.set_decimal_point(point)
+        self.assertIsInstance(r, d)
+        self.assertEqual(r, bad_money)
         # then try with German localization (using the comma)
-        for lc in 'de', 'de_DE', 'de_DE.utf8', 'de_DE.UTF-8':
+        for lc in de_locales:
             try:
                 query("set lc_monetary='%s'" % lc)
             except pg.ProgrammingError:
@@ -1196,24 +1231,36 @@
                 break
         else:
             self.SkipTest("cannot set German money locale")
+        r = query(select_money)
+        pg.set_decimal_point(None)
         try:
-            r = query("select '34,25'::money")
-        except pg.ProgrammingError:
-            self.skipTest('database does not support money')
+            r = r.getresult()[0][0]
+        finally:
+            pg.set_decimal_point(point)
+        self.assertIsInstance(r, str)
+        self.assertIn(r, de_money)
+        r = query(select_money)
         pg.set_decimal_point(',')
         try:
             r = r.getresult()[0][0]
         finally:
             pg.set_decimal_point(point)
         self.assertIsInstance(r, d)
-        self.assertEqual(r, d('34.25'))
-        r = query("select '34,25'::money")
+        self.assertEqual(r, proper_money)
+        r = query(select_money)
         pg.set_decimal_point('.')
         try:
             r = r.getresult()[0][0]
         finally:
             pg.set_decimal_point(point)
-        self.assertNotEqual(r, d('34.25'))
+        self.assertEqual(r, bad_money)
+        r = query(select_money)
+        pg.set_decimal_point('!')
+        try:
+            r = r.getresult()[0][0]
+        finally:
+            pg.set_decimal_point(point)
+        self.assertEqual(r, bad_money)
 
     def testSetDecimal(self):
         d = pg.Decimal

Modified: trunk/module/pgmodule.c
==============================================================================
--- trunk/module/pgmodule.c     Mon Nov 23 16:15:38 2015        (r615)
+++ trunk/module/pgmodule.c     Mon Nov 23 19:42:03 2015        (r616)
@@ -108,7 +108,7 @@
 
 static PyObject *decimal = NULL, /* decimal type */
                                *namedresult = NULL; /* function for getting 
named results */
-static char *decimal_point = "."; /* decimal point used in money values */
+static char decimal_point = '.'; /* decimal point used in money values */
 
 static int pg_encoding_utf8 = 0;
 static int pg_encoding_latin1 = 0;
@@ -3283,13 +3283,15 @@
                                                break;
 
                                        case 5:  /* money */
+                                               /* convert to decimal only if 
decimal point is set */
+                                               if (!decimal_point) goto 
default_case;
                                                for (k = 0;
                                                         *s && k < 
sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
                                                         s++)
                                                {
                                                        if (isdigit((int)*s))
                                                                cashbuf[k++] = 
*s;
-                                                       else if (*s == 
*decimal_point)
+                                                       else if (*s == 
decimal_point)
                                                                cashbuf[k++] = 
'.';
                                                        else if (*s == '(' || 
*s == '-')
                                                                cashbuf[k++] = 
'-';
@@ -3317,6 +3319,7 @@
                                                break;
 
                                        default:
+                                       default_case:
 #if IS_PY3
                                                if (encoding == 
pg_encoding_utf8)
                                                        val = 
PyUnicode_DecodeUTF8(s, strlen(s), "strict");
@@ -3431,14 +3434,17 @@
                                                Py_DECREF(tmp_obj);
                                                break;
 
-                                       case 5:  /* money */
+                                       case 5:  /* pgmoney */
+                                               /* convert to decimal only if 
decimal point is set */
+                                               if (!decimal_point) goto 
default_case;
+
                                                for (k = 0;
                                                         *s && k < 
sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
                                                         s++)
                                                {
                                                        if (isdigit((int)*s))
                                                                cashbuf[k++] = 
*s;
-                                                       else if (*s == 
*decimal_point)
+                                                       else if (*s == 
decimal_point)
                                                                cashbuf[k++] = 
'.';
                                                        else if (*s == '(' || 
*s == '-')
                                                                cashbuf[k++] = 
'-';
@@ -3466,6 +3472,7 @@
                                                break;
 
                                        default:
+                                       default_case:
                                                val = PyStr_FromString(s);
                                                break;
                                }
@@ -3789,13 +3796,24 @@
 pgSetDecimalPoint(PyObject *self, PyObject * args)
 {
        PyObject *ret = NULL;
-       char *s;
+       char *s = NULL;
 
-       if (PyArg_ParseTuple(args, "s", &s))
-       {
-               decimal_point = s;
+       /* gets arguments */
+       if (PyArg_ParseTuple(args, "z", &s)) {
+               if (!s)
+                       s = "\0";
+               else if (*s && (*(s+1) || !ispunct(*s)))
+                       s = NULL;
+       }
+
+       if (s) {
+               decimal_point = *s;
                Py_INCREF(Py_None); ret = Py_None;
+       } else {
+               PyErr_SetString(PyExc_TypeError,
+                       "set_decimal_point() takes a punctuation character");
        }
+
        return ret;
 }
 
@@ -3807,18 +3825,23 @@
 pgGetDecimalPoint(PyObject *self, PyObject * args)
 {
        PyObject *ret = NULL;
+       char s[2];
 
        if (PyArg_ParseTuple(args, ""))
        {
-               ret = PyStr_FromString(decimal_point);
+               if (decimal_point) {
+                       s[0] = decimal_point; s[1] = '\0';
+                       ret = PyStr_FromString(s);
+               } else {
+                       Py_INCREF(Py_None); ret = Py_None;
+               }
        }
        else
        {
                PyErr_SetString(PyExc_TypeError,
-                       " get_decimal_point() takes no parameter");
+                       "get_decimal_point() takes no parameter");
        }
 
-
        return ret;
 }
 
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo.cgi/pygresql

Reply via email to