[PyGreSQL] [CVS] Change to pygresql: TEST_PyGreSQL_classic.py, pg.py, pgdb.py, pgmodule.c, test_pg.py
Christoph Zwerschke
cito at druid.net
Tue Sep 16 18:29:48 EDT 2008
Update of /usr/cvs/Public/pygresql/module
In directory druid.net:/tmp/cvs-serv1192/module
Modified Files:
TEST_PyGreSQL_classic.py pg.py pgdb.py pgmodule.c test_pg.py
Log Message:
If available, use decimal.Decimal for numeric types.
To see the diffs for this commit:
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/TEST_PyGreSQL_classic.py.diff?r1=1.10&r2=1.11
Index: TEST_PyGreSQL_classic.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/TEST_PyGreSQL_classic.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- TEST_PyGreSQL_classic.py 10 Aug 2007 15:55:04 -0000 1.10
+++ TEST_PyGreSQL_classic.py 16 Sep 2008 22:29:47 -0000 1.11
@@ -5,7 +5,7 @@
# We need a database to test against. If LOCAL_PyGreSQL.py exists we will
# get our information from that. Otherwise we use the defaults.
-dbname = 'unittest'
+dbname = 'pygresql_test'
dbhost = None
dbport = 5432
@@ -128,11 +128,11 @@
self.assertEqual(_quote(1, 'int'), "1")
self.assertEqual(_quote(1, 'text'), "'1'")
self.assertEqual(_quote(1, 'seq'), "1")
- self.assertEqual(_quote(1, 'decimal'), "1")
+ self.assertEqual(_quote(1, 'num'), "1")
self.assertEqual(_quote('1', 'int'), "1")
self.assertEqual(_quote('1', 'text'), "'1'")
self.assertEqual(_quote('1', 'seq'), "1")
- self.assertEqual(_quote('1', 'decimal'), "1")
+ self.assertEqual(_quote('1', 'num'), "1")
self.assertEqual(_quote(None, 'int'), "NULL")
self.assertEqual(_quote(1, 'money'), "'1.00'")
self.assertEqual(_quote('1', 'money'), "'1.00'")
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/pg.py.diff?r1=1.54&r2=1.55
Index: pg.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/pg.py,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -r1.54 -r1.55
--- pg.py 10 Aug 2007 15:55:04 -0000 1.54
+++ pg.py 16 Sep 2008 22:29:47 -0000 1.55
@@ -5,7 +5,7 @@
# Written by D'Arcy J.M. Cain
# Improved by Christoph Zwerschke
#
-# $Id: pg.py,v 1.54 2007/08/10 15:55:04 darcy Exp $
+# $Id: pg.py,v 1.55 2008/09/16 22:29:47 cito Exp $
#
"""PyGreSQL classic interface.
@@ -20,6 +20,11 @@
from _pg import *
from types import *
+try:
+ from decimal import Decimal
+ set_decimal(Decimal)
+except ImportError:
+ pass # Python < 2.4
# Auxiliary functions which are independent from a DB connection:
@@ -27,7 +32,7 @@
"""Return quotes if needed."""
if d is None:
return 'NULL'
- if t in ('int', 'seq', 'decimal'):
+ if t in ('int', 'seq', 'float', 'num'):
if d == '': return 'NULL'
return str(d)
if t == 'money':
@@ -322,7 +327,9 @@
elif typ.startswith('oid'):
t[att] = 'int'
elif typ.startswith('float'):
- t[att] = 'decimal'
+ t[att] = 'float'
+ elif typ.startswith('numeric'):
+ t[att] = 'num'
elif typ.startswith('abstime'):
t[att] = 'date'
elif typ.startswith('date'):
@@ -505,7 +512,7 @@
fnames = self.get_attnames(qcl)
for k, t in fnames.items():
if k == 'oid': continue
- if t in ['int', 'decimal', 'seq', 'money']:
+ if t in ['int', 'seq', 'float', 'num', 'money']:
a[k] = 0
elif t == 'bool':
a[k] = 'f'
@@ -546,6 +553,7 @@
self.db.query(q)
# if run as script, print some information
+
if __name__ == '__main__':
print 'PyGreSQL version', version
print
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/pgdb.py.diff?r1=1.36&r2=1.37
Index: pgdb.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/pgdb.py,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- pgdb.py 30 Dec 2006 07:06:50 -0000 1.36
+++ pgdb.py 16 Sep 2008 22:29:47 -0000 1.37
@@ -4,7 +4,7 @@
#
# Written by D'Arcy J.M. Cain
#
-# $Id: pgdb.py,v 1.36 2006/12/30 07:06:50 darcy Exp $
+# $Id: pgdb.py,v 1.37 2008/09/16 22:29:47 cito Exp $
#
"""pgdb - DB-API 2.0 compliant module for PygreSQL.
@@ -63,17 +63,20 @@
"""
+from _pg import *
import types
import time
-
-from _pg import *
-
try: # use mx.DateTime module if available
from mx.DateTime import DateTime, \
TimeDelta, DateTimeType
except ImportError: # otherwise use standard datetime module
from datetime import datetime as DateTime, \
timedelta as TimeDelta, datetime as DateTimeType
+try: # use Decimal if available
+ from decimal import Decimal
+ set_decimal(Decimal)
+except ImportError: # otherwise (Python < 2.4)
+ Decimal = float # use float instead of Decimal
### module constants
@@ -111,10 +114,11 @@
value = long(value)
elif typ == FLOAT:
value = float(value)
+ elif typ == NUMERIC:
+ value = Decimal(value)
elif typ == MONEY:
- value = value.replace("$", "")
- value = value.replace(",", "")
- value = float(value)
+ value = ''.join(filter(lambda v: v in '0123456789.-', value))
+ value = Decimal(value)
elif typ == DATETIME:
# format may differ ... we'll give string
pass
@@ -156,12 +160,12 @@
"""You can overwrite this with a custom row factory
e.g. a dict_factory
- class myCursor(pgdb.pgdbCursor):
- def cursor.row_factory(self, row):
- d = {}
- for idx, col in enumerate(self.description):
- d[col[0]] = row[idx]
- return d
+ class myCursor(pgdb.pgdbCursor):
+ def cursor.row_factory(self, row):
+ d = {}
+ for idx, col in enumerate(self.description):
+ d[col[0]] = row[idx]
+ return d
cursor = myCursor(cnx)
"""
@@ -294,6 +298,8 @@
x = 'NULL'
elif isinstance(x, (types.ListType, types.TupleType)):
x = '(%s)' % ','.join(map(lambda x: str(_quote(x)), x))
+ elif Decimal is not float and isinstance(x, Decimal):
+ pass
elif hasattr(x, '__pg_repr__'):
x = x.__pg_repr__()
else:
@@ -457,7 +463,8 @@
BOOL = pgdbType('bool')
INTEGER = pgdbType('int2', 'int4', 'serial')
LONG = pgdbType('int8')
-FLOAT = pgdbType('float4', 'float8', 'numeric')
+FLOAT = pgdbType('float4', 'float8')
+NUMERIC = pgdbType('numeric')
MONEY = pgdbType('money')
DATE = pgdbType('date')
TIME = pgdbType('time', 'timetz')
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/pgmodule.c.diff?r1=1.77&r2=1.78
Index: pgmodule.c
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/pgmodule.c,v
retrieving revision 1.77
retrieving revision 1.78
diff -u -r1.77 -r1.78
--- pgmodule.c 16 Sep 2008 15:15:20 -0000 1.77
+++ pgmodule.c 16 Sep 2008 22:29:48 -0000 1.78
@@ -1,5 +1,5 @@
/*
- * $Id: pgmodule.c,v 1.77 2008/09/16 15:15:20 cito Exp $
+ * $Id: pgmodule.c,v 1.78 2008/09/16 22:29:48 cito Exp $
* PyGres, version 2.2 A Python interface for PostgreSQL database. Written by
* D'Arcy J.M. Cain, (darcy at druid.net). Based heavily on code written by
* Pascal Andre, andre at chimay.via.ecp.fr. Copyright (c) 1995, Pascal Andre
@@ -121,6 +121,8 @@
DL_EXPORT(void) init_pg(void);
int *get_type_array(PGresult *result, int nfields);
+static PyObject *decimal = NULL; /* decimal type */
+
/* --------------------------------------------------------------------- */
/* OBJECTS DECLARATION */
@@ -405,17 +407,20 @@
case FLOAT4OID:
case FLOAT8OID:
- case NUMERICOID:
typ[j] = 3;
break;
- case CASHOID:
+ case NUMERICOID:
typ[j] = 4;
break;
- default:
+ case CASHOID:
typ[j] = 5;
break;
+
+ default:
+ typ[j] = 6;
+ break;
}
}
@@ -1917,7 +1922,7 @@
int k;
char *s = PQgetvalue(self->last_result, i, j);
char cashbuf[64];
- PyObject *float_str;
+ PyObject *tmp_obj;
if (PQgetisnull(self->last_result, i, j))
{
@@ -1936,38 +1941,37 @@
break;
case 3:
- float_str = PyString_FromString(s);
- val = PyFloat_FromString(float_str, NULL);
- Py_DECREF(float_str);
+ tmp_obj = PyString_FromString(s);
+ val = PyFloat_FromString(tmp_obj, NULL);
+ Py_DECREF(tmp_obj);
break;
- case 4:
+ case 5:
+ for (k = 0;
+ *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
+ s++)
{
- int mult = 1;
+ if (isdigit(*s) || *s == '.')
+ cashbuf[k++] = *s;
+ else if (*s == '(' || *s == '-')
+ cashbuf[k++] = '-';
+ }
+ cashbuf[k] = 0;
+ s = cashbuf;
- if (*s == '$') /* there's talk of getting
- * rid of it */
- s++;
-
- if (*s == '-' || *s == '(')
- {
- s++;
- mult = -1;
- }
-
- /* get rid of the '$' and commas */
- if (*s == '$') /* Just in case we exposed
- * one */
- s++;
-
- for (k = 0; *s; s++)
- if (*s != ',')
- cashbuf[k++] = *s;
-
- cashbuf[k] = 0;
- val = PyFloat_FromDouble(strtod(cashbuf, NULL) * mult);
- break;
+ case 4:
+ if (decimal)
+ {
+ tmp_obj = Py_BuildValue("(s)", s);
+ val = PyEval_CallObject(decimal, tmp_obj);
}
+ else
+ {
+ tmp_obj = PyString_FromString(s);
+ val = PyFloat_FromString(tmp_obj, NULL);
+ }
+ Py_DECREF(tmp_obj);
+ break;
default:
val = PyString_FromString(s);
@@ -2042,7 +2046,7 @@
int k;
char *s = PQgetvalue(self->last_result, i, j);
char cashbuf[64];
- PyObject *float_str;
+ PyObject *tmp_obj;
if (PQgetisnull(self->last_result, i, j))
{
@@ -2061,42 +2065,37 @@
break;
case 3:
- float_str = PyString_FromString(s);
- val = PyFloat_FromString(float_str, NULL);
- Py_DECREF(float_str);
+ tmp_obj = PyString_FromString(s);
+ val = PyFloat_FromString(tmp_obj, NULL);
+ Py_DECREF(tmp_obj);
break;
- case 4:
+ case 5:
+ for (k = 0;
+ *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
+ s++)
{
- int mult = 1;
+ if (isdigit(*s) || *s == '.')
+ cashbuf[k++] = *s;
+ else if (*s == '(' || *s == '-')
+ cashbuf[k++] = '-';
+ }
+ cashbuf[k] = 0;
+ s = cashbuf;
- if (*s == '$') /* there's talk of getting
- * rid of it */
- s++;
-
- if (*s == '-' || *s == '(')
- {
- s++;
- mult = -1;
- }
-
- /* get rid of the '$' and commas */
- if (*s == '$') /* Just in case we exposed
- * one */
- s++;
-
- for (k = 0;
- *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1;
- s++)
- {
- if (*s != ',')
- cashbuf[k++] = *s;
- }
-
- cashbuf[k] = 0;
- val = PyFloat_FromDouble(strtod(cashbuf, NULL) * mult);
- break;
+ case 4:
+ if (decimal)
+ {
+ tmp_obj = Py_BuildValue("(s)", s);
+ val = PyEval_CallObject(decimal, tmp_obj);
}
+ else
+ {
+ tmp_obj = PyString_FromString(s);
+ val = PyFloat_FromString(tmp_obj, NULL);
+ }
+ Py_DECREF(tmp_obj);
+ break;
default:
val = PyString_FromString(s);
@@ -3076,6 +3075,29 @@
return ret;
}
+/* set decimal */
+static char set_decimal__doc__[] =
+"set_decimal(class) -- set a decimal type to be used for numeric values.";
+
+static PyObject *
+set_decimal(PyObject *dummy, PyObject *args)
+{
+ PyObject *result = NULL;
+ PyObject *temp;
+
+ if (PyArg_ParseTuple(args, "O:set_decimal", &temp))
+ {
+ if (!PyCallable_Check(temp))
+ {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+ Py_XINCREF(temp); Py_XDECREF(decimal); decimal = temp;
+ Py_INCREF(Py_None); result = Py_None;
+ }
+ return result;
+}
+
#ifdef DEFAULT_VARS
/* gets default host */
@@ -3431,6 +3453,8 @@
escape_bytea__doc__},
{"unescape_bytea", (PyCFunction) unescape_bytea, METH_VARARGS,
unescape_bytea__doc__},
+ {"set_decimal", (PyCFunction) set_decimal, METH_VARARGS,
+ set_decimal__doc__},
#ifdef DEFAULT_VARS
{"get_defhost", pggetdefhost, METH_VARARGS, getdefhost__doc__},
http://www.druid.net/pygresql/viewcvs.cgi/cvs/pygresql/module/test_pg.py.diff?r1=1.11&r2=1.12
Index: test_pg.py
===================================================================
RCS file: /usr/cvs/Public/pygresql/module/test_pg.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- test_pg.py 16 Sep 2008 15:10:01 -0000 1.11
+++ test_pg.py 16 Sep 2008 22:29:48 -0000 1.12
@@ -4,7 +4,7 @@
#
# Written by Christoph Zwerschke
#
-# $Id: test_pg.py,v 1.11 2008/09/16 15:10:01 cito Exp $
+# $Id: test_pg.py,v 1.12 2008/09/16 22:29:48 cito Exp $
#
"""Test the classic PyGreSQL interface in the pg module.
@@ -38,6 +38,12 @@
except:
german = 0
+try:
+ from decimal import Decimal
+except ImportError:
+ Decimal = float
+
+
def smart_ddl(conn, cmd):
"""Execute DDL, but don't complain about minor things."""
try:
@@ -58,6 +64,7 @@
else:
raise
+
class TestAuxiliaryFunctions(unittest.TestCase):
"""Test the auxiliary functions external to the connection class."""
@@ -65,25 +72,27 @@
f = pg._quote
self.assertEqual(f(None, None), 'NULL')
self.assertEqual(f(None, 'int'), 'NULL')
- self.assertEqual(f(None, 'decimal'), 'NULL')
+ self.assertEqual(f(None, 'float'), 'NULL')
+ self.assertEqual(f(None, 'num'), 'NULL')
self.assertEqual(f(None, 'money'), 'NULL')
self.assertEqual(f(None, 'bool'), 'NULL')
self.assertEqual(f(None, 'date'), 'NULL')
self.assertEqual(f('', 'int'), 'NULL')
self.assertEqual(f('', 'seq'), 'NULL')
- self.assertEqual(f('', 'decimal'), 'NULL')
+ self.assertEqual(f('', 'float'), 'NULL')
+ self.assertEqual(f('', 'num'), 'NULL')
self.assertEqual(f('', 'money'), 'NULL')
self.assertEqual(f('', 'bool'), 'NULL')
self.assertEqual(f('', 'date'), 'NULL')
self.assertEqual(f('', 'text'), "''")
self.assertEqual(f(123456789, 'int'), '123456789')
self.assertEqual(f(123654789, 'seq'), '123654789')
- self.assertEqual(f(123456987, 'decimal'), '123456987')
- self.assertEqual(f(1.23654789, 'decimal'), '1.23654789')
- self.assertEqual(f(12365478.9, 'decimal'), '12365478.9')
- self.assertEqual(f('123456789', 'decimal'), '123456789')
- self.assertEqual(f('1.23456789', 'decimal'), '1.23456789')
- self.assertEqual(f('12345678.9', 'decimal'), '12345678.9')
+ self.assertEqual(f(123456987, 'num'), '123456987')
+ self.assertEqual(f(1.23654789, 'num'), '1.23654789')
+ self.assertEqual(f(12365478.9, 'num'), '12365478.9')
+ self.assertEqual(f('123456789', 'num'), '123456789')
+ self.assertEqual(f('1.23456789', 'num'), '1.23456789')
+ self.assertEqual(f('12345678.9', 'num'), '12345678.9')
self.assertEqual(f(123, 'money'), "'123.00'")
self.assertEqual(f('123', 'money'), "'123.00'")
self.assertEqual(f(123.45, 'money'), "'123.45'")
@@ -617,30 +626,31 @@
self.c.close()
def testInserttable1Row(self):
- data = [(1, 1, 1L, 1.0, 1.0, 1.0, "1", "1111", "1")]
+ d = Decimal is float and 1.0 or None
+ data = [(1, 1, 1L, d, 1.0, 1.0, d, "1", "1111", "1")]
self.c.inserttable("test", data)
r = self.c.query("select * from test").getresult()
self.assertEqual(r, data)
def testInserttable4Rows(self):
- data = [(-1, -1, -1L, -1.0, -1.0, -1.0, "-1", "-1-1", "-1"),
- (0, 0, 0L, 0.0, 0.0, 0.0, "0", "0000", "0"),
- (1, 1, 1L, 1.0, 1.0, 1.0, "1", "1111", "1"),
- (2, 2, 2L, 2.0, 2.0, 2.0, "2", "2222", "2")]
+ data = [(-1, -1, -1L, None, -1.0, -1.0, None, "-1", "-1-1", "-1"),
+ (0, 0, 0L, None, 0.0, 0.0, None, "0", "0000", "0"),
+ (1, 1, 1L, None, 1.0, 1.0, None, "1", "1111", "1"),
+ (2, 2, 2L, None, 2.0, 2.0, None, "2", "2222", "2")]
self.c.inserttable("test", data)
r = self.c.query("select * from test order by 1").getresult()
self.assertEqual(r, data)
def testInserttableMultipleRows(self):
num_rows = 100
- data = [(1, 1, 1L, 1.0, 1.0, 1.0, "1", "1111", "1")] * num_rows
+ data = [(1, 1, 1L, None, 1.0, 1.0, None, "1", "1111", "1")] * num_rows
self.c.inserttable("test", data)
r = self.c.query("select count(*) from test").getresult()[0][0]
self.assertEqual(r, num_rows)
def testInserttableMultipleCalls(self):
num_rows = 10
- data = [(1, 1, 1L, 1.0, 1.0, 1.0, "1", "1111", "1")]
+ data = [(1, 1, 1L, None, 1.0, 1.0, None, "1", "1111", "1")]
for i in range(num_rows):
self.c.inserttable("test", data)
r = self.c.query("select count(*) from test").getresult()[0][0]
@@ -648,14 +658,14 @@
def testInserttableNullValues(self):
num_rows = 100
- data = [(None,) * 9]
+ data = [(None,) * 10]
self.c.inserttable("test", data)
r = self.c.query("select * from test").getresult()
self.assertEqual(r, data)
def testInserttableMaxValues(self):
data = [(2**15 - 1, int(2**31 - 1), long(2**31 - 1),
- 1.0 + 1.0/32, 1.0 + 1.0/32, 1.0 + 1.0/32,
+ None, 1.0 + 1.0/32, 1.0 + 1.0/32, None,
"1234", "1234", "1234" * 10)]
self.c.inserttable("test", data)
r = self.c.query("select * from test").getresult()
@@ -765,6 +775,7 @@
self.assertRaises(pg.InternalError, self.db.query, 'select 1')
self.db = pg.DB(self.dbname)
+
class TestDBClass(unittest.TestCase):
""""Test the methods of the DB class wrapped pg connection."""
@@ -870,17 +881,17 @@
smart_ddl(self.db, 'drop table "%s"' % table)
smart_ddl(self.db, 'create table "%s" ('
'a smallint, b integer, c bigint, '
- 'e decimal, f float, f2 double precision, '
+ 'e numeric, f float, f2 double precision, m money, '
'x smallint, y smallint, z smallint, '
'Normal_NaMe smallint, "Special Name" smallint, '
't text, u char(2), v varchar(2), '
'primary key (y, u))' % table)
attributes = self.db.get_attnames(table)
result = {'a': 'int', 'c': 'int', 'b': 'int',
- 'e': 'text', 'f': 'decimal', 'f2': 'decimal',
- 'normal_name': 'int', 'Special Name': 'int',
- 'u': 'text', 't': 'text', 'v': 'text',
- 'y': 'int', 'x': 'int', 'z': 'int', 'oid': 'int' }
+ 'e': 'num', 'f': 'float', 'f2': 'float', 'm': 'money',
+ 'normal_name': 'int', 'Special Name': 'int',
+ 'u': 'text', 't': 'text', 'v': 'text',
+ 'y': 'int', 'x': 'int', 'z': 'int', 'oid': 'int' }
self.assertEqual(attributes, result)
def testGet(self):
@@ -934,13 +945,14 @@
smart_ddl(self.db, 'drop table "%s"' % table)
smart_ddl(self.db, 'create table "%s" ('
"i2 smallint, i4 integer, i8 bigint,"
- "d decimal, f4 real, f8 double precision,"
+ "d numeric, f4 real, f8 double precision, m money, "
"v4 varchar(4), c4 char(4), t text,"
"b boolean, ts timestamp)" % table)
data = dict(i2 = 2**15 - 1,
i4 = int(2**31 - 1), i8 = long(2**31 - 1),
- d = 1.0 + 1.0/32, f4 = 1.0 + 1.0/32, f8 = 1.0 + 1.0/32,
- v4 = "1234", c4 = "1234", t = "1234" * 10,
+ d = Decimal('123456789.9876543212345678987654321'),
+ f4 = 1.0 + 1.0/32, f8 = 1.0 + 1.0/32,
+ m = "1234.56", v4 = "1234", c4 = "1234", t = "1234" * 10,
b = 1, ts = 'current_date')
r = self.db.insert(table, data)
self.assertEqual(r, data)
@@ -1026,6 +1038,7 @@
r = self.db.unescape_bytea(r)
self.assertEqual(r, s)
+
class TestSchemas(unittest.TestCase):
""""Test correct handling of schemas (namespaces)."""
@@ -1125,7 +1138,7 @@
c = pg.connect(dbname)
smart_ddl(c, "create table test ("
"i2 smallint, i4 integer, i8 bigint,"
- "d decimal, f4 real, f8 double precision,"
+ "d numeric, f4 real, f8 double precision, m money, "
"v4 varchar(4), c4 char(4), t text)")
c.query("create view test_view as"
" select i4, v4 from test")
More information about the PyGreSQL
mailing list