Changeset 342


Ignore:
Timestamp:
Nov 1, 2008, 9:04:07 AM (11 years ago)
Author:
cito
Message:

Modernized pgdb module code a bit (we now require Python 2.3). Improvements and tests for pgdbType.

Location:
trunk/module
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/module/TEST_PyGreSQL_dbapi20.py

    r336 r342  
    11#!/usr/bin/env python
    2 # $Id: TEST_PyGreSQL_dbapi20.py,v 1.8 2008-10-31 17:13:50 cito Exp $
     2# $Id: TEST_PyGreSQL_dbapi20.py,v 1.9 2008-11-01 13:04:07 cito Exp $
    33
    44import dbapi20
     
    8585
    8686    def test_nextset(self):
    87         pass
     87        pass # not implemented
    8888
    8989    def test_setoutputsize(self):
    90         pass
     90        pass # not implemented
     91
     92    def test_pgdb_type(self):
     93        self.assertEqual(pgdb.STRING, pgdb.STRING)
     94        self.assertNotEqual(pgdb.STRING, pgdb.INTEGER)
     95        self.assertNotEqual(pgdb.STRING, pgdb.BOOL)
     96        self.assertNotEqual(pgdb.BOOL, pgdb.INTEGER)
     97        self.assertEqual(pgdb.INTEGER, pgdb.INTEGER)
     98        self.assertNotEqual(pgdb.INTEGER, pgdb.NUMBER)
     99        self.assertEqual('char', pgdb.STRING)
     100        self.assertEqual('varchar', pgdb.STRING)
     101        self.assertEqual('text', pgdb.STRING)
     102        self.assertNotEqual('numeric', pgdb.STRING)
     103        self.assertEqual('numeric', pgdb.NUMERIC)
     104        self.assertEqual('numeric', pgdb.NUMBER)
     105        self.assertEqual('int4', pgdb.NUMBER)
     106        self.assertNotEqual('int4', pgdb.NUMERIC)
     107        self.assert_('char' in pgdb.STRING)
     108        self.assert_(pgdb.NUMERIC <= pgdb.NUMBER)
     109        self.assert_(pgdb.NUMBER >= pgdb.INTEGER)
     110        self.assert_(pgdb.TIME <= pgdb.DATETIME)
     111        self.assert_(pgdb.DATETIME >= pgdb.DATE)
     112
    91113
    92114if __name__ == '__main__':
  • trunk/module/pgdb.py

    r341 r342  
    55# Written by D'Arcy J.M. Cain
    66#
    7 # $Id: pgdb.py,v 1.41 2008-11-01 11:29:46 cito Exp $
     7# $Id: pgdb.py,v 1.42 2008-11-01 13:04:07 cito Exp $
    88#
    99
     
    6767import types
    6868import time
     69try:
     70        frozenset
     71except NameError: # Python < 2.4
     72        from sets import ImmutableSet as frozenset
    6973try: # use mx.DateTime module if available
    7074        from mx.DateTime import DateTime, \
     
    110114
    111115
    112 class pgdbTypeCache:
     116class pgdbTypeCache(dict):
    113117        """Cache for database types."""
    114118
     
    116120                """Initialize type cache for connection."""
    117121                self._src = cnx.source()
    118                 self._cache = {}
    119122
    120123        def typecast(typ, value):
     
    134137                """Get name of database type with given oid."""
    135138                try:
    136                         return self._cache[oid]
     139                        return self[oid]
    137140                except:
    138141                        self._src.execute(
     
    144147                        res = (res[0], None, int(res[1]),
    145148                                None, None, None)
    146                         self._cache[oid] = res
     149                        self[oid] = res
    147150                        return res
    148151
    149152
    150153class _quoteitem(dict):
     154        """Dictionary with auto quoting of its items."""
     155
    151156        def __getitem__(self, key):
    152157                return _quote(super(_quoteitem, self).__getitem__(key))
     
    154159
    155160def _quote(x):
     161        """Quote value depending on its type."""
    156162        if isinstance(x, DateTimeType):
    157163                x = str(x)
    158164        elif isinstance(x, unicode):
    159165                x = x.encode( 'utf-8' )
    160         if isinstance(x, types.StringType):
     166        if isinstance(x, str):
    161167                x = "'%s'" % str(x).replace("\\", "\\\\").replace("'", "''")
    162         elif isinstance(x, (types.IntType, types.LongType, types.FloatType)):
     168        elif isinstance(x, (int, long, float)):
    163169                pass
    164170        elif x is None:
    165171                x = 'NULL'
    166         elif isinstance(x, (types.ListType, types.TupleType)):
     172        elif isinstance(x, (list, tuple)):
    167173                x = '(%s)' % ','.join(map(lambda x: str(_quote(x)), x))
    168174        elif Decimal is not float and isinstance(x, Decimal):
     
    176182
    177183def _quoteparams(s, params):
     184        """Quote parameters.
     185
     186        This function works for both mappings and sequences.
     187
     188        """
    178189        if hasattr(params, 'has_key'):
    179190                params = _quoteitem(params)
     
    185196### Cursor Object
    186197
    187 class pgdbCursor:
     198class pgdbCursor(object):
    188199        """Cursor Object."""
    189200
     
    198209                self.lastrowid = None
    199210
    200         def row_factory(self, row):
     211        def row_factory(row):
    201212                """You can overwrite this with a custom row factory
    202213                        e.g. a dict_factory
     
    210221                        cursor = myCursor(cnx)
    211222                """
    212 
    213223                return row
     224        row_factory = staticmethod(row_factory)
    214225
    215226        def close(self):
     
    227238                # tuples to e.g. insert multiple rows in a single
    228239                # operation, but this kind of usage is deprecated:
    229                 if params and isinstance(params, types.ListType) \
    230                                 and isinstance(params[0], types.TupleType):
     240                if (params and isinstance(params, list)
     241                                and isinstance(params[0], tuple)):
    231242                        self.executemany(operation, params)
    232243                else:
     
    301312                        self.arraysize = size
    302313                try:
    303                         res = self._src.fetch(size)
     314                        result = self._src.fetch(size)
    304315                except Error, e:
    305316                        raise DatabaseError(str(e))
    306                 result = []
    307                 for r in res:
    308                         row = []
    309                         for desc, val in zip(self.description, r):
    310                                 row.append(self._type_cache.typecast(desc[1], val))
    311                         result.append(self.row_factory(row))
    312                 return result
    313 
    314         def nextset(self):
     317                row_factory = self.row_factory
     318                typecast = self._type_cache.typecast
     319                coltypes = [desc[1] for desc in self.description]
     320                return [row_factory([typecast(*args)
     321                        for args in zip(coltypes, row)]) for row in result]
     322
     323        def nextset():
    315324                raise NotSupportedError("nextset() is not supported")
    316 
    317         def setinputsizes(self, sizes):
     325        nextset = staticmethod(nextset)
     326
     327        def setinputsizes(sizes):
    318328                pass
    319 
    320         def setoutputsize(self, size, col=0):
     329        setinputsizes = staticmethod(setinputsizes)
     330
     331        def setoutputsize(size, col=0):
    321332                pass
     333        setoutputsize = staticmethod(setoutputsize)
    322334
    323335
    324336### Connection Objects
    325337
    326 class pgdbCnx:
     338class pgdbCnx(object):
    327339        """Connection Object."""
    328340
     
    430442### Types Handling
    431443
    432 class pgdbType:
     444class pgdbType(frozenset):
    433445        """Type class for a couple of PostgreSQL data types.
    434446
     
    438450        """
    439451
    440         def __init__(self, *values):
    441                 self.values = values
    442 
    443         def __cmp__(self, other):
    444                 if other in self.values:
    445                         return 0
    446                 if other < self.values:
    447                         return 1
    448                 else:
    449                         return -1
     452        def __new__(cls, values):
     453                # for Python >= 2.4
     454                if isinstance(values, basestring):
     455                        values = values.split()
     456                return super(pgdbType, cls).__new__(cls, values)
     457
     458        def __init__(self, values):
     459                # for Python < 2.4
     460                if isinstance(values, basestring):
     461                        values = values.split()
     462                super(pgdbType, self).__init__(values)
     463
     464        def __eq__(self, other):
     465                if isinstance(other, basestring):
     466                        return other in self
     467                else:
     468                        return super(pgdbType, self).__eq__(other)
     469
     470        def __ne__(self, other):
     471                if isinstance(other, basestring):
     472                        return other not in self
     473                else:
     474                        return super(pgdbType, self).__ne__(other)
    450475
    451476
    452477# Mandatory type objects defined by DB-API 2 specs:
    453478
    454 STRING = pgdbType('char', 'bpchar', 'name', 'text', 'varchar', 'bool')
    455 BINARY = pgdbType() # BLOB support is pg specific
    456 NUMBER = pgdbType('int2', 'int4', 'serial', 'int8',
    457         'float4', 'float8', 'numeric', 'money')
    458 # this may be problematic as type are quite different ... I hope it won't hurt
    459 DATETIME = pgdbType('date', 'time', 'timetz',
    460         'timestamp', 'timestamptz', 'datetime', 'abstime'
    461         'interval', 'tinterval', 'timespan', 'reltime')
    462 # OIDs are used for everything (types, tables, BLOBs, rows, ...). This may cause
    463 # confusion, but we are unable to find out what exactly is behind the OID (at
    464 # least not easily enough). Should this be undefined as BLOBs ?
    465 ROWID = pgdbType('oid', 'oid8')
     479STRING = pgdbType('char bpchar name text varchar')
     480BINARY = pgdbType('bytea')
     481NUMBER = pgdbType('int2 int4 serial int8 float4 float8 numeric money')
     482DATETIME = pgdbType('date time timetz timestamp timestamptz datetime abstime'
     483        ' interval tinterval timespan reltime')
     484ROWID = pgdbType('oid oid8')
    466485
    467486
     
    469488
    470489BOOL = pgdbType('bool')
    471 INTEGER = pgdbType('int2', 'int4', 'serial')
     490INTEGER = pgdbType('int2 int4 serial')
    472491LONG = pgdbType('int8')
    473 FLOAT = pgdbType('float4', 'float8')
     492FLOAT = pgdbType('float4 float8')
    474493NUMERIC = pgdbType('numeric')
    475494MONEY = pgdbType('money')
    476495DATE = pgdbType('date')
    477 TIME = pgdbType('time', 'timetz')
    478 TIMESTAMP = pgdbType('timestamp', 'timestamptz', 'datetime', 'abstime')
    479 INTERVAL = pgdbType('interval', 'tinterval', 'timespan', 'reltime')
     496TIME = pgdbType('time timetz')
     497TIMESTAMP = pgdbType('timestamp timestamptz datetime abstime')
     498INTERVAL = pgdbType('interval tinterval timespan reltime')
    480499
    481500
     
    492511
    493512def DateFromTicks(ticks):
    494         return apply(Date, time.localtime(ticks)[:3])
     513        return Date(*time.localtime(ticks)[:3])
    495514
    496515def TimeFromTicks(ticks):
    497         return apply(Time, time.localtime(ticks)[3:6])
     516        return Time(*time.localtime(ticks)[3:6])
    498517
    499518def TimestampFromTicks(ticks):
    500         return apply(Timestamp, time.localtime(ticks)[:6])
    501 
    502 def Binary(str):
    503         return str
     519        return Timestamp(*time.localtime(ticks)[:6])
     520
     521def Binary(value):
     522        return value
    504523
    505524
Note: See TracChangeset for help on using the changeset viewer.