Author: cito
Date: Sun Jul 17 11:10:42 2011
New Revision: 430
Log:
Support nan and inf as float values (#39, as suggested by Maxim Yegorushkin).
Modified:
trunk/module/TEST_PyGreSQL_dbapi20.py
trunk/module/pgdb.py
Modified: trunk/module/TEST_PyGreSQL_dbapi20.py
==============================================================================
--- trunk/module/TEST_PyGreSQL_dbapi20.py Thu Jul 7 14:49:17 2011
(r429)
+++ trunk/module/TEST_PyGreSQL_dbapi20.py Sun Jul 17 11:10:42 2011
(r430)
@@ -49,7 +49,7 @@
con = self._connect()
curs = myCursor(con)
ret = curs.execute("select 1 as a, 2 as b")
- self.assertTrue(ret is curs, 'execute() should return cursor')
+ self.assert_(ret is curs, 'execute() should return cursor')
self.assertEqual(curs.fetchone(), {'a': 1, 'b': 2})
def test_cursor_iteration(self):
@@ -61,9 +61,9 @@
def test_fetch_2_rows(self):
Decimal = pgdb.decimal_type()
values = ['test', pgdb.Binary('\xff\x52\xb2'),
- True, 5, 6,
- 5.7, Decimal('234.234234'), Decimal('75.45'),
- '2008-10-20 15:25:35', 7897234]
+ True, 5, 6, 5.7, Decimal('234.234234'), Decimal('75.45'),
+ '2011-07-17', '15:47:42', '2008-10-20 15:25:35', '15:31:05',
+ 7897234]
table = self.table_prefix + 'booze'
con = self._connect()
try:
@@ -77,14 +77,17 @@
"floattest float8,"
"numerictest numeric,"
"moneytest money,"
+ "datetest date,"
+ "timetest time,"
"datetimetest timestamp,"
+ "intervaltest interval,"
"rowidtest oid)" % table)
for s in ('numeric', 'monetary', 'time'):
cur.execute("set lc_%s to 'C'" % s)
for i in range(2):
cur.execute("insert into %s values ("
"%%s,%%s,%%s,%%s,%%s,%%s,%%s,"
- "'%%s'::money,%%s,%%s)" % table, values)
+ "'%%s'::money,%%s,%%s,%%s,%%s,%%s)" % table, values)
cur.execute("select * from %s" % table)
rows = cur.fetchall()
self.assertEqual(len(rows), 2)
@@ -93,26 +96,74 @@
finally:
con.close()
+ def test_float(self):
+ from math import pi, e
+ try:
+ nan = float('nan')
+ except ValueError: # Python < 2.6
+ nan = 3.0e999 - 1.5e999999
+ try:
+ inf = float('inf')
+ except ValueError: # Python < 2.6
+ inf = 3.0e999 * 1.5e999999
+ try:
+ from math import isnan, isinf
+ except ImportError: # Python < 2.6
+ isnan = lambda x: x != x
+ isinf = lambda x: not isnan(x) and isnan(x * 0)
+ try:
+ from math import isnan, isinf
+ except ImportError: # Python < 2.6
+ isnan = lambda x: x != x
+ isinf = lambda x: not isnan(x) and isnan(x * 0)
+ self.assert_(isnan(nan) and not isinf(nan))
+ self.assert_(isinf(inf) and not isnan(inf))
+ values = [0, 1, 0.03125, -42.53125, nan, inf, -inf]
+ table = self.table_prefix + 'float'
+ con = self._connect()
+ try:
+ cur = con.cursor()
+ cur.execute("create table %s (floattest float)" % table)
+ params = [(val,) for val in values]
+ cur.executemany("insert into %s values(%%s)" % table, params)
+ cur.execute("select * from %s" % table)
+ rows = cur.fetchall()
+ self.assertEqual(len(rows), len(values))
+ rows = [row[0] for row in rows]
+ for inval, outval in zip(values, rows):
+ if isinf(inval):
+ self.assert_(isinf(outval))
+ if inval < 0:
+ self.assert_(outval < 0)
+ else:
+ self.assert_(outval > 0)
+ elif isnan(inval):
+ self.assert_(isnan(outval))
+ else:
+ self.assertEqual(inval, outval)
+ finally:
+ con.close()
+
def test_set_decimal_type(self):
decimal_type = pgdb.decimal_type()
- self.assertTrue(decimal_type is not None and callable(decimal_type))
+ self.assert_(decimal_type is not None and callable(decimal_type))
con = self._connect()
try:
cur = con.cursor()
- self.assertTrue(pgdb.decimal_type(int) is int)
+ self.assert_(pgdb.decimal_type(int) is int)
cur.execute('select 42')
value = cur.fetchone()[0]
- self.assertTrue(isinstance(value, int))
+ self.assert_(isinstance(value, int))
self.assertEqual(value, 42)
- self.assertTrue(pgdb.decimal_type(float) is float)
+ self.assert_(pgdb.decimal_type(float) is float)
cur.execute('select 4.25')
value = cur.fetchone()[0]
- self.assertTrue(isinstance(value, float))
+ self.assert_(isinstance(value, float))
self.assertEqual(value, 4.25)
finally:
con.close()
pgdb.decimal_type(decimal_type)
- self.assertTrue(pgdb.decimal_type() is decimal_type)
+ self.assert_(pgdb.decimal_type() is decimal_type)
def test_nextset(self):
pass # not implemented
Modified: trunk/module/pgdb.py
==============================================================================
--- trunk/module/pgdb.py Thu Jul 7 14:49:17 2011 (r429)
+++ trunk/module/pgdb.py Sun Jul 17 11:10:42 2011 (r430)
@@ -75,6 +75,17 @@
set_decimal(Decimal)
except ImportError: # otherwise (Python < 2.4)
Decimal = float # use float instead of Decimal
+try:
+ from math import isnan, isinf
+except ImportError: # Python < 2.6
+ isnan = lambda x: x != x
+ isinf = lambda x: not isnan(x) and isnan(x * 0)
+try:
+ inf = float('inf')
+ nan = float('nan')
+except ValueError: # Python < 2.6
+ inf = 1.0e999
+ nan = inf * 0
### Module Constants
@@ -113,10 +124,23 @@
return unescape_bytea(value)
+def _cast_float(value):
+ try:
+ return float(value)
+ except ValueError:
+ if value == 'NaN':
+ return nan
+ elif value == 'Infinity':
+ return inf
+ elif value == '-Infinity':
+ return -inf
+ raise
+
+
_cast = {'bool': _cast_bool, 'bytea': _cast_bytea,
'int2': int, 'int4': int, 'serial': int,
'int8': long, 'oid': long, 'oid8': long,
- 'float4': float, 'float8': float,
+ 'float4': _cast_float, 'float8': _cast_float,
'numeric': Decimal, 'money': _cast_money}
@@ -201,8 +225,13 @@
val = "E'%s'" % self._cnx.escape_bytea(val)
else:
val = "'%s'" % self._cnx.escape_string(val)
- elif isinstance(val, (int, long, float)):
+ elif isinstance(val, (int, long)):
pass
+ elif isinstance(val, float):
+ if isinf(val):
+ return val < 0 and "'-Infinity'" or "'Infinity'"
+ elif isnan(val):
+ return "'NaN'"
elif val is None:
val = 'NULL'
elif isinstance(val, (list, tuple)):
_______________________________________________
PyGreSQL mailing list
[email protected]
http://mailman.vex.net/mailman/listinfo/pygresql